* [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
@ 2026-05-07 9:27 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import Jens Wiklander
` (67 more replies)
0 siblings, 68 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Hi,
This is a follow-up to Jerome's patchset [1], addressing previous feedback
regarding the monolithic nature of the driver update.
The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
to u-boot"). Since then, not many kernel changes have been ported back into
U-Boot.
This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
a clear audit trail and maintain bisectability, I have structured the
series as follows:
1. Restore to Baseline: The first commit reverts U-Boot-specific changes
to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
2. Milestone Imports: A sequence of 50+ commits follows, each performing
a "snapshot" import of the drivers/usb/dwc3 directory for every major
kernel version (v3.19 through v6.16-rc7).
3. U-Boot Adaptation: The final commits (based on Jerome's original work)
re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
required for U-Boot integration.
The final diff is identical to [1]. I decided to stick with that for now to
focus on the method of how we import or update the code.
Note that this is compile-tested only. The CI pipeline on source.denx.de
was used as an OK/NOK indicator [2].
The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
this diff is identical to the previous, it should still work on that
platform. With the help of a custom build script [3] and with an additional
patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
plugged into one of its USB 3.0 ports. It certainly doesn't mean all
platforms using the DWC3 driver are still OK, but at least there is some
hope. If this breaks your platform I'd like to know, and if you can send a
fix it's even better.
I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
were a few conflicts, even when backing out the original U-Boot patches on
top of the original v3.19-rc1 import. However, the resulting state still
diverged significantly from [1].
Instead, I've imported each new kernel in a separate commit. That way it's
very clear which kernel patches are included. Since there aren't too many
patches for each kernel I'm listing the relevant commits in the U-Boot
commit message for easier reference. I did this with a script so it's easy
to make changes, if the approach is OK but we need to tune it. With this
approach it should be easy to tell if a Fixes patch for the kernel might
also be needed here.
Thanks,
Jens
[1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
[2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
[3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
[4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
RFC v1 -> RFC v2:
- Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
Jens Wiklander (60):
usb: dwc3: restore to original v3.19-rc1 kernel import
usb: dwc3: import from kernel v3.19
usb: dwc3: import from kernel v4.0
usb: dwc3: import from kernel v4.1
usb: dwc3: import from kernel v4.2
usb: dwc3: import from kernel v4.3
usb: dwc3: import from kernel v4.4
usb: dwc3: import from kernel v4.5
usb: dwc3: import from kernel v4.6
usb: dwc3: import from kernel v4.7
usb: dwc3: import from kernel v4.8
usb: dwc3: import from kernel v4.9
usb: dwc3: import from kernel v4.10
usb: dwc3: import from kernel v4.11
usb: dwc3: import from kernel v4.12
usb: dwc3: import from kernel v4.13
usb: dwc3: import from kernel v4.14
usb: dwc3: import from kernel v4.15
usb: dwc3: import from kernel v4.16
usb: dwc3: import from kernel v4.17
usb: dwc3: import from kernel v4.18
usb: dwc3: import from kernel v4.19
usb: dwc3: import from kernel v4.20
usb: dwc3: import from kernel v5.0
usb: dwc3: import from kernel v5.1
usb: dwc3: import from kernel v5.2
usb: dwc3: import from kernel v5.3
usb: dwc3: import from kernel v5.4
usb: dwc3: import from kernel v5.5
usb: dwc3: import from kernel v5.6
usb: dwc3: import from kernel v5.7
usb: dwc3: import from kernel v5.8
usb: dwc3: import from kernel v5.9
usb: dwc3: import from kernel v5.10
usb: dwc3: import from kernel v5.11
usb: dwc3: import from kernel v5.12
usb: dwc3: import from kernel v5.13
usb: dwc3: import from kernel v5.14
usb: dwc3: import from kernel v5.15
usb: dwc3: import from kernel v5.16
usb: dwc3: import from kernel v5.17
usb: dwc3: import from kernel v5.18
usb: dwc3: import from kernel v5.19
usb: dwc3: import from kernel v6.0
usb: dwc3: import from kernel v6.1
usb: dwc3: import from kernel v6.2
usb: dwc3: import from kernel v6.3
usb: dwc3: import from kernel v6.4
usb: dwc3: import from kernel v6.5
usb: dwc3: import from kernel v6.6
usb: dwc3: import from kernel v6.7
usb: dwc3: import from kernel v6.8
usb: dwc3: import from kernel v6.9
usb: dwc3: import from kernel v6.10
usb: dwc3: import from kernel v6.11
usb: dwc3: import from kernel v6.12
usb: dwc3: import from kernel v6.13
usb: dwc3: import from kernel v6.14
usb: dwc3: import from kernel v6.15
usb: dwc3: import from kernel v6.16-rc7
Jerome Forissier (4):
usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
usb: udc: re-import udc-core.c from kernel v6.16-rc7
usb: fix build after resync of DWC3 with kernel v6.16-rc7
drivers/usb/cdns3/ep0.c | 8 +-
drivers/usb/common/common.c | 23 +
drivers/usb/dwc3/Makefile | 57 +-
drivers/usb/dwc3/core.c | 1780 ++++++----
drivers/usb/dwc3/core.h | 1120 +++++--
drivers/usb/dwc3/debug.h | 432 +++
drivers/usb/dwc3/dwc3-omap.c | 2 -
drivers/usb/dwc3/ep0.c | 737 +++--
drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
drivers/usb/dwc3/gadget.h | 124 +-
drivers/usb/dwc3/glue.h | 36 +
drivers/usb/dwc3/io.h | 29 +-
drivers/usb/dwc3/linux-compat.h | 16 -
drivers/usb/dwc3/samsung_usb_phy.c | 77 -
drivers/usb/dwc3/ti_usb_phy.c | 2 -
drivers/usb/gadget/at91_udc.c | 46 -
drivers/usb/gadget/atmel_usba_udc.c | 102 +-
drivers/usb/gadget/ci_udc.c | 419 ---
drivers/usb/gadget/composite.c | 2 +-
drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
drivers/usb/gadget/epautoconf.c | 289 +-
drivers/usb/gadget/ether.c | 21 +-
drivers/usb/gadget/f_acm.c | 20 +-
drivers/usb/gadget/f_fastboot.c | 10 +-
drivers/usb/gadget/f_mass_storage.c | 5 +-
drivers/usb/gadget/f_rockusb.c | 11 +-
drivers/usb/gadget/f_sdp.c | 12 +-
drivers/usb/gadget/f_thor.c | 19 +-
drivers/usb/gadget/udc/Makefile | 1 +
drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
drivers/usb/host/xhci-dwc3.c | 4 +-
drivers/usb/host/xhci-ext-caps.h | 158 +
drivers/usb/host/xhci-exynos5.c | 2 +-
drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
drivers/usb/musb-new/musb_uboot.c | 39 -
include/dm/device_compat.h | 13 +
include/dm/read.h | 46 +
include/linux/compat.h | 15 +
include/linux/usb/ch9.h | 25 +-
include/linux/usb/gadget.h | 520 +--
include/linux/usb/otg.h | 10 +
include/linux/usb/phy.h | 56 +
43 files changed, 7659 insertions(+), 4091 deletions(-)
create mode 100644 drivers/usb/dwc3/debug.h
create mode 100644 drivers/usb/dwc3/glue.h
delete mode 100644 drivers/usb/dwc3/linux-compat.h
delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
create mode 100644 drivers/usb/host/xhci-ext-caps.h
base-commit: 0f865ab5d68484b4dc1724809de1be06edc85df1
--
2.43.0
^ permalink raw reply [flat|nested] 102+ messages in thread
* [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:40 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 02/64] usb: dwc3: import from kernel v3.19 Jens Wiklander
` (66 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Restore the dwc3 source files to the state of the original import in
commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel to
u-boot").
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 1002 +++++++++++++---------------
drivers/usb/dwc3/core.h | 171 ++---
drivers/usb/dwc3/debug.c | 32 +
drivers/usb/dwc3/debug.h | 228 +++++++
drivers/usb/dwc3/dwc3-am62.c | 125 ----
drivers/usb/dwc3/ep0.c | 217 +++---
drivers/usb/dwc3/gadget.c | 482 +++++++------
drivers/usb/dwc3/gadget.h | 16 +-
drivers/usb/dwc3/io.h | 54 +-
drivers/usb/dwc3/linux-compat.h | 16 -
drivers/usb/dwc3/platform_data.h | 47 ++
drivers/usb/dwc3/samsung_usb_phy.c | 77 ---
12 files changed, 1211 insertions(+), 1256 deletions(-)
create mode 100644 drivers/usb/dwc3/debug.c
create mode 100644 drivers/usb/dwc3/debug.h
delete mode 100644 drivers/usb/dwc3/dwc3-am62.c
delete mode 100644 drivers/usb/dwc3/linux-compat.h
create mode 100644 drivers/usb/dwc3/platform_data.h
delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 847fa1f82c37..25ddc39efad8 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1,48 +1,55 @@
-// SPDX-License-Identifier: GPL-2.0
/**
* core.c - DesignWare USB3 DRD Controller Core file
*
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/core.c) and ported
- * to uboot.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
- * commit cd72f890d2 : usb: dwc3: core: enable phy suspend quirk on non-FPGA
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <clk.h>
-#include <cpu_func.h>
-#include <malloc.h>
-#include <dwc3-uboot.h>
-#include <dm/device_compat.h>
-#include <dm/devres.h>
-#include <linux/bug.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/list.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/iopoll.h>
-#include <linux/ioport.h>
-#include <dm.h>
-#include <generic-phy.h>
+#include <linux/of.h>
+#include <linux/acpi.h>
+
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/bitfield.h>
-#include <linux/math64.h>
-#include <linux/time.h>
+#include <linux/usb/of.h>
+#include <linux/usb/otg.h>
+#include "platform_data.h"
#include "core.h"
#include "gadget.h"
#include "io.h"
-#include "linux-compat.h"
+#include "debug.h"
-static LIST_HEAD(dwc3_list);
/* -------------------------------------------------------------------------- */
-static void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
+void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -59,6 +66,7 @@ static void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
static int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
+ int ret;
/* Before Resetting PHY, put Core in Reset */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -75,6 +83,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0) {
+ phy_exit(dwc->usb2_generic_phy);
+ return ret;
+ }
mdelay(100);
/* Clear USB3 PHY reset */
@@ -97,94 +116,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
return 0;
}
-/*
- * dwc3_frame_length_adjustment - Adjusts frame length if required
- * @dwc3: Pointer to our controller context structure
- * @fladj: Value of GFLADJ_30MHZ to adjust frame length
- */
-static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
-{
- u32 reg;
-
- if (dwc->revision < DWC3_REVISION_250A)
- return;
-
- if (fladj == 0)
- return;
-
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
- reg &= ~DWC3_GFLADJ_30MHZ_MASK;
- reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj;
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
-}
-
-/**
- * dwc3_ref_clk_period - Reference clock period configuration
- * Default reference clock period depends on hardware
- * configuration. For systems with reference clock that differs
- * from the default, this will set clock period in DWC3_GUCTL
- * register.
- * @dwc: Pointer to our controller context structure
- * @ref_clk_per: reference clock period in ns
- */
-static void dwc3_ref_clk_period(struct dwc3 *dwc)
-{
- unsigned long period;
- unsigned long fladj;
- unsigned long decr;
- unsigned long rate;
- u32 reg;
-
- if (dwc->ref_clk) {
- rate = clk_get_rate(dwc->ref_clk);
- if (!rate)
- return;
- period = NSEC_PER_SEC / rate;
- } else {
- return;
- }
-
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
- reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
- reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
- dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
-
- if (dwc->revision <= DWC3_REVISION_250A)
- return;
-
- /*
- * The calculation below is
- *
- * 125000 * (NSEC_PER_SEC / (rate * period) - 1)
- *
- * but rearranged for fixed-point arithmetic. The division must be
- * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and
- * neither does rate * period).
- *
- * Note that rate * period ~= NSEC_PER_SECOND, minus the number of
- * nanoseconds of error caused by the truncation which happened during
- * the division when calculating rate or period (whichever one was
- * derived from the other). We first calculate the relative error, then
- * scale it to units of 8 ppm.
- */
- fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period);
- fladj -= 125000;
-
- /*
- * The documented 240MHz constant is scaled by 2 to get PLS1 as well.
- */
- decr = 480000000 / rate;
-
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
- reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
- & ~DWC3_GFLADJ_240MHZDECR
- & ~DWC3_GFLADJ_240MHZDECR_PLS1;
- reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
- | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
- | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
-}
-
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
@@ -193,7 +124,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
- dma_free_coherent(evt->buf);
+ dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma);
}
/**
@@ -209,20 +140,17 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
{
struct dwc3_event_buffer *evt;
- evt = devm_kzalloc((struct udevice *)dwc->dev, sizeof(*evt),
- GFP_KERNEL);
+ evt = devm_kzalloc(dwc->dev, sizeof(*evt), GFP_KERNEL);
if (!evt)
return ERR_PTR(-ENOMEM);
evt->dwc = dwc;
evt->length = length;
- evt->buf = dma_alloc_coherent(length,
- (unsigned long *)&evt->dma);
+ evt->buf = dma_alloc_coherent(dwc->dev, length,
+ &evt->dma, GFP_KERNEL);
if (!evt->buf)
return ERR_PTR(-ENOMEM);
- dwc3_flush_cache((uintptr_t)evt->buf, evt->length);
-
return evt;
}
@@ -258,8 +186,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
num = DWC3_NUM_INT(dwc->hwparams.hwparams1);
dwc->num_event_buffers = num;
- dwc->ev_buffs = memalign(CONFIG_SYS_CACHELINE_SIZE,
- sizeof(*dwc->ev_buffs) * num);
+ dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
+ GFP_KERNEL);
if (!dwc->ev_buffs)
return -ENOMEM;
@@ -354,9 +282,13 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
if (!dwc->nr_scratch)
return 0;
- scratch_addr = dma_map_single(dwc->scratchbuf,
- dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
- DMA_BIDIRECTIONAL);
+ /* should never fall here */
+ if (!WARN_ON(dwc->scratchbuf))
+ return 0;
+
+ scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+ dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
+ DMA_BIDIRECTIONAL);
if (dma_mapping_error(dwc->dev, scratch_addr)) {
dev_err(dwc->dev, "failed to map scratch buffer\n");
ret = -EFAULT;
@@ -382,8 +314,8 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
return 0;
err1:
- dma_unmap_single(scratch_addr, dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
- DMA_BIDIRECTIONAL);
+ dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
err0:
return ret;
@@ -397,8 +329,12 @@ static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
if (!dwc->nr_scratch)
return;
- dma_unmap_single(dwc->scratch_addr, dwc->nr_scratch *
- DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+ /* should never fall here */
+ if (!WARN_ON(dwc->scratchbuf))
+ return;
+
+ dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
kfree(dwc->scratchbuf);
}
@@ -428,34 +364,6 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
}
-static void dwc3_hsphy_mode_setup(struct dwc3 *dwc)
-{
- enum usb_phy_interface hsphy_mode = dwc->hsphy_mode;
- u32 reg;
-
- /* Set dwc3 usb2 phy config */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
-
- switch (hsphy_mode) {
- case USBPHY_INTERFACE_MODE_UTMI:
- reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
- DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
- reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
- DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
- break;
- case USBPHY_INTERFACE_MODE_UTMIW:
- reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
- DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
- reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
- DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
- break;
- default:
- break;
- }
-
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
-}
-
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -499,13 +407,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u3_susphy_quirk)
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- if (dwc->dis_del_phy_power_chg_quirk)
- reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
-
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
- dwc3_hsphy_mode_setup(dwc);
-
mdelay(100);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@@ -522,64 +425,11 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- if (dwc->dis_enblslpm_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
-
- if (dwc->dis_u2_freeclk_exists_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
-
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
mdelay(100);
}
-/* set global incr burst type configuration registers */
-static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
-{
- struct udevice *dev = dwc->dev;
- u32 cfg;
-
- if (!dwc->incrx_size)
- return;
-
- cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
-
- /* Enable Undefined Length INCR Burst and Enable INCRx Burst */
- cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK;
- if (dwc->incrx_mode)
- cfg |= DWC3_GSBUSCFG0_INCRBRSTENA;
- switch (dwc->incrx_size) {
- case 256:
- cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA;
- break;
- case 128:
- cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA;
- break;
- case 64:
- cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA;
- break;
- case 32:
- cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA;
- break;
- case 16:
- cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA;
- break;
- case 8:
- cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA;
- break;
- case 4:
- cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA;
- break;
- case 1:
- break;
- default:
- dev_err(dev, "Invalid property\n");
- break;
- }
-
- dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
-}
-
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -588,20 +438,26 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
+ unsigned long timeout;
u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
int ret;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
/* This should read as U3 followed by revision number */
- if ((reg & DWC3_GSNPSID_MASK) != 0x55330000 &&
- (reg & DWC3_GSNPSID_MASK) != 0x33310000) {
+ if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
goto err0;
}
dwc->revision = reg;
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+
/* Handle USB2.0-only core configuration */
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
@@ -610,17 +466,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
/* issue device SoftReset too */
+ timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
- ret = read_poll_timeout(dwc3_readl, reg,
- !(reg & DWC3_DCTL_CSFTRST),
- 1, 5000, dwc->regs, DWC3_DCTL);
- if (ret) {
- dev_err(dwc->dev, "Reset Timed Out\n");
- ret = -ETIMEDOUT;
- goto err0;
- }
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ break;
- dwc3_phy_setup(dwc);
+ if (time_after(jiffies, timeout)) {
+ dev_err(dwc->dev, "Reset Timed Out\n");
+ ret = -ETIMEDOUT;
+ goto err0;
+ }
+
+ cpu_relax();
+ } while (true);
ret = dwc3_core_soft_reset(dwc);
if (ret)
@@ -671,9 +531,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->is_fpga = true;
}
- if(dwc->disable_scramble_quirk && !dwc->is_fpga)
- WARN(true,
- "disable_scramble cannot be used on non-FPGA builds\n");
+ WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga,
+ "disable_scramble cannot be used on non-FPGA builds\n");
if (dwc->disable_scramble_quirk && dwc->is_fpga)
reg |= DWC3_GCTL_DISSCRAMBLE;
@@ -696,27 +555,27 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_phy_setup(dwc);
+
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
- goto err0;
+ goto err1;
ret = dwc3_setup_scratch_buffers(dwc);
if (ret)
- goto err1;
-
- /* Adjust Frame Length */
- dwc3_frame_length_adjustment(dwc, dwc->fladj);
-
- /* Adjust Reference Clock Period */
- dwc3_ref_clk_period(dwc);
-
- dwc3_set_incr_burst_type(dwc);
+ goto err2;
return 0;
-err1:
+err2:
dwc3_free_scratch_buffers(dwc);
+err1:
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
err0:
return ret;
}
@@ -724,10 +583,82 @@ err0:
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_free_scratch_buffers(dwc);
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+}
+
+static int dwc3_core_get_phy(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+ struct device_node *node = dev->of_node;
+ int ret;
+
+ if (node) {
+ dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
+ dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
+ } else {
+ dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
+ }
+
+ if (IS_ERR(dwc->usb2_phy)) {
+ ret = PTR_ERR(dwc->usb2_phy);
+ if (ret == -ENXIO || ret == -ENODEV) {
+ dwc->usb2_phy = NULL;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else {
+ dev_err(dev, "no usb2 phy configured\n");
+ return ret;
+ }
+ }
+
+ if (IS_ERR(dwc->usb3_phy)) {
+ ret = PTR_ERR(dwc->usb3_phy);
+ if (ret == -ENXIO || ret == -ENODEV) {
+ dwc->usb3_phy = NULL;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else {
+ dev_err(dev, "no usb3 phy configured\n");
+ return ret;
+ }
+ }
+
+ dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
+ if (IS_ERR(dwc->usb2_generic_phy)) {
+ ret = PTR_ERR(dwc->usb2_generic_phy);
+ if (ret == -ENOSYS || ret == -ENODEV) {
+ dwc->usb2_generic_phy = NULL;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else {
+ dev_err(dev, "no usb2 phy configured\n");
+ return ret;
+ }
+ }
+
+ dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
+ if (IS_ERR(dwc->usb3_generic_phy)) {
+ ret = PTR_ERR(dwc->usb3_generic_phy);
+ if (ret == -ENOSYS || ret == -ENODEV) {
+ dwc->usb3_generic_phy = NULL;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else {
+ dev_err(dev, "no usb3 phy configured\n");
+ return ret;
+ }
+ }
+
+ return 0;
}
static int dwc3_core_init_mode(struct dwc3 *dwc)
{
+ struct device *dev = dwc->dev;
int ret;
switch (dwc->dr_mode) {
@@ -735,7 +666,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
- dev_err(dwc->dev, "failed to initialize gadget\n");
+ dev_err(dev, "failed to initialize gadget\n");
return ret;
}
break;
@@ -743,7 +674,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
- dev_err(dwc->dev, "failed to initialize host\n");
+ dev_err(dev, "failed to initialize host\n");
return ret;
}
break;
@@ -751,39 +682,24 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc);
if (ret) {
- dev_err(dwc->dev, "failed to initialize host\n");
+ dev_err(dev, "failed to initialize host\n");
return ret;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
- dev_err(dwc->dev, "failed to initialize gadget\n");
+ dev_err(dev, "failed to initialize gadget\n");
return ret;
}
break;
default:
- dev_err(dwc->dev,
- "Unsupported mode of operation %d\n", dwc->dr_mode);
+ dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
return -EINVAL;
}
return 0;
}
-static void dwc3_gadget_run(struct dwc3 *dwc)
-{
- dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_RUN_STOP);
- mdelay(100);
-}
-
-static void dwc3_core_stop(struct dwc3 *dwc)
-{
- u32 reg;
-
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg & ~(DWC3_DCTL_RUN_STOP));
-}
-
static void dwc3_core_exit_mode(struct dwc3 *dwc)
{
switch (dwc->dr_mode) {
@@ -801,50 +717,74 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
/* do nothing */
break;
}
-
- /*
- * switch back to peripheral mode
- * This enables the phy to enter idle and then, if enabled, suspend.
- */
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- dwc3_gadget_run(dwc);
}
#define DWC3_ALIGN_MASK (16 - 1)
-/**
- * dwc3_uboot_init - dwc3 core uboot initialization code
- * @dwc3_dev: struct dwc3_device containing initialization data
- *
- * Entry point for dwc3 driver (equivalent to dwc3_probe in linux
- * kernel driver). Pointer to dwc3_device should be passed containing
- * base address and other initialization data. Returns '0' on success and
- * a negative value on failure.
- *
- * Generally called from board_usb_init() implemented in board file.
- */
-int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
+static int dwc3_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct dwc3_platform_data *pdata = dev_get_platdata(dev);
+ struct device_node *node = dev->of_node;
+ struct resource *res;
struct dwc3 *dwc;
- struct device *dev = NULL;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
int ret;
+ void __iomem *regs;
void *mem;
- mem = devm_kzalloc((struct udevice *)dev,
- sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
+ mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
if (!mem)
return -ENOMEM;
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem;
+ dwc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "missing IRQ\n");
+ return -ENODEV;
+ }
+ dwc->xhci_resources[1].start = res->start;
+ dwc->xhci_resources[1].end = res->end;
+ dwc->xhci_resources[1].flags = res->flags;
+ dwc->xhci_resources[1].name = res->name;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc->xhci_resources[0].start = res->start;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
+ dwc->xhci_resources[0].flags = res->flags;
+ dwc->xhci_resources[0].name = res->name;
- dwc->regs = (void *)(uintptr_t)(dwc3_dev->base +
- DWC3_GLOBALS_REGS_START);
+ res->start += DWC3_GLOBALS_REGS_START;
+
+ /*
+ * Request memory region but exclude xHCI regs,
+ * since it will be requested by the xhci-plat driver.
+ */
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ dwc->regs = regs;
+ dwc->regs_size = resource_size(res);
+ /*
+ * restore res->start back to its original value so that,
+ * in case the probe is deferred, we don't end up getting error in
+ * request the memory region the next time probe is called.
+ */
+ res->start -= DWC3_GLOBALS_REGS_START;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -858,35 +798,73 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
*/
hird_threshold = 12;
- dwc->maximum_speed = dwc3_dev->maximum_speed;
- dwc->has_lpm_erratum = dwc3_dev->has_lpm_erratum;
- if (dwc3_dev->lpm_nyet_threshold)
- lpm_nyet_threshold = dwc3_dev->lpm_nyet_threshold;
- dwc->is_utmi_l1_suspend = dwc3_dev->is_utmi_l1_suspend;
- if (dwc3_dev->hird_threshold)
- hird_threshold = dwc3_dev->hird_threshold;
-
- dwc->needs_fifo_resize = dwc3_dev->tx_fifo_resize;
- dwc->dr_mode = dwc3_dev->dr_mode;
-
- dwc->disable_scramble_quirk = dwc3_dev->disable_scramble_quirk;
- dwc->u2exit_lfps_quirk = dwc3_dev->u2exit_lfps_quirk;
- dwc->u2ss_inp3_quirk = dwc3_dev->u2ss_inp3_quirk;
- dwc->req_p1p2p3_quirk = dwc3_dev->req_p1p2p3_quirk;
- dwc->del_p1p2p3_quirk = dwc3_dev->del_p1p2p3_quirk;
- dwc->del_phy_power_chg_quirk = dwc3_dev->del_phy_power_chg_quirk;
- dwc->lfps_filter_quirk = dwc3_dev->lfps_filter_quirk;
- dwc->rx_detect_poll_quirk = dwc3_dev->rx_detect_poll_quirk;
- dwc->dis_u3_susphy_quirk = dwc3_dev->dis_u3_susphy_quirk;
- dwc->dis_u2_susphy_quirk = dwc3_dev->dis_u2_susphy_quirk;
- dwc->dis_del_phy_power_chg_quirk = dwc3_dev->dis_del_phy_power_chg_quirk;
- dwc->dis_tx_ipgap_linecheck_quirk = dwc3_dev->dis_tx_ipgap_linecheck_quirk;
- dwc->dis_enblslpm_quirk = dwc3_dev->dis_enblslpm_quirk;
- dwc->dis_u2_freeclk_exists_quirk = dwc3_dev->dis_u2_freeclk_exists_quirk;
-
- dwc->tx_de_emphasis_quirk = dwc3_dev->tx_de_emphasis_quirk;
- if (dwc3_dev->tx_de_emphasis)
- tx_de_emphasis = dwc3_dev->tx_de_emphasis;
+ if (node) {
+ dwc->maximum_speed = of_usb_get_maximum_speed(node);
+ dwc->has_lpm_erratum = of_property_read_bool(node,
+ "snps,has-lpm-erratum");
+ of_property_read_u8(node, "snps,lpm-nyet-threshold",
+ &lpm_nyet_threshold);
+ dwc->is_utmi_l1_suspend = of_property_read_bool(node,
+ "snps,is-utmi-l1-suspend");
+ of_property_read_u8(node, "snps,hird-threshold",
+ &hird_threshold);
+
+ dwc->needs_fifo_resize = of_property_read_bool(node,
+ "tx-fifo-resize");
+ dwc->dr_mode = of_usb_get_dr_mode(node);
+
+ dwc->disable_scramble_quirk = of_property_read_bool(node,
+ "snps,disable_scramble_quirk");
+ dwc->u2exit_lfps_quirk = of_property_read_bool(node,
+ "snps,u2exit_lfps_quirk");
+ dwc->u2ss_inp3_quirk = of_property_read_bool(node,
+ "snps,u2ss_inp3_quirk");
+ dwc->req_p1p2p3_quirk = of_property_read_bool(node,
+ "snps,req_p1p2p3_quirk");
+ dwc->del_p1p2p3_quirk = of_property_read_bool(node,
+ "snps,del_p1p2p3_quirk");
+ dwc->del_phy_power_chg_quirk = of_property_read_bool(node,
+ "snps,del_phy_power_chg_quirk");
+ dwc->lfps_filter_quirk = of_property_read_bool(node,
+ "snps,lfps_filter_quirk");
+ dwc->rx_detect_poll_quirk = of_property_read_bool(node,
+ "snps,rx_detect_poll_quirk");
+ dwc->dis_u3_susphy_quirk = of_property_read_bool(node,
+ "snps,dis_u3_susphy_quirk");
+ dwc->dis_u2_susphy_quirk = of_property_read_bool(node,
+ "snps,dis_u2_susphy_quirk");
+
+ dwc->tx_de_emphasis_quirk = of_property_read_bool(node,
+ "snps,tx_de_emphasis_quirk");
+ of_property_read_u8(node, "snps,tx_de_emphasis",
+ &tx_de_emphasis);
+ } else if (pdata) {
+ dwc->maximum_speed = pdata->maximum_speed;
+ dwc->has_lpm_erratum = pdata->has_lpm_erratum;
+ if (pdata->lpm_nyet_threshold)
+ lpm_nyet_threshold = pdata->lpm_nyet_threshold;
+ dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
+ if (pdata->hird_threshold)
+ hird_threshold = pdata->hird_threshold;
+
+ dwc->needs_fifo_resize = pdata->tx_fifo_resize;
+ dwc->dr_mode = pdata->dr_mode;
+
+ dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
+ dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
+ dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
+ dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
+ dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
+ dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
+ dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
+ dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
+ dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
+ dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
+
+ dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
+ if (pdata->tx_de_emphasis)
+ tx_de_emphasis = pdata->tx_de_emphasis;
+ }
/* default to superspeed if no maximum_speed passed */
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
@@ -898,21 +876,35 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
- dwc->hsphy_mode = dwc3_dev->hsphy_mode;
+ ret = dwc3_core_get_phy(dwc);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&dwc->lock);
+ platform_set_drvdata(pdev, dwc);
- dwc->index = dwc3_dev->index;
+ if (!dev->dma_mask) {
+ dev->dma_mask = dev->parent->dma_mask;
+ dev->dma_parms = dev->parent->dma_parms;
+ dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+ }
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ pm_runtime_forbid(dev);
dwc3_cache_hwparams(dwc);
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err0;
}
- if (!IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
dwc->dr_mode = USB_DR_MODE_HOST;
- else if (!IS_ENABLED(CONFIG_USB_HOST))
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
@@ -920,28 +912,55 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
ret = dwc3_core_init(dwc);
if (ret) {
- dev_err(dwc->dev, "failed to initialize core\n");
+ dev_err(dev, "failed to initialize core\n");
goto err0;
}
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+ ret = phy_power_on(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err1;
+
+ ret = phy_power_on(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_usb2phy_power;
+
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err1;
+ goto err_usb3phy_power;
}
ret = dwc3_core_init_mode(dwc);
if (ret)
goto err2;
- list_add_tail(&dwc->list, &dwc3_list);
+ ret = dwc3_debugfs_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize debugfs\n");
+ goto err3;
+ }
+
+ pm_runtime_allow(dev);
return 0;
+err3:
+ dwc3_core_exit_mode(dwc);
+
err2:
dwc3_event_buffers_cleanup(dwc);
+err_usb3phy_power:
+ phy_power_off(dwc->usb3_generic_phy);
+
+err_usb2phy_power:
+ phy_power_off(dwc->usb2_generic_phy);
+
err1:
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
dwc3_core_exit(dwc);
err0:
@@ -950,276 +969,151 @@ err0:
return ret;
}
-/**
- * dwc3_uboot_exit - dwc3 core uboot cleanup code
- * @index: index of this controller
- *
- * Performs cleanup of memory allocated in dwc3_uboot_init and other misc
- * cleanups (equivalent to dwc3_remove in linux). index of _this_ controller
- * should be passed and should match with the index passed in
- * dwc3_device during init.
- *
- * Generally called from board file.
- */
-void dwc3_uboot_exit(int index)
-{
- struct dwc3 *dwc;
-
- list_for_each_entry(dwc, &dwc3_list, list) {
- if (dwc->index != index)
- continue;
-
- dwc3_core_exit_mode(dwc);
- dwc3_event_buffers_cleanup(dwc);
- dwc3_free_event_buffers(dwc);
- dwc3_core_exit(dwc);
- list_del(&dwc->list);
- kfree(dwc->mem);
- break;
- }
-}
-
-MODULE_ALIAS("platform:dwc3");
-MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
-
-#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
-__weak int dwc3_uboot_interrupt_status(struct udevice *dev)
+static int dwc3_remove(struct platform_device *pdev)
{
- return 1;
-}
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
-/**
- * dm_usb_gadget_handle_interrupts - handle dwc3 core interrupt
- * @dev: device of this controller
- *
- * Invokes dwc3 gadget interrupts.
- *
- * Generally called from board file.
- */
-int dm_usb_gadget_handle_interrupts(struct udevice *dev)
-{
- struct dwc3 *dwc = NULL;
+ dwc3_debugfs_exit(dwc);
+ dwc3_core_exit_mode(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_free_event_buffers(dwc);
- if (!dwc3_uboot_interrupt_status(dev))
- return 0;
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
- list_for_each_entry(dwc, &dwc3_list, list) {
- if (dwc->dev != dev)
- continue;
+ dwc3_core_exit(dwc);
- dwc3_gadget_uboot_handle_interrupt(dwc);
- break;
- }
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
-#endif
-#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB)
-int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys)
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_suspend(struct device *dev)
{
- int ret;
-
- ret = generic_phy_get_bulk(dev, phys);
- if (ret)
- return ret;
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
- ret = generic_phy_init_bulk(phys);
- if (ret)
- return ret;
+ spin_lock_irqsave(&dwc->lock, flags);
- ret = generic_phy_power_on_bulk(phys);
- if (ret)
- generic_phy_exit_bulk(phys);
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
+ dwc3_gadget_suspend(dwc);
+ /* FALLTHROUGH */
+ case USB_DR_MODE_HOST:
+ default:
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ }
- return ret;
-}
+ dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
+ spin_unlock_irqrestore(&dwc->lock, flags);
-int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys)
-{
- int ret;
+ usb_phy_shutdown(dwc->usb3_phy);
+ usb_phy_shutdown(dwc->usb2_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
- ret = generic_phy_power_off_bulk(phys);
- ret |= generic_phy_exit_bulk(phys);
- return ret;
+ return 0;
}
-#endif
-#if CONFIG_IS_ENABLED(DM_USB)
-void dwc3_of_parse(struct dwc3 *dwc)
+static int dwc3_resume(struct device *dev)
{
- const u8 *tmp;
- struct udevice *dev = dwc->dev;
- u8 lpm_nyet_threshold;
- u8 tx_de_emphasis;
- u8 hird_threshold;
- u32 val;
- int i;
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+ int ret;
- /* default to highest possible threshold */
- lpm_nyet_threshold = 0xff;
-
- /* default to -3.5dB de-emphasis */
- tx_de_emphasis = 1;
-
- /*
- * default to assert utmi_sleep_n and use maximum allowed HIRD
- * threshold value of 0b1100
- */
- hird_threshold = 12;
-
- dwc->hsphy_mode = usb_get_phy_mode(dev_ofnode(dev));
-
- dwc->has_lpm_erratum = dev_read_bool(dev,
- "snps,has-lpm-erratum");
- tmp = dev_read_u8_array_ptr(dev, "snps,lpm-nyet-threshold", 1);
- if (tmp)
- lpm_nyet_threshold = *tmp;
-
- dwc->is_utmi_l1_suspend = dev_read_bool(dev,
- "snps,is-utmi-l1-suspend");
- tmp = dev_read_u8_array_ptr(dev, "snps,hird-threshold", 1);
- if (tmp)
- hird_threshold = *tmp;
-
- dwc->disable_scramble_quirk = dev_read_bool(dev,
- "snps,disable_scramble_quirk");
- dwc->u2exit_lfps_quirk = dev_read_bool(dev,
- "snps,u2exit_lfps_quirk");
- dwc->u2ss_inp3_quirk = dev_read_bool(dev,
- "snps,u2ss_inp3_quirk");
- dwc->req_p1p2p3_quirk = dev_read_bool(dev,
- "snps,req_p1p2p3_quirk");
- dwc->del_p1p2p3_quirk = dev_read_bool(dev,
- "snps,del_p1p2p3_quirk");
- dwc->del_phy_power_chg_quirk = dev_read_bool(dev,
- "snps,del_phy_power_chg_quirk");
- dwc->lfps_filter_quirk = dev_read_bool(dev,
- "snps,lfps_filter_quirk");
- dwc->rx_detect_poll_quirk = dev_read_bool(dev,
- "snps,rx_detect_poll_quirk");
- dwc->dis_u3_susphy_quirk = dev_read_bool(dev,
- "snps,dis_u3_susphy_quirk");
- dwc->dis_u2_susphy_quirk = dev_read_bool(dev,
- "snps,dis_u2_susphy_quirk");
- dwc->dis_del_phy_power_chg_quirk = dev_read_bool(dev,
- "snps,dis-del-phy-power-chg-quirk");
- dwc->dis_tx_ipgap_linecheck_quirk = dev_read_bool(dev,
- "snps,dis-tx-ipgap-linecheck-quirk");
- dwc->dis_enblslpm_quirk = dev_read_bool(dev,
- "snps,dis_enblslpm_quirk");
- dwc->dis_u2_freeclk_exists_quirk = dev_read_bool(dev,
- "snps,dis-u2-freeclk-exists-quirk");
- dwc->tx_de_emphasis_quirk = dev_read_bool(dev,
- "snps,tx_de_emphasis_quirk");
- tmp = dev_read_u8_array_ptr(dev, "snps,tx_de_emphasis", 1);
- if (tmp)
- tx_de_emphasis = *tmp;
-
- dwc->lpm_nyet_threshold = lpm_nyet_threshold;
- dwc->tx_de_emphasis = tx_de_emphasis;
-
- dwc->hird_threshold = hird_threshold
- | (dwc->is_utmi_l1_suspend << 4);
-
- dev_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj);
-
- /*
- * Handle property "snps,incr-burst-type-adjustment".
- * Get the number of value from this property:
- * result <= 0, means this property is not supported.
- * result = 1, means INCRx burst mode supported.
- * result > 1, means undefined length burst mode supported.
- */
- dwc->incrx_mode = INCRX_BURST_MODE;
- dwc->incrx_size = 0;
- for (i = 0; i < 8; i++) {
- if (dev_read_u32_index(dev, "snps,incr-burst-type-adjustment",
- i, &val))
- break;
-
- dwc->incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE;
- dwc->incrx_size = max(dwc->incrx_size, val);
- }
-}
-
-int dwc3_init(struct dwc3 *dwc)
-{
- int ret;
- u32 reg;
+ usb_phy_init(dwc->usb3_phy);
+ usb_phy_init(dwc->usb2_phy);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
- dwc3_cache_hwparams(dwc);
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_usb2phy_init;
- ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
- if (ret) {
- dev_err(dwc->dev, "failed to allocate event buffers\n");
- return -ENOMEM;
- }
+ spin_lock_irqsave(&dwc->lock, flags);
- ret = dwc3_core_init(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to initialize core\n");
- goto core_fail;
- }
+ dwc3_event_buffers_setup(dwc);
+ dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
- ret = dwc3_event_buffers_setup(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to setup event buffers\n");
- goto event_fail;
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
+ dwc3_gadget_resume(dwc);
+ /* FALLTHROUGH */
+ case USB_DR_MODE_HOST:
+ default:
+ /* do nothing */
+ break;
}
- if (dwc->revision >= DWC3_REVISION_250A) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ spin_unlock_irqrestore(&dwc->lock, flags);
- /*
- * Enable hardware control of sending remote wakeup
- * in HS when the device is in the L1 state.
- */
- if (dwc->revision >= DWC3_REVISION_290A)
- reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
- if (dwc->dis_tx_ipgap_linecheck_quirk)
- reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+ return 0;
- dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
- }
+err_usb2phy_init:
+ phy_exit(dwc->usb2_generic_phy);
- if (dwc->dr_mode == USB_DR_MODE_HOST ||
- dwc->dr_mode == USB_DR_MODE_OTG) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+ return ret;
+}
- reg |= DWC3_GUCTL_HSTINAUTORETRY;
+static const struct dev_pm_ops dwc3_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+};
- dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
- }
+#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
+#else
+#define DWC3_PM_OPS NULL
+#endif
- ret = dwc3_core_init_mode(dwc);
- if (ret)
- goto mode_fail;
+#ifdef CONFIG_OF
+static const struct of_device_id of_dwc3_match[] = {
+ {
+ .compatible = "snps,dwc3"
+ },
+ {
+ .compatible = "synopsys,dwc3"
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_match);
+#endif
- return 0;
+#ifdef CONFIG_ACPI
-mode_fail:
- dwc3_event_buffers_cleanup(dwc);
+#define ACPI_ID_INTEL_BSW "808622B7"
-event_fail:
- dwc3_core_exit(dwc);
+static const struct acpi_device_id dwc3_acpi_match[] = {
+ { ACPI_ID_INTEL_BSW, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
+#endif
-core_fail:
- dwc3_free_event_buffers(dwc);
+static struct platform_driver dwc3_driver = {
+ .probe = dwc3_probe,
+ .remove = dwc3_remove,
+ .driver = {
+ .name = "dwc3",
+ .of_match_table = of_match_ptr(of_dwc3_match),
+ .acpi_match_table = ACPI_PTR(dwc3_acpi_match),
+ .pm = DWC3_PM_OPS,
+ },
+};
- return ret;
-}
+module_platform_driver(dwc3_driver);
-void dwc3_remove(struct dwc3 *dwc)
-{
- dwc3_core_exit_mode(dwc);
- dwc3_event_buffers_cleanup(dwc);
- dwc3_free_event_buffers(dwc);
- dwc3_core_stop(dwc);
- dwc3_core_exit(dwc);
- kfree(dwc->mem);
-}
-#endif
+MODULE_ALIAS("platform:dwc3");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b572ea340c8c..4bb9aa696ede 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1,28 +1,37 @@
-/* SPDX-License-Identifier: GPL-2.0 */
/**
* core.h - DesignWare USB3 DRD Core Header
*
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/core.h) and ported
- * to uboot.
- *
- * commit 460d098cb6 : usb: dwc3: make HIRD threshold configurable
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
#ifndef __DRIVERS_USB_DWC3_CORE_H
#define __DRIVERS_USB_DWC3_CORE_H
-#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
-#include <linux/usb/phy.h>
+
+#include <linux/phy/phy.h>
#define DWC3_MSG_MAX 500
@@ -75,7 +84,6 @@
#define DWC3_GCTL 0xc110
#define DWC3_GEVTEN 0xc114
#define DWC3_GSTS 0xc118
-#define DWC3_GUCTL1 0xc11c
#define DWC3_GSNPSID 0xc120
#define DWC3_GGPIO 0xc124
#define DWC3_GUID 0xc128
@@ -115,7 +123,6 @@
#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
#define DWC3_GHWPARAMS8 0xc600
-#define DWC3_GFLADJ 0xc630
/* Device Registers */
#define DWC3_DCFG 0xc700
@@ -139,17 +146,6 @@
/* Bit fields */
-/* Global SoC Bus Configuration INCRx Register 0 */
-#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
-#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
-#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
-#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */
-#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */
-#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */
-#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */
-#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
-#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
-
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN (1 << 16)
@@ -174,26 +170,9 @@
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
-/* Global User Control Register */
-#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
-
-/* Global User Control 1 Register */
-#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
-#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
-
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
-#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
-#define DWC3_GUSB2PHYCFG_PHYIF(n) ((n) << 3)
-#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
-#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) ((n) << 10)
-#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
-#define USBTRDTIM_UTMI_8_BIT 9
-#define USBTRDTIM_UTMI_16_BIT 5
-#define UTMI_PHYIF_16_BIT 1
-#define UTMI_PHYIF_8_BIT 0
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
@@ -245,17 +224,6 @@
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
-/* Global Frame Length Adjustment Register */
-#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
-#define DWC3_GFLADJ_30MHZ_MASK 0x3f
-#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
-#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
-#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
-
-/* Global User Control Register*/
-#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
-#define DWC3_GUCTL_REFCLKPER_SEL 22
-
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -405,8 +373,6 @@
#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
-#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
-
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
#define DWC3_DALEPENA_EP(n) (1 << n)
@@ -436,7 +402,7 @@ struct dwc3_event_buffer {
unsigned int count;
unsigned int flags;
-#define DWC3_EVENT_PENDING (1UL << 0)
+#define DWC3_EVENT_PENDING BIT(0)
dma_addr_t dma;
@@ -670,7 +636,6 @@ struct dwc3_scratchpad_array {
* @ep0_trb: dma address of ep0_trb
* @ep0_usb_req: dummy req used while handling STD USB requests
* @ep0_bounce_addr: dma address of ep0_bounce
- * @setup_buf_addr: dma address of setup_buf
* @scratch_addr: dma address of scratchbuf
* @lock: for synchronizing
* @dev: pointer to our struct device
@@ -678,19 +643,18 @@ struct dwc3_scratchpad_array {
* @event_buffer_list: a list of event buffers
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
- * @ref_clk: reference clock
* @regs: base address for our registers
* @regs_size: address space size
- * @ref_clk_per: reference clock period configuration
* @nr_scratch: number of scratch buffers
* @num_event_buffers: calculated number of event buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
- * @hsphy_mode: UTMI phy mode, one of following:
- * - USBPHY_INTERFACE_MODE_UTMI
- * - USBPHY_INTERFACE_MODE_UTMIW
+ * @usb2_phy: pointer to USB2 PHY
+ * @usb3_phy: pointer to USB3 PHY
+ * @usb2_generic_phy: pointer to USB2 PHY
+ * @usb3_generic_phy: pointer to USB3 PHY
* @dcfg: saved contents of DCFG register
* @gctl: saved contents of GCTL register
* @isoch_delay: wValue from Set Isochronous Delay request;
@@ -719,8 +683,8 @@ struct dwc3_scratchpad_array {
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
- * 0 - utmi_sleep_n
- * 1 - utmi_l1_suspend_n
+ * 0 - utmi_sleep_n
+ * 1 - utmi_l1_suspend_n
* @is_selfpowered: true when we are selfpowered
* @is_fpga: true when we are using the FPGA board
* @needs_fifo_resize: not all users might want fifo resizing, flag it
@@ -741,12 +705,10 @@ struct dwc3_scratchpad_array {
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
- * 0 - -6dB de-emphasis
- * 1 - -3.5dB de-emphasis
- * 2 - No de-emphasis
- * 3 - Reserved
- * @index: index of _this_ controller
- * @list: to maintain the list of dwc3 controllers
+ * 0 - -6dB de-emphasis
+ * 1 - -3.5dB de-emphasis
+ * 2 - No de-emphasis
+ * 3 - Reserved
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -758,17 +720,12 @@ struct dwc3 {
dma_addr_t ep0_trb_addr;
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
- dma_addr_t setup_buf_addr;
struct dwc3_request ep0_usb_req;
/* device lock */
spinlock_t lock;
-#if defined(__UBOOT__) && CONFIG_IS_ENABLED(DM_USB)
- struct udevice *dev;
-#else
struct device *dev;
-#endif
struct platform_device *xhci;
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
@@ -779,13 +736,16 @@ struct dwc3 {
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
- struct clk *ref_clk;
+ struct usb_phy *usb2_phy;
+ struct usb_phy *usb3_phy;
+
+ struct phy *usb2_generic_phy;
+ struct phy *usb3_generic_phy;
void __iomem *regs;
size_t regs_size;
enum usb_dr_mode dr_mode;
- enum usb_phy_interface hsphy_mode;
/* used for suspend/resume */
u32 dcfg;
@@ -816,7 +776,6 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
-#define DWC3_REVISION_290A 0x5533290a
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -843,10 +802,6 @@ struct dwc3 {
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
- u32 fladj;
- u32 ref_clk_per;
- u8 incrx_mode;
- u32 incrx_size;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
@@ -873,20 +828,11 @@ struct dwc3 {
unsigned rx_detect_poll_quirk:1;
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
- unsigned dis_del_phy_power_chg_quirk:1;
- unsigned dis_tx_ipgap_linecheck_quirk:1;
- unsigned dis_enblslpm_quirk:1;
- unsigned dis_u2_freeclk_exists_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
- int index;
- struct list_head list;
};
-#define INCRX_BURST_MODE 0
-#define INCRX_UNDEF_LENGTH_BURST_MODE 1
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
@@ -904,30 +850,6 @@ struct dwc3_event_type {
#define DWC3_DEPEVT_STREAMEVT 0x06
#define DWC3_DEPEVT_EPCMDCMPLT 0x07
-/**
- * dwc3_ep_event_string - returns event name
- * @event: then event code
- */
-static inline const char *dwc3_ep_event_string(u8 event)
-{
- switch (event) {
- case DWC3_DEPEVT_XFERCOMPLETE:
- return "Transfer Complete";
- case DWC3_DEPEVT_XFERINPROGRESS:
- return "Transfer In-Progress";
- case DWC3_DEPEVT_XFERNOTREADY:
- return "Transfer Not Ready";
- case DWC3_DEPEVT_RXTXFIFOEVT:
- return "FIFO";
- case DWC3_DEPEVT_STREAMEVT:
- return "Stream";
- case DWC3_DEPEVT_EPCMDCMPLT:
- return "Endpoint Command Complete";
- }
-
- return "UNKNOWN";
-}
-
/**
* struct dwc3_event_depvt - Device Endpoint Events
* @one_bit: indicates this is an endpoint event (not used)
@@ -1057,17 +979,20 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
-void dwc3_of_parse(struct dwc3 *dwc);
-int dwc3_init(struct dwc3 *dwc);
-void dwc3_remove(struct dwc3 *dwc);
+#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_host_init(struct dwc3 *dwc);
+void dwc3_host_exit(struct dwc3 *dwc);
+#else
static inline int dwc3_host_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_host_exit(struct dwc3 *dwc)
{ }
+#endif
-#ifdef CONFIG_USB_DWC3_GADGET
+#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_gadget_init(struct dwc3 *dwc);
void dwc3_gadget_exit(struct dwc3 *dwc);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
@@ -1097,4 +1022,20 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
#endif
+/* power management interface */
+#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
+int dwc3_gadget_suspend(struct dwc3 *dwc);
+int dwc3_gadget_resume(struct dwc3 *dwc);
+#else
+static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
+{
+ return 0;
+}
+
+static inline int dwc3_gadget_resume(struct dwc3 *dwc)
+{
+ return 0;
+}
+#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
+
#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c
new file mode 100644
index 000000000000..0be6885bc370
--- /dev/null
+++ b/drivers/usb/dwc3/debug.c
@@ -0,0 +1,32 @@
+/**
+ * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "debug.h"
+
+void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ trace(&vaf);
+
+ va_end(args);
+}
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
new file mode 100644
index 000000000000..07fbc2d94fd4
--- /dev/null
+++ b/drivers/usb/dwc3/debug.h
@@ -0,0 +1,228 @@
+/**
+ * debug.h - DesignWare USB3 DRD Controller Debug Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DWC3_DEBUG_H
+#define __DWC3_DEBUG_H
+
+#include "core.h"
+
+/**
+ * dwc3_gadget_ep_cmd_string - returns endpoint command string
+ * @cmd: command code
+ */
+static inline const char *
+dwc3_gadget_ep_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ case DWC3_DEPCMD_DEPSTARTCFG:
+ return "Start New Configuration";
+ case DWC3_DEPCMD_ENDTRANSFER:
+ return "End Transfer";
+ case DWC3_DEPCMD_UPDATETRANSFER:
+ return "Update Transfer";
+ case DWC3_DEPCMD_STARTTRANSFER:
+ return "Start Transfer";
+ case DWC3_DEPCMD_CLEARSTALL:
+ return "Clear Stall";
+ case DWC3_DEPCMD_SETSTALL:
+ return "Set Stall";
+ case DWC3_DEPCMD_GETEPSTATE:
+ return "Get Endpoint State";
+ case DWC3_DEPCMD_SETTRANSFRESOURCE:
+ return "Set Endpoint Transfer Resource";
+ case DWC3_DEPCMD_SETEPCONFIG:
+ return "Set Endpoint Configuration";
+ default:
+ return "UNKNOWN command";
+ }
+}
+
+/**
+ * dwc3_gadget_generic_cmd_string - returns generic command string
+ * @cmd: command code
+ */
+static inline const char *
+dwc3_gadget_generic_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ case DWC3_DGCMD_SET_LMP:
+ return "Set LMP";
+ case DWC3_DGCMD_SET_PERIODIC_PAR:
+ return "Set Periodic Parameters";
+ case DWC3_DGCMD_XMIT_FUNCTION:
+ return "Transmit Function Wake Device Notification";
+ case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
+ return "Set Scratchpad Buffer Array Address Lo";
+ case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
+ return "Set Scratchpad Buffer Array Address Hi";
+ case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
+ return "Selected FIFO Flush";
+ case DWC3_DGCMD_ALL_FIFO_FLUSH:
+ return "All FIFO Flush";
+ case DWC3_DGCMD_SET_ENDPOINT_NRDY:
+ return "Set Endpoint NRDY";
+ case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
+ return "Run SoC Bus Loopback Test";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
+ * dwc3_gadget_link_string - returns link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_link_string(enum dwc3_link_state link_state)
+{
+ switch (link_state) {
+ case DWC3_LINK_STATE_U0:
+ return "U0";
+ case DWC3_LINK_STATE_U1:
+ return "U1";
+ case DWC3_LINK_STATE_U2:
+ return "U2";
+ case DWC3_LINK_STATE_U3:
+ return "U3";
+ case DWC3_LINK_STATE_SS_DIS:
+ return "SS.Disabled";
+ case DWC3_LINK_STATE_RX_DET:
+ return "RX.Detect";
+ case DWC3_LINK_STATE_SS_INACT:
+ return "SS.Inactive";
+ case DWC3_LINK_STATE_POLL:
+ return "Polling";
+ case DWC3_LINK_STATE_RECOV:
+ return "Recovery";
+ case DWC3_LINK_STATE_HRESET:
+ return "Hot Reset";
+ case DWC3_LINK_STATE_CMPLY:
+ return "Compliance";
+ case DWC3_LINK_STATE_LPBK:
+ return "Loopback";
+ case DWC3_LINK_STATE_RESET:
+ return "Reset";
+ case DWC3_LINK_STATE_RESUME:
+ return "Resume";
+ default:
+ return "UNKNOWN link state\n";
+ }
+}
+
+/**
+ * dwc3_gadget_event_string - returns event name
+ * @event: the event code
+ */
+static inline const char *dwc3_gadget_event_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ return "Disconnect";
+ case DWC3_DEVICE_EVENT_RESET:
+ return "Reset";
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ return "Connection Done";
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ return "Link Status Change";
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ return "WakeUp";
+ case DWC3_DEVICE_EVENT_EOPF:
+ return "End-Of-Frame";
+ case DWC3_DEVICE_EVENT_SOF:
+ return "Start-Of-Frame";
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ return "Erratic Error";
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ return "Command Complete";
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ return "Overflow";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * dwc3_ep_event_string - returns event name
+ * @event: then event code
+ */
+static inline const char *dwc3_ep_event_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ return "Transfer Complete";
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ return "Transfer In-Progress";
+ case DWC3_DEPEVT_XFERNOTREADY:
+ return "Transfer Not Ready";
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ return "FIFO";
+ case DWC3_DEPEVT_STREAMEVT:
+ return "Stream";
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ return "Endpoint Command Complete";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * dwc3_gadget_event_type_string - return event name
+ * @event: the event code
+ */
+static inline const char *dwc3_gadget_event_type_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ return "Disconnect";
+ case DWC3_DEVICE_EVENT_RESET:
+ return "Reset";
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ return "Connect Done";
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ return "Link Status Change";
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ return "Wake-Up";
+ case DWC3_DEVICE_EVENT_HIBER_REQ:
+ return "Hibernation";
+ case DWC3_DEVICE_EVENT_EOPF:
+ return "End of Periodic Frame";
+ case DWC3_DEVICE_EVENT_SOF:
+ return "Start of Frame";
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ return "Erratic Error";
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ return "Command Complete";
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ return "Overflow";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
+
+#ifdef CONFIG_DEBUG_FS
+extern int dwc3_debugfs_init(struct dwc3 *);
+extern void dwc3_debugfs_exit(struct dwc3 *);
+#else
+static inline int dwc3_debugfs_init(struct dwc3 *d)
+{ return 0; }
+static inline void dwc3_debugfs_exit(struct dwc3 *d)
+{ }
+#endif
+#endif /* __DWC3_DEBUG_H */
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
deleted file mode 100644
index 99519602eb2c..000000000000
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ /dev/null
@@ -1,125 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * TI AM62 specific glue layer for DWC3
- */
-
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <regmap.h>
-#include <syscon.h>
-#include <asm/io.h>
-
-#include "dwc3-generic.h"
-
-#define USBSS_MODE_CONTROL 0x1c
-#define USBSS_PHY_CONFIG 0x8
-#define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1)
-#define USBSS_PHY_VBUS_SEL_SHIFT 1
-#define USBSS_MODE_VALID BIT(0)
-#define PHY_PLL_REFCLK_MASK GENMASK(3, 0)
-static const int dwc3_ti_am62_rate_table[] = { /* in KHZ */
- 9600,
- 10000,
- 12000,
- 19200,
- 20000,
- 24000,
- 25000,
- 26000,
- 38400,
- 40000,
- 58000,
- 50000,
- 52000,
-};
-
-static void dwc3_ti_am62_glue_configure(struct udevice *dev, int index,
- enum usb_dr_mode mode)
-{
- struct clk usb2_refclk;
- int rate_code, i, ret;
- unsigned long rate;
- u32 reg;
- void *usbss;
- bool vbus_divider;
- struct regmap *syscon;
- struct ofnode_phandle_args args;
-
- usbss = dev_remap_addr_index(dev, 0);
- if (IS_ERR(usbss)) {
- dev_err(dev, "can't map IOMEM resource\n");
- return;
- }
-
- ret = clk_get_by_name(dev, "ref", &usb2_refclk);
- if (ret) {
- dev_err(dev, "can't get usb2_refclk\n");
- return;
- }
-
- /* Calculate the rate code */
- rate = clk_get_rate(&usb2_refclk);
- rate /= 1000; /* To KHz */
- for (i = 0; i < ARRAY_SIZE(dwc3_ti_am62_rate_table); i++) {
- if (dwc3_ti_am62_rate_table[i] == rate)
- break;
- }
-
- if (i == ARRAY_SIZE(dwc3_ti_am62_rate_table)) {
- dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
- return;
- }
-
- rate_code = i;
-
- /* Read the syscon property */
- syscon = syscon_regmap_lookup_by_phandle(dev, "ti,syscon-phy-pll-refclk");
- if (IS_ERR(syscon)) {
- dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
- return;
- }
-
- ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "ti,syscon-phy-pll-refclk", NULL, 1,
- 0, &args);
- if (ret)
- return;
-
- /* Program PHY PLL refclk by reading syscon property */
- ret = regmap_update_bits(syscon, args.args[0], PHY_PLL_REFCLK_MASK, rate_code);
- if (ret) {
- dev_err(dev, "failed to set phy pll reference clock rate\n");
- return;
- }
-
- /* VBUS divider select */
- reg = readl(usbss + USBSS_PHY_CONFIG);
- vbus_divider = dev_read_bool(dev, "ti,vbus-divider");
- if (vbus_divider)
- reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
-
- writel(reg, usbss + USBSS_PHY_CONFIG);
-
- /* Set mode valid */
- reg = readl(usbss + USBSS_MODE_CONTROL);
- reg |= USBSS_MODE_VALID;
- writel(reg, usbss + USBSS_MODE_CONTROL);
-}
-
-struct dwc3_glue_ops ti_am62_ops = {
- .glue_configure = dwc3_ti_am62_glue_configure,
-};
-
-static const struct udevice_id dwc3_am62_match[] = {
- { .compatible = "ti,am62-usb", .data = (ulong)&ti_am62_ops },
- { /* sentinel */ }
-};
-
-U_BOOT_DRIVER(dwc3_am62_wrapper) = {
- .name = "dwc3-am62",
- .id = UCLASS_SIMPLE_BUS,
- .of_match = dwc3_am62_match,
- .bind = dwc3_glue_bind,
- .probe = dwc3_glue_probe,
- .remove = dwc3_glue_remove,
- .plat_auto = sizeof(struct dwc3_glue_data),
-};
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 680756532f0d..1bc77a3b4997 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1,34 +1,40 @@
-// SPDX-License-Identifier: GPL-2.0
/**
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported
- * to uboot.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
- * commit c00552ebaf : Merge 3.18-rc7 into usb-next
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
-#include <cpu_func.h>
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <linux/bug.h>
+
#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/list.h>
+#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include "core.h"
+#include "debug.h"
#include "gadget.h"
#include "io.h"
-#include "linux-compat.h"
-
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
@@ -50,7 +56,7 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
}
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type, unsigned chain)
+ u32 len, u32 type)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
@@ -60,14 +66,11 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
dep = dwc->eps[epnum];
if (dep->flags & DWC3_EP_BUSY) {
- dev_vdbg(dwc->dev, "%s still busy\n", dep->name);
+ dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
return 0;
}
- trb = &dwc->ep0_trb[dep->free_slot];
-
- if (chain)
- dep->free_slot++;
+ trb = dwc->ep0_trb;
trb->bpl = lower_32_bits(buf_dma);
trb->bph = upper_32_bits(buf_dma);
@@ -75,28 +78,21 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trb->ctrl = type;
trb->ctrl |= (DWC3_TRB_CTRL_HWO
+ | DWC3_TRB_CTRL_LST
+ | DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_ISP_IMI);
- if (chain)
- trb->ctrl |= DWC3_TRB_CTRL_CHN;
- else
- trb->ctrl |= (DWC3_TRB_CTRL_IOC
- | DWC3_TRB_CTRL_LST);
-
- dwc3_flush_cache((uintptr_t)buf_dma, len);
- dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
-
- if (chain)
- return 0;
-
memset(¶ms, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
+ trace_dwc3_prepare_trb(dep, trb);
+
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_STARTTRANSFER, ¶ms);
if (ret < 0) {
- dev_dbg(dwc->dev, "%s STARTTRANSFER failed", dep->name);
+ dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
+ dep->name);
return ret;
}
@@ -161,7 +157,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
else
- dev_dbg(dwc->dev, "too early for delayed status");
+ dwc3_trace(trace_dwc3_ep0,
+ "too early for delayed status");
return 0;
}
@@ -225,7 +222,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
if (!dep->endpoint.desc) {
- dev_dbg(dwc->dev, "trying to queue request %p to disabled %s",
+ dwc3_trace(trace_dwc3_ep0,
+ "trying to queue request %p to disabled %s",
request, dep->name);
ret = -ESHUTDOWN;
goto out;
@@ -237,9 +235,10 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
- dev_vdbg(dwc->dev, "queueing request %p to %s length %d state '%s\n'",
- request, dep->name, request->length,
- dwc3_ep0_state_string(dwc->ep0state));
+ dwc3_trace(trace_dwc3_ep0,
+ "queueing request %p to %s length %d state '%s'",
+ request, dep->name, request->length,
+ dwc3_ep0_state_string(dwc->ep0state));
ret = __dwc3_gadget_ep0_queue(dep, req);
@@ -286,6 +285,8 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
unsigned long flags;
int ret;
@@ -301,7 +302,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
int ret;
ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
- DWC3_TRBCTL_CONTROL_SETUP, 0);
+ DWC3_TRBCTL_CONTROL_SETUP);
WARN_ON(ret < 0);
}
@@ -380,7 +381,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
dep = dwc->eps[0];
dwc->ep0_usb_req.dep = dep;
dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
- dwc->ep0_usb_req.request.buf = (void *)(uintptr_t)dwc->setup_buf_addr;
+ dwc->ep0_usb_req.request.buf = dwc->setup_buf;
dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
@@ -504,12 +505,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
addr = le16_to_cpu(ctrl->wValue);
if (addr > 127) {
- dev_dbg(dwc->dev, "invalid device address %d", addr);
+ dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr);
return -EINVAL;
}
if (state == USB_STATE_CONFIGURED) {
- dev_dbg(dwc->dev, "trying to set address when configured");
+ dwc3_trace(trace_dwc3_ep0,
+ "trying to set address when configured");
return -EINVAL;
}
@@ -574,7 +576,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
dwc->resize_fifos = true;
- dev_dbg(dwc->dev, "resize FIFOs flag SET");
+ dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
}
break;
@@ -639,10 +641,12 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
struct dwc3_ep *dep;
enum usb_device_state state = dwc->gadget.state;
u16 wLength;
+ u16 wValue;
if (state == USB_STATE_DEFAULT)
return -EINVAL;
+ wValue = le16_to_cpu(ctrl->wValue);
wLength = le16_to_cpu(ctrl->wLength);
if (wLength != 6) {
@@ -662,7 +666,7 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dep = dwc->eps[0];
dwc->ep0_usb_req.dep = dep;
dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
- dwc->ep0_usb_req.request.buf = (void *)(uintptr_t)dwc->setup_buf_addr;
+ dwc->ep0_usb_req.request.buf = dwc->setup_buf;
dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
@@ -696,35 +700,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
- dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
- dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
- dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
- dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
- dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
case USB_REQ_SET_SEL:
- dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL");
ret = dwc3_ep0_set_sel(dwc, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
- dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
default:
- dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
+ dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
}
@@ -742,7 +746,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
if (!dwc->gadget_driver)
goto out;
- dwc3_invalidate_cache((uintptr_t)ctrl, sizeof(*ctrl));
+ trace_dwc3_ctrl_req(ctrl);
len = le16_to_cpu(ctrl->wLength);
if (!len) {
@@ -775,10 +779,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
- unsigned transfer_size = 0;
- unsigned maxp;
- void *buf;
- u32 transferred = 0;
+ u32 transferred;
u32 status;
u32 length;
u8 epnum;
@@ -790,50 +791,34 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
trb = dwc->ep0_trb;
+ trace_dwc3_complete_trb(ep0, trb);
+
r = next_request(&ep0->request_list);
if (!r)
return;
- dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
-
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
- dev_dbg(dwc->dev, "Setup Pending received");
- dwc3_gadget_giveback(ep0, r, -ECONNRESET);
+ dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
+
+ if (r)
+ dwc3_gadget_giveback(ep0, r, -ECONNRESET);
+
return;
}
ur = &r->request;
- buf = ur->buf;
length = trb->size & DWC3_TRB_SIZE_MASK;
- maxp = ep0->endpoint.maxpacket;
-
if (dwc->ep0_bounced) {
- /*
- * Handle the first TRB before handling the bounce buffer if
- * the request length is greater than the bounce buffer size.
- */
- if (ur->length > DWC3_EP0_BOUNCE_SIZE) {
- transfer_size = (ur->length / maxp) * maxp;
- transferred = transfer_size - length;
- buf = (u8 *)buf + transferred;
- ur->actual += transferred;
+ unsigned transfer_size = ur->length;
+ unsigned maxp = ep0->endpoint.maxpacket;
- trb++;
- dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
- length = trb->size & DWC3_TRB_SIZE_MASK;
-
- ep0->free_slot = 0;
- }
-
- transfer_size = roundup((ur->length - transfer_size),
- maxp);
- transferred = min_t(u32, ur->length - transferred,
- transfer_size - length);
- dwc3_flush_cache((uintptr_t)dwc->ep0_bounce, DWC3_EP0_BOUNCE_SIZE);
- memcpy(buf, dwc->ep0_bounce, transferred);
+ transfer_size += (maxp - (transfer_size % maxp));
+ transferred = min_t(u32, ur->length,
+ transfer_size - length);
+ memcpy(ur->buf, dwc->ep0_bounce, transferred);
} else {
transferred = ur->length - length;
}
@@ -855,7 +840,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dwc, epnum,
dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, 0);
+ DWC3_TRBCTL_CONTROL_DATA);
WARN_ON(ret < 0);
}
}
@@ -872,6 +857,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
dep = dwc->eps[0];
trb = dwc->ep0_trb;
+ trace_dwc3_complete_trb(dep, trb);
+
if (!list_empty(&dep->request_list)) {
r = next_request(&dep->request_list);
@@ -883,7 +870,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
if (ret < 0) {
- dev_dbg(dwc->dev, "Invalid Test #%d",
+ dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d",
dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -892,7 +879,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING)
- dev_dbg(dwc->dev, "Setup Pending received");
+ dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
@@ -909,17 +896,17 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
switch (dwc->ep0state) {
case EP0_SETUP_PHASE:
- dev_vdbg(dwc->dev, "Setup Phase\n");
+ dwc3_trace(trace_dwc3_ep0, "Setup Phase");
dwc3_ep0_inspect_setup(dwc, event);
break;
case EP0_DATA_PHASE:
- dev_vdbg(dwc->dev, "Data Phase\n");
+ dwc3_trace(trace_dwc3_ep0, "Data Phase");
dwc3_ep0_complete_data(dwc, event);
break;
case EP0_STATUS_PHASE:
- dev_vdbg(dwc->dev, "Status Phase\n");
+ dwc3_trace(trace_dwc3_ep0, "Status Phase");
dwc3_ep0_complete_status(dwc, event);
break;
default:
@@ -936,11 +923,11 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
if (req->request.length == 0) {
ret = dwc3_ep0_start_trans(dwc, dep->number,
- dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, 0);
- } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
- (dep->number == 0)) {
- u32 transfer_size = 0;
+ dwc->ctrl_req_addr, 0,
+ DWC3_TRBCTL_CONTROL_DATA);
+ } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
+ && (dep->number == 0)) {
+ u32 transfer_size;
u32 maxpacket;
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
@@ -950,18 +937,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
}
- maxpacket = dep->endpoint.maxpacket;
- if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
- transfer_size = (req->request.length / maxpacket) *
- maxpacket;
- ret = dwc3_ep0_start_trans(dwc, dep->number,
- req->request.dma,
- transfer_size,
- DWC3_TRBCTL_CONTROL_DATA, 1);
- }
+ WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
- transfer_size = roundup((req->request.length - transfer_size),
- maxpacket);
+ maxpacket = dep->endpoint.maxpacket;
+ transfer_size = roundup(req->request.length, maxpacket);
dwc->ep0_bounced = true;
@@ -971,8 +950,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
* TRBs to handle the transfer.
*/
ret = dwc3_ep0_start_trans(dwc, dep->number,
- dwc->ep0_bounce_addr, transfer_size,
- DWC3_TRBCTL_CONTROL_DATA, 0);
+ dwc->ep0_bounce_addr, transfer_size,
+ DWC3_TRBCTL_CONTROL_DATA);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
@@ -982,8 +961,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
}
ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
- req->request.length,
- DWC3_TRBCTL_CONTROL_DATA, 0);
+ req->request.length, DWC3_TRBCTL_CONTROL_DATA);
}
WARN_ON(ret < 0);
@@ -998,13 +976,13 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
: DWC3_TRBCTL_CONTROL_STATUS2;
return dwc3_ep0_start_trans(dwc, dep->number,
- dwc->ctrl_req_addr, 0, type, 0);
+ dwc->ctrl_req_addr, 0, type);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
if (dwc->resize_fifos) {
- dev_dbg(dwc->dev, "Resizing FIFOs");
+ dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
dwc3_gadget_resize_tx_fifos(dwc);
dwc->resize_fifos = 0;
}
@@ -1045,7 +1023,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
- dev_vdbg(dwc->dev, "Control Data\n");
+ dwc3_trace(trace_dwc3_ep0, "Control Data");
/*
* We already have a DATA transfer in the controller's cache,
@@ -1059,7 +1037,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_expect_in != event->endpoint_number) {
struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
- dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
+ dwc3_trace(trace_dwc3_ep0,
+ "Wrong direction for Data phase");
dwc3_ep0_end_control_data(dwc, dep);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -1071,13 +1050,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
- dev_vdbg(dwc->dev, "Control Status\n");
+ dwc3_trace(trace_dwc3_ep0, "Control Status");
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
WARN_ON_ONCE(event->endpoint_number != 1);
- dev_vdbg(dwc->dev, "Delayed Status\n");
+ dwc3_trace(trace_dwc3_ep0, "Delayed Status");
return;
}
@@ -1090,10 +1069,10 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
{
u8 epnum = event->endpoint_number;
- dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
- dwc3_ep_event_string(event->endpoint_event),
- epnum >> 1, (epnum & 1) ? "in" : "out",
- dwc3_ep0_state_string(dwc->ep0state));
+ dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
+ dwc3_ep_event_string(event->endpoint_event),
+ epnum >> 1, (epnum & 1) ? "in" : "out",
+ dwc3_ep0_state_string(dwc->ep0state));
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2b01113d54cd..f03b136ecfce 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1,39 +1,40 @@
-// SPDX-License-Identifier: GPL-2.0
/**
* gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
*
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported
- * to uboot.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
- * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
-#include <cpu_func.h>
-#include <log.h>
-#include <malloc.h>
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <dm/devres.h>
-#include <linux/bug.h>
+#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/list.h>
-#include <linux/printk.h>
+#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include "debug.h"
#include "core.h"
#include "gadget.h"
#include "io.h"
-#include "linux-compat.h"
-
/**
* dwc3_gadget_set_test_mode - Enables USB2 Test Modes
* @dwc: pointer to our context structure
@@ -167,6 +168,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
{
int last_fifo_depth = 0;
+ int ram1_depth;
int fifo_size;
int mdwidth;
int num;
@@ -174,6 +176,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
if (!dwc->needs_fifo_resize)
return 0;
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
/* MDWIDTH is represented in bits, we need it in bytes */
@@ -231,38 +234,40 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ int i;
if (req->queued) {
- dep->busy_slot++;
- /*
- * Skip LINK TRB. We can't use req->trb and check for
- * DWC3_TRBCTL_LINK_TRB because it points the TRB we
- * just completed (not the LINK TRB).
- */
- if (((dep->busy_slot & DWC3_TRB_MASK) ==
- DWC3_TRB_NUM- 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ i = 0;
+ do {
dep->busy_slot++;
+ /*
+ * Skip LINK TRB. We can't use req->trb and check for
+ * DWC3_TRBCTL_LINK_TRB because it points the TRB we
+ * just completed (not the LINK TRB).
+ */
+ if (((dep->busy_slot & DWC3_TRB_MASK) ==
+ DWC3_TRB_NUM- 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dep->busy_slot++;
+ } while(++i < req->request.num_mapped_sgs);
req->queued = false;
}
-
list_del(&req->list);
req->trb = NULL;
- if (req->request.dma && req->request.length)
- dwc3_flush_cache((uintptr_t)req->request.dma, req->request.length);
if (req->request.status == -EINPROGRESS)
req->request.status = status;
if (dwc->ep0_bounced && dep->number == 0)
dwc->ep0_bounced = false;
- else if (req->request.dma)
+ else
usb_gadget_unmap_request(&dwc->gadget, &req->request,
req->direction);
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
req->request.length, status);
+ trace_dwc3_gadget_giveback(req);
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
@@ -274,6 +279,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
u32 timeout = 500;
u32 reg;
+ trace_dwc3_gadget_generic_cmd(cmd, param);
+
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
@@ -299,38 +306,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
+ struct dwc3_ep *dep = dwc->eps[ep];
u32 timeout = 500;
- u32 saved_config = 0;
u32 reg;
- int ret = -EINVAL;
-
- /*
- * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
- * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
- * endpoint command.
- *
- * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
- * settings. Restore them after the command is completed.
- *
- * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
- */
- if (dwc->gadget.speed <= USB_SPEED_HIGH ||
- DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
- saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- }
-
- if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
- saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
- reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
- }
-
- if (saved_config)
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
- }
+ trace_dwc3_gadget_ep_cmd(dep, cmd, params);
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
@@ -342,8 +322,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
if (!(reg & DWC3_DEPCMD_CMDACT)) {
dev_vdbg(dwc->dev, "Command Complete --> %d\n",
DWC3_DEPCMD_STATUS(reg));
- ret = 0;
- break;
+ return 0;
}
/*
@@ -351,21 +330,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
* interrupt context.
*/
timeout--;
- if (!timeout) {
- ret = -ETIMEDOUT;
- break;
- }
+ if (!timeout)
+ return -ETIMEDOUT;
udelay(1);
} while (1);
-
- if (saved_config) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= saved_config;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
- }
-
- return ret;
}
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
@@ -378,15 +347,17 @@ static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
{
+ struct dwc3 *dwc = dep->dwc;
+
if (dep->trb_pool)
return 0;
if (dep->number == 0 || dep->number == 1)
return 0;
- dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) *
- DWC3_TRB_NUM,
- (unsigned long *)&dep->trb_pool_dma);
+ dep->trb_pool = dma_alloc_coherent(dwc->dev,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ &dep->trb_pool_dma, GFP_KERNEL);
if (!dep->trb_pool) {
dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
dep->name);
@@ -398,7 +369,10 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
- dma_free_coherent(dep->trb_pool);
+ struct dwc3 *dwc = dep->dwc;
+
+ dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ dep->trb_pool, dep->trb_pool_dma);
dep->trb_pool = NULL;
dep->trb_pool_dma = 0;
@@ -640,6 +614,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
struct dwc3_ep *dep;
+ struct dwc3 *dwc;
unsigned long flags;
int ret;
@@ -654,9 +629,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
}
dep = to_dwc3_ep(ep);
+ dwc = dep->dwc;
if (dep->flags & DWC3_EP_ENABLED) {
- WARN(true, "%s is already enabled\n",
+ dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
dep->name);
return 0;
}
@@ -675,7 +651,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
strlcat(dep->name, "-int", sizeof(dep->name));
break;
default:
- dev_err(dep->dwc->dev, "invalid endpoint transfer type\n");
+ dev_err(dwc->dev, "invalid endpoint transfer type\n");
}
spin_lock_irqsave(&dwc->lock, flags);
@@ -688,6 +664,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
static int dwc3_gadget_ep_disable(struct usb_ep *ep)
{
struct dwc3_ep *dep;
+ struct dwc3 *dwc;
unsigned long flags;
int ret;
@@ -697,9 +674,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
}
dep = to_dwc3_ep(ep);
+ dwc = dep->dwc;
if (!(dep->flags & DWC3_EP_ENABLED)) {
- WARN(true, "%s is already disabled\n",
+ dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
dep->name);
return 0;
}
@@ -728,6 +706,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
req->epnum = dep->number;
req->dep = dep;
+ trace_dwc3_alloc_request(req);
+
return &req->request;
}
@@ -736,6 +716,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
{
struct dwc3_request *req = to_dwc3_request(request);
+ trace_dwc3_free_request(req);
kfree(req);
}
@@ -748,11 +729,14 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
unsigned length, unsigned last, unsigned chain, unsigned node)
{
+ struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- dev_vdbg(dep->dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
- dep->name, req, (unsigned long long)dma,
- length, last ? " last" : "", chain ? " chain" : "");
+ dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
+ dep->name, req, (unsigned long long) dma,
+ length, last ? " last" : "",
+ chain ? " chain" : "");
+
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
@@ -815,8 +799,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl |= DWC3_TRB_CTRL_HWO;
- dwc3_flush_cache((uintptr_t)dma, length);
- dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
+ trace_dwc3_prepare_trb(dep, trb);
}
/*
@@ -833,6 +816,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
struct dwc3_request *req, *n;
u32 trbs_left;
u32 max;
+ unsigned int last_one = 0;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
@@ -882,14 +866,59 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
list_for_each_entry_safe(req, n, &dep->request_list, list) {
unsigned length;
dma_addr_t dma;
+ last_one = false;
- dma = req->request.dma;
- length = req->request.length;
+ if (req->request.num_mapped_sgs > 0) {
+ struct usb_request *request = &req->request;
+ struct scatterlist *sg = request->sg;
+ struct scatterlist *s;
+ int i;
- dwc3_prepare_one_trb(dep, req, dma, length,
- true, false, 0);
+ for_each_sg(sg, s, request->num_mapped_sgs, i) {
+ unsigned chain = true;
- break;
+ length = sg_dma_len(s);
+ dma = sg_dma_address(s);
+
+ if (i == (request->num_mapped_sgs - 1) ||
+ sg_is_last(s)) {
+ if (list_is_last(&req->list,
+ &dep->request_list))
+ last_one = true;
+ chain = false;
+ }
+
+ trbs_left--;
+ if (!trbs_left)
+ last_one = true;
+
+ if (last_one)
+ chain = false;
+
+ dwc3_prepare_one_trb(dep, req, dma, length,
+ last_one, chain, i);
+
+ if (last_one)
+ break;
+ }
+ } else {
+ dma = req->request.dma;
+ length = req->request.length;
+ trbs_left--;
+
+ if (!trbs_left)
+ last_one = 1;
+
+ /* Is this the last request? */
+ if (list_is_last(&req->list, &dep->request_list))
+ last_one = 1;
+
+ dwc3_prepare_one_trb(dep, req, dma, length,
+ last_one, false, 0);
+
+ if (last_one)
+ break;
+ }
}
}
@@ -1007,14 +1036,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
req->direction = dep->direction;
req->epnum = dep->number;
- /*
- * DWC3 hangs on OUT requests smaller than maxpacket size,
- * so HACK the request length
- */
- if (dep->direction == 0 &&
- req->request.length < dep->endpoint.maxpacket)
- req->request.length = dep->endpoint.maxpacket;
-
/*
* We only add to our list of requests now and
* start consuming the list once we get XferNotReady
@@ -1094,6 +1115,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
if (ret && ret != -EBUSY) {
+ struct dwc3 *dwc = dep->dwc;
+
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
}
@@ -1107,6 +1130,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
{
struct dwc3_request *req = to_dwc3_request(request);
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
unsigned long flags;
@@ -1114,22 +1138,21 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
if (!dep->endpoint.desc) {
- dev_dbg(dep->dwc->dev,
- "trying to queue request %p to disabled %s\n", request,
- ep->name);
+ dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
+ request, ep->name);
ret = -ESHUTDOWN;
goto out;
}
- if (req->dep != dep) {
- WARN(true, "request %p belongs to '%s'\n", request,
- req->dep->name);
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
+ request, req->dep->name)) {
ret = -EINVAL;
goto out;
}
- dev_vdbg(dep->dwc->dev, "queing request %p to %s length %d\n",
- request, ep->name, request->length);
+ dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
+ request, ep->name, request->length);
+ trace_dwc3_ep_queue(req);
ret = __dwc3_gadget_ep_queue(dep, req);
@@ -1151,6 +1174,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
unsigned long flags;
int ret = 0;
+ trace_dwc3_ep_dequeue(req);
+
spin_lock_irqsave(&dwc->lock, flags);
list_for_each_entry(r, &dep->request_list, list) {
@@ -1229,6 +1254,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
unsigned long flags;
@@ -1244,6 +1270,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
unsigned long flags;
int ret;
@@ -1359,9 +1386,9 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
}
/* poll until Link State changes to ON */
- timeout = 1000;
+ timeout = jiffies + msecs_to_jiffies(100);
- while (timeout--) {
+ while (!time_after(jiffies, timeout)) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
/* in HS, means ON */
@@ -1486,6 +1513,9 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
}
+static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
+static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
+
static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
@@ -1493,14 +1523,24 @@ static int dwc3_gadget_start(struct usb_gadget *g,
struct dwc3_ep *dep;
unsigned long flags;
int ret = 0;
+ int irq;
u32 reg;
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
+ IRQF_SHARED, "dwc3", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ irq, ret);
+ goto err0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver) {
dev_err(dwc->dev, "%s is already bound to %s\n",
dwc->gadget.name,
- dwc->gadget_driver->function);
+ dwc->gadget_driver->driver.name);
ret = -EBUSY;
goto err1;
}
@@ -1584,6 +1624,9 @@ err2:
err1:
spin_unlock_irqrestore(&dwc->lock, flags);
+ free_irq(irq, dwc);
+
+err0:
return ret;
}
@@ -1591,6 +1634,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int irq;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1602,56 +1646,10 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
spin_unlock_irqrestore(&dwc->lock, flags);
- return 0;
-}
-
-static struct usb_ep *dwc3_find_ep(struct usb_gadget *gadget, const char *name)
-{
- struct usb_ep *ep;
-
- list_for_each_entry(ep, &gadget->ep_list, ep_list)
- if (!strcmp(ep->name, name))
- return ep;
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ free_irq(irq, dwc);
- return NULL;
-}
-
-static struct
-usb_ep *dwc3_gadget_match_ep(struct usb_gadget *gadget,
- struct usb_endpoint_descriptor *desc,
- struct usb_ss_ep_comp_descriptor *comp_desc)
-{
- /*
- * First try standard, common configuration: ep1in-bulk,
- * ep2out-bulk, ep3in-int to match other udc drivers to avoid
- * confusion in already deployed software (endpoint numbers
- * hardcoded in userspace software/drivers)
- */
- if (usb_endpoint_is_bulk_in(desc))
- return dwc3_find_ep(gadget, "ep1in");
- if (usb_endpoint_is_bulk_out(desc))
- return dwc3_find_ep(gadget, "ep2out");
- if (usb_endpoint_is_int_in(desc)) {
- /*
- * Special workaround for NXP UUU tool in SPL.
- *
- * The tool expects the interrupt-in endpoint to be ep1in,
- * otherwise it crashes. This is a result of the previous
- * hard-coded EP setup in drivers/usb/gadget/epautoconf.c
- * which did special-case EP allocation for SPL builds,
- * and which was since converted to this callback, but
- * without the special-case EP allocation in SPL part.
- *
- * This reinstates the SPL part in an isolated manner,
- * only for NXP iMX SoCs, only for SPL builds, and only
- * for the ep1in interrupt-in endpoint.
- */
- if (IS_ENABLED(CONFIG_MACH_IMX) && IS_ENABLED(CONFIG_XPL_BUILD))
- return dwc3_find_ep(gadget, "ep1in");
- return dwc3_find_ep(gadget, "ep3in");
- }
-
- return NULL;
+ return 0;
}
static const struct usb_gadget_ops dwc3_gadget_ops = {
@@ -1661,7 +1659,6 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
- .match_ep = dwc3_gadget_match_ep,
};
/* -------------------------------------------------------------------------- */
@@ -1700,7 +1697,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
} else {
int ret;
- usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
dep->endpoint.max_streams = 15;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
@@ -1776,6 +1773,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int s_pkt = 0;
unsigned int trb_status;
+ trace_dwc3_complete_trb(dep, trb);
+
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
/*
* We continue despite the error. There is not much we
@@ -1850,23 +1849,35 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_request *req;
struct dwc3_trb *trb;
unsigned int slot;
+ unsigned int i;
+ int ret;
- req = next_request(&dep->req_queued);
- if (!req) {
- WARN_ON_ONCE(1);
- return 1;
- }
+ do {
+ req = next_request(&dep->req_queued);
+ if (!req) {
+ WARN_ON_ONCE(1);
+ return 1;
+ }
+ i = 0;
+ do {
+ slot = req->start_slot + i;
+ if ((slot == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ slot++;
+ slot %= DWC3_TRB_NUM;
+ trb = &dep->trb_pool[slot];
+
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status);
+ if (ret)
+ break;
+ }while (++i < req->request.num_mapped_sgs);
- slot = req->start_slot;
- if ((slot == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
- slot++;
- slot %= DWC3_TRB_NUM;
- trb = &dep->trb_pool[slot];
+ dwc3_gadget_giveback(dep, req, status);
- dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
- __dwc3_cleanup_done_trbs(dwc, dep, req, trb, event, status);
- dwc3_gadget_giveback(dep, req, status);
+ if (ret)
+ break;
+ } while (1);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) {
@@ -2299,8 +2310,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* BESL value in the LPM token is less than or equal to LPM
* NYET threshold.
*/
- if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum)
- WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+ WARN_ONCE(dwc->revision < DWC3_REVISION_240A
+ && dwc->has_lpm_erratum,
+ "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
@@ -2449,7 +2461,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
unsigned int evtinfo)
{
- unsigned int is_ss = evtinfo & (1UL << 4);
+ unsigned int is_ss = evtinfo & BIT(4);
/**
* WORKAROUND: DWC3 revison 2.20a with hibernation support
@@ -2487,10 +2499,10 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_wakeup_interrupt(dwc);
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
- if (!dwc->has_hibernation) {
- WARN(1 ,"unexpected hibernation event\n");
+ if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
+ "unexpected hibernation event\n"))
break;
- }
+
dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
@@ -2519,6 +2531,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
static void dwc3_process_event_entry(struct dwc3 *dwc,
const union dwc3_event *event)
{
+ trace_dwc3_event(event->raw);
+
/* Endpoint IRQ, handle it and return early */
if (event->type.is_devspec == 0) {
/* depevt */
@@ -2551,8 +2565,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
while (left > 0) {
union dwc3_event event;
- dwc3_invalidate_cache((uintptr_t)evt->buf, evt->length);
-
event.raw = *(u32 *) (evt->buf + evt->lpos);
dwc3_process_event_entry(dwc, &event);
@@ -2656,31 +2668,31 @@ int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
- dwc->ctrl_req = dma_alloc_coherent(sizeof(*dwc->ctrl_req),
- (unsigned long *)&dwc->ctrl_req_addr);
+ dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ &dwc->ctrl_req_addr, GFP_KERNEL);
if (!dwc->ctrl_req) {
dev_err(dwc->dev, "failed to allocate ctrl request\n");
ret = -ENOMEM;
goto err0;
}
- dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2,
- (unsigned long *)&dwc->ep0_trb_addr);
+ dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ &dwc->ep0_trb_addr, GFP_KERNEL);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
goto err1;
}
- dwc->setup_buf = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE,
- (unsigned long *)&dwc->setup_buf_addr);
+ dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
if (!dwc->setup_buf) {
ret = -ENOMEM;
goto err2;
}
- dwc->ep0_bounce = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE,
- (unsigned long *)&dwc->ep0_bounce_addr);
+ dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
+ DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
+ GFP_KERNEL);
if (!dwc->ep0_bounce) {
dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
ret = -ENOMEM;
@@ -2690,6 +2702,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.max_speed = USB_SPEED_SUPER;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
/*
@@ -2707,7 +2720,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;
- ret = usb_add_gadget_udc((struct device *)dwc->dev, &dwc->gadget);
+ ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err4;
@@ -2717,16 +2730,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
err4:
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->ep0_bounce);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
err3:
- dma_free_coherent(dwc->setup_buf);
+ kfree(dwc->setup_buf);
err2:
- dma_free_coherent(dwc->ep0_trb);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
err1:
- dma_free_coherent(dwc->ctrl_req);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
err0:
return ret;
@@ -2740,37 +2756,69 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->ep0_bounce);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
- dma_free_coherent(dwc->setup_buf);
+ kfree(dwc->setup_buf);
- dma_free_coherent(dwc->ep0_trb);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
- dma_free_coherent(dwc->ctrl_req);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
}
-/**
- * dwc3_gadget_uboot_handle_interrupt - handle dwc3 gadget interrupt
- * @dwc: struct dwce *
- *
- * Handles ep0 and gadget interrupt
- *
- * Should be called from dwc3 core.
- */
-void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc)
+int dwc3_gadget_suspend(struct dwc3 *dwc)
{
- int ret = dwc3_interrupt(0, dwc);
+ if (dwc->pullups_connected) {
+ dwc3_gadget_disable_irq(dwc);
+ dwc3_gadget_run_stop(dwc, true, true);
+ }
- if (ret == IRQ_WAKE_THREAD) {
- int i;
- struct dwc3_event_buffer *evt;
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
- dwc3_thread_interrupt(0, dwc);
+ dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG);
- /* Clean + Invalidate the buffers after touching them */
- for (i = 0; i < dwc->num_event_buffers; i++) {
- evt = dwc->ev_buffs[i];
- dwc3_flush_cache((uintptr_t)evt->buf, evt->length);
- }
+ return 0;
+}
+
+int dwc3_gadget_resume(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret;
+
+ /* Start with SuperSpeed Default */
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+ false);
+ if (ret)
+ goto err0;
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+ false);
+ if (ret)
+ goto err1;
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+
+ dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
+
+ if (dwc->pullups_connected) {
+ dwc3_gadget_enable_irq(dwc);
+ dwc3_gadget_run_stop(dwc, true, false);
}
+
+ return 0;
+
+err1:
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+
+err0:
+ return ret;
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index f28a9755dcb3..18ae3eaa8b6f 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -1,18 +1,19 @@
-/* SPDX-License-Identifier: GPL-2.0 */
/**
* gadget.h - DesignWare USB3 DRD Gadget Header
*
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported
- * to uboot.
- *
- * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending
- transfers
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
#ifndef __DRIVERS_USB_DWC3_GADGET_H
@@ -86,7 +87,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
-void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index c1ab02881424..6a79c8e66bbc 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -1,29 +1,32 @@
-/* SPDX-License-Identifier: GPL-2.0 */
/**
* io.h - DesignWare USB3 DRD IO Header
*
- * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/io.h) and ported
- * to uboot.
- *
- * commit 2c4cbe6e5a : usb: dwc3: add tracepoints to aid debugging
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
#ifndef __DRIVERS_USB_DWC3_IO_H
#define __DRIVERS_USB_DWC3_IO_H
-#include <cpu_func.h>
-#include <asm/io.h>
+#include <linux/io.h>
+#include "trace.h"
+#include "debug.h"
+#include "core.h"
-#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
{
- unsigned long offs = offset - DWC3_GLOBALS_REGS_START;
+ u32 offs = offset - DWC3_GLOBALS_REGS_START;
u32 value;
/*
@@ -33,12 +36,20 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
*/
value = readl(base + offs);
+ /*
+ * When tracing we want to make it easy to find the correct address on
+ * documentation, so we revert it back to the proper addresses, the
+ * same way they are described on SNPS documentation
+ */
+ dwc3_trace(trace_dwc3_readl, "addr %p value %08x",
+ base - DWC3_GLOBALS_REGS_START + offset, value);
+
return value;
}
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
{
- unsigned long offs = offset - DWC3_GLOBALS_REGS_START;
+ u32 offs = offset - DWC3_GLOBALS_REGS_START;
/*
* We requested the mem region starting from the Globals address
@@ -46,21 +57,14 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
* However, the offsets are given starting from xHCI address space.
*/
writel(value, base + offs);
-}
-
-static inline void dwc3_flush_cache(uintptr_t addr, int length)
-{
- uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1);
- uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE);
- flush_dcache_range((unsigned long)start_addr, (unsigned long)end_addr);
+ /*
+ * When tracing we want to make it easy to find the correct address on
+ * documentation, so we revert it back to the proper addresses, the
+ * same way they are described on SNPS documentation
+ */
+ dwc3_trace(trace_dwc3_writel, "addr %p value %08x",
+ base - DWC3_GLOBALS_REGS_START + offset, value);
}
-static inline void dwc3_invalidate_cache(uintptr_t addr, int length)
-{
- uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1);
- uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE);
-
- invalidate_dcache_range((unsigned long)start_addr, (unsigned long)end_addr);
-}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h
deleted file mode 100644
index 563f8727cdde..000000000000
--- a/drivers/usb/dwc3/linux-compat.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/**
- * linux-compat.h - DesignWare USB3 Linux Compatibiltiy Adapter Header
- *
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
- *
- * Authors: Kishon Vijay Abraham I <kishon@ti.com>
- *
- */
-
-#ifndef __DWC3_LINUX_COMPAT__
-#define __DWC3_LINUX_COMPAT__
-
-#define dev_WARN(dev, format, arg...) debug(format, ##arg)
-
-#endif
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
new file mode 100644
index 000000000000..a3a3b6d5668c
--- /dev/null
+++ b/drivers/usb/dwc3/platform_data.h
@@ -0,0 +1,47 @@
+/**
+ * platform_data.h - USB DWC3 Platform Data Support
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/otg.h>
+
+struct dwc3_platform_data {
+ enum usb_device_speed maximum_speed;
+ enum usb_dr_mode dr_mode;
+ bool tx_fifo_resize;
+
+ unsigned is_utmi_l1_suspend:1;
+ u8 hird_threshold;
+
+ u8 lpm_nyet_threshold;
+
+ unsigned disable_scramble_quirk:1;
+ unsigned has_lpm_erratum:1;
+ unsigned u2exit_lfps_quirk:1;
+ unsigned u2ss_inp3_quirk:1;
+ unsigned req_p1p2p3_quirk:1;
+ unsigned del_p1p2p3_quirk:1;
+ unsigned del_phy_power_chg_quirk:1;
+ unsigned lfps_filter_quirk:1;
+ unsigned rx_detect_poll_quirk:1;
+ unsigned dis_u3_susphy_quirk:1;
+ unsigned dis_u2_susphy_quirk:1;
+
+ unsigned tx_de_emphasis_quirk:1;
+ unsigned tx_de_emphasis:2;
+};
diff --git a/drivers/usb/dwc3/samsung_usb_phy.c b/drivers/usb/dwc3/samsung_usb_phy.c
deleted file mode 100644
index 3563070cb85d..000000000000
--- a/drivers/usb/dwc3/samsung_usb_phy.c
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/**
- * samsung_usb_phy.c - DesignWare USB3 (DWC3) PHY handling file
- *
- * Copyright (C) 2015 Samsung Electronics
- *
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
- */
-
-#include <asm/io.h>
-#include <asm/arch/power.h>
-#include <asm/arch/xhci-exynos.h>
-#include <linux/delay.h>
-
-void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
-{
- u32 reg;
-
- /* Reset USB 3.0 PHY */
- writel(0x0, &phy->phy_reg0);
-
- clrbits_le32(&phy->phy_param0,
- /* Select PHY CLK source */
- PHYPARAM0_REF_USE_PAD |
- /* Set Loss-of-Signal Detector sensitivity */
- PHYPARAM0_REF_LOSLEVEL_MASK);
- setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL);
-
- writel(0x0, &phy->phy_resume);
-
- /*
- * Setting the Frame length Adj value[6:1] to default 0x20
- * See xHCI 1.0 spec, 5.2.4
- */
- setbits_le32(&phy->link_system,
- LINKSYSTEM_XHCI_VERSION_CONTROL |
- LINKSYSTEM_FLADJ(0x20));
-
- /* Set Tx De-Emphasis level */
- clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK);
- setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH);
-
- setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL);
-
- /* PHYTEST POWERDOWN Control */
- clrbits_le32(&phy->phy_test,
- PHYTEST_POWERDOWN_SSP |
- PHYTEST_POWERDOWN_HSP);
-
- /* UTMI Power Control */
- writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi);
-
- /* Use core clock from main PLL */
- reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
- /* Default 24Mhz crystal clock */
- PHYCLKRST_FSEL(FSEL_CLKSEL_24M) |
- PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
- PHYCLKRST_SSC_REFCLKSEL(0) |
- /* Force PortReset of PHY */
- PHYCLKRST_PORTRESET |
- /* Digital power supply in normal operating mode */
- PHYCLKRST_RETENABLEN |
- /* Enable ref clock for SS function */
- PHYCLKRST_REF_SSP_EN |
- /* Enable spread spectrum */
- PHYCLKRST_SSC_EN |
- /* Power down HS Bias and PLL blocks in suspend mode */
- PHYCLKRST_COMMONONN;
-
- writel(reg, &phy->phy_clk_rst);
-
- /* giving time to Phy clock to settle before resetting */
- udelay(10);
-
- reg &= ~PHYCLKRST_PORTRESET;
- writel(reg, &phy->phy_clk_rst);
-}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 02/64] usb: dwc3: import from kernel v3.19
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:25 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 03/64] usb: dwc3: import from kernel v4.0 Jens Wiklander
` (65 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v3.19-rc1 to v3.19.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v3.19-rc1..v3.19
Commits imported:
39e60635a015 usb: dwc3: gadget: Stop TRB preparation after limit is reached
ec512fb8e561 usb: dwc3: gadget: Fix TRB preparation during SG
84a2b61b6eb9 usb: dwc3: pci: add support for Intel Sunrise Point PCH
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/gadget.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f03b136ecfce..8f65ab3a3b92 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -882,8 +882,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
if (i == (request->num_mapped_sgs - 1) ||
sg_is_last(s)) {
- if (list_is_last(&req->list,
- &dep->request_list))
+ if (list_empty(&dep->request_list))
last_one = true;
chain = false;
}
@@ -901,6 +900,9 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
if (last_one)
break;
}
+
+ if (last_one)
+ break;
} else {
dma = req->request.dma;
length = req->request.length;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 03/64] usb: dwc3: import from kernel v4.0
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 02/64] usb: dwc3: import from kernel v3.19 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 04/64] usb: dwc3: import from kernel v4.1 Jens Wiklander
` (64 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v3.19 to v4.0.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v3.19..v4.0
Commits imported:
96e5d31244c5 usb: dwc3: dwc3-omap: Fix disable IRQ
bcdea50312b2 usb: dwc3: gadget: use common is_selfpowered
5c7b3b02de76 usb: dwc3: gadget: add missing spin_lock()
73815280a5af usb: dwc3: remove reliance on dev_vdbg()
6bac4ff0a522 usb: dwc3: trace: add trace logs for core and gadget
e9f2aa871c73 usb: dwc3: gadget: WARN() in case of unknown IRQ
4cd8f6d05599 usb: dwc3: gadget: avoid variable shadowing
d1fc4440d761 Merge tag 'v3.19-rc5' into next
2cd9ddf77e0d usb: dwc3: pci: code cleanup
3b44ed90cdb2 usb: dwc3: pci: rely on default PM callbacks from PCI driver utility
d3fdcc78b2d6 usb: dwc3: pci: remove registration of NOP PHYs
3cd0e29d5e28 usb: dwc3: Remove current_trb as it is unused
110381e11b5f usb: dwc3: gadget: Remove redundant check
dc93b41a1561 usb: dwc3: Fixed a typo in comments
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 2 +-
drivers/usb/dwc3/core.h | 5 ---
drivers/usb/dwc3/ep0.c | 2 +-
drivers/usb/dwc3/gadget.c | 82 ++++++++++++++++++++-------------------
4 files changed, 45 insertions(+), 46 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 25ddc39efad8..9f0e209b8f6c 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -345,7 +345,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
- dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n",
+ dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints",
dwc->num_in_eps, dwc->num_out_eps);
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4bb9aa696ede..d201910b892f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -431,7 +431,6 @@ struct dwc3_event_buffer {
* @dwc: pointer to DWC controller
* @saved_state: ep state saved during hibernation
* @flags: endpoint flags (wedged, stalled, ...)
- * @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
@@ -464,8 +463,6 @@ struct dwc3_ep {
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
- unsigned current_trb;
-
u8 number;
u8 type;
u8 resource_index;
@@ -685,7 +682,6 @@ struct dwc3_scratchpad_array {
* @is_utmi_l1_suspend: the core asserts output signal
* 0 - utmi_sleep_n
* 1 - utmi_l1_suspend_n
- * @is_selfpowered: true when we are selfpowered
* @is_fpga: true when we are using the FPGA board
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @pullups_connected: true when Run/Stop bit is set
@@ -809,7 +805,6 @@ struct dwc3 {
unsigned has_hibernation:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
- unsigned is_selfpowered:1;
unsigned is_fpga:1;
unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 1bc77a3b4997..2ef3c8d6a9db 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -344,7 +344,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
/*
* LTM will be set once we know how to set this in HW.
*/
- usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+ usb_status |= dwc->gadget.is_selfpowered;
if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8f65ab3a3b92..a03a485205c7 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -139,7 +139,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
udelay(5);
}
- dev_vdbg(dwc->dev, "link state change request timed out\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "link state change request timed out");
return -ETIMEDOUT;
}
@@ -219,7 +220,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
fifo_size |= (last_fifo_depth << 16);
- dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
+ dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d",
dep->name, last_fifo_depth, fifo_size & 0xffff);
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
@@ -287,7 +288,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
do {
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) {
- dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Complete --> %d",
DWC3_DGCMD_STATUS(reg));
return 0;
}
@@ -297,8 +299,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
* interrupt context.
*/
timeout--;
- if (!timeout)
+ if (!timeout) {
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Timed Out");
return -ETIMEDOUT;
+ }
udelay(1);
} while (1);
}
@@ -320,7 +325,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
do {
reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
if (!(reg & DWC3_DEPCMD_CMDACT)) {
- dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Complete --> %d",
DWC3_DEPCMD_STATUS(reg));
return 0;
}
@@ -330,8 +336,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
* interrupt context.
*/
timeout--;
- if (!timeout)
+ if (!timeout) {
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Timed Out");
return -ETIMEDOUT;
+ }
udelay(1);
} while (1);
@@ -352,9 +361,6 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
if (dep->trb_pool)
return 0;
- if (dep->number == 0 || dep->number == 1)
- return 0;
-
dep->trb_pool = dma_alloc_coherent(dwc->dev,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
&dep->trb_pool_dma, GFP_KERNEL);
@@ -492,7 +498,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
u32 reg;
int ret;
- dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
if (!(dep->flags & DWC3_EP_ENABLED)) {
ret = dwc3_gadget_start_config(dwc, dep);
@@ -729,10 +735,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
unsigned length, unsigned last, unsigned chain, unsigned node)
{
- struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
+ dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s",
dep->name, req, (unsigned long long) dma,
length, last ? " last" : "",
chain ? " chain" : "");
@@ -934,7 +939,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
u32 cmd;
if (start_new && (dep->flags & DWC3_EP_BUSY)) {
- dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name);
return -EBUSY;
}
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
@@ -1005,8 +1010,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
u32 uf;
if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
- dep->name);
+ dwc3_trace(trace_dwc3_gadget,
+ "ISOC ep %s run out for requests",
+ dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
}
@@ -1113,15 +1119,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* handled.
*/
if (dep->stream_capable) {
- int ret;
-
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
-
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
}
return 0;
@@ -1152,8 +1153,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
- dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
- request, ep->name, request->length);
trace_dwc3_ep_queue(req);
ret = __dwc3_gadget_ep_queue(dep, req);
@@ -1416,7 +1415,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
- dwc->is_selfpowered = !!is_selfpowered;
+ g->is_selfpowered = !!is_selfpowered;
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
@@ -1468,7 +1467,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
udelay(1);
} while (1);
- dev_vdbg(dwc->dev, "gadget %s data soft-%s\n",
+ dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
dwc->gadget_driver
? dwc->gadget_driver->function : "no-function",
is_on ? "connect" : "disconnect");
@@ -1688,7 +1687,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.name = dep->name;
- dev_vdbg(dwc->dev, "initializing %s\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
if (epnum == 0 || epnum == 1) {
usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
@@ -1725,13 +1724,15 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
- dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "failed to allocate OUT endpoints");
return ret;
}
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
if (ret < 0) {
- dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "failed to allocate IN endpoints");
return ret;
}
@@ -1977,7 +1978,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
} else {
int ret;
- dev_vdbg(dwc->dev, "%s: reason %s\n",
+ dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
dep->name, event->status &
DEPEVT_STATUS_TRANSFER_ACTIVE
? "Transfer Active"
@@ -2001,7 +2002,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
switch (event->status) {
case DEPEVT_STREAMEVT_FOUND:
- dev_vdbg(dwc->dev, "Stream %d found and started\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "Stream %d found and started",
event->parameters);
break;
@@ -2015,7 +2017,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
+ dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
break;
}
}
@@ -2043,6 +2045,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->resume(&dwc->gadget);
+ spin_lock(&dwc->lock);
}
}
@@ -2079,7 +2082,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
* We have discussed this with the IP Provider and it was
* suggested to giveback all requests here, but give HW some
* extra time to synchronize with the interconnect. We're using
- * an arbitraty 100us delay for that.
+ * an arbitrary 100us delay for that.
*
* Note also that a similar handling was tested by Synopsys
* (thanks a lot Paul) and nothing bad has come out of it.
@@ -2389,7 +2392,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
- dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "ignoring transition U3 -> Resume");
return;
}
}
@@ -2511,22 +2515,22 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_EOPF:
- dev_vdbg(dwc->dev, "End of Periodic Frame\n");
+ dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
break;
case DWC3_DEVICE_EVENT_SOF:
- dev_vdbg(dwc->dev, "Start of Periodic Frame\n");
+ dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- dev_vdbg(dwc->dev, "Erratic Error\n");
+ dwc3_trace(trace_dwc3_gadget, "Erratic Error");
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- dev_vdbg(dwc->dev, "Command Complete\n");
+ dwc3_trace(trace_dwc3_gadget, "Command Complete");
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- dev_vdbg(dwc->dev, "Overflow\n");
+ dwc3_trace(trace_dwc3_gadget, "Overflow");
break;
default:
- dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 04/64] usb: dwc3: import from kernel v4.1
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (2 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 03/64] usb: dwc3: import from kernel v4.0 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 05/64] usb: dwc3: import from kernel v4.2 Jens Wiklander
` (63 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.0 to v4.1.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.0..v4.1
Commits imported:
459e210c4fd0 usb: dwc3: gadget: Fix incorrect DEPCMD and DGCMD status macros
228321904089 usb: dwc3: dwc3-omap: correct the register macros
eac68e8f979b usb: dwc3: make LPM configurable in DT
adf9c3c85615 usb: move definition of PCI_VENDOR_ID_SYNOPSYS to linux/pci_ids.h
8f2c9544aba6 usb: dwc3: gadget: drop unnecessary loop when cleaning up TRBs
3d0184d08757 usb: dwc3: omap: call of_platform_depopulate() instead
3da1f6ee3563 usb: dwc3: core: only reset res->start in case of error
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 59 ++++++++++++++++++++------------
drivers/usb/dwc3/core.h | 6 ++--
drivers/usb/dwc3/gadget.c | 37 +++++++++-----------
drivers/usb/dwc3/platform_data.h | 1 +
4 files changed, 58 insertions(+), 45 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9f0e209b8f6c..2bbab3d86fff 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
* since it will be requested by the xhci-plat driver.
*/
regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto err0;
+ }
dwc->regs = regs;
dwc->regs_size = resource_size(res);
- /*
- * restore res->start back to its original value so that,
- * in case the probe is deferred, we don't end up getting error in
- * request the memory region the next time probe is called.
- */
- res->start -= DWC3_GLOBALS_REGS_START;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,is-utmi-l1-suspend");
of_property_read_u8(node, "snps,hird-threshold",
&hird_threshold);
+ dwc->usb3_lpm_capable = of_property_read_bool(node,
+ "snps,usb3_lpm_capable");
dwc->needs_fifo_resize = of_property_read_bool(node,
"tx-fifo-resize");
@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
hird_threshold = pdata->hird_threshold;
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
+ dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
dwc->dr_mode = pdata->dr_mode;
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_get_phy(dwc);
if (ret)
- return ret;
+ goto err0;
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
- goto err0;
+ goto err1;
}
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize core\n");
- goto err0;
+ goto err1;
}
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0)
- goto err1;
+ goto err2;
ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0)
- goto err_usb2phy_power;
+ goto err3;
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err_usb3phy_power;
+ goto err4;
}
ret = dwc3_core_init_mode(dwc);
if (ret)
- goto err2;
+ goto err5;
ret = dwc3_debugfs_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize debugfs\n");
- goto err3;
+ goto err6;
}
pm_runtime_allow(dev);
return 0;
-err3:
+err6:
dwc3_core_exit_mode(dwc);
-err2:
+err5:
dwc3_event_buffers_cleanup(dwc);
-err_usb3phy_power:
+err4:
phy_power_off(dwc->usb3_generic_phy);
-err_usb2phy_power:
+err3:
phy_power_off(dwc->usb2_generic_phy);
-err1:
+err2:
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
dwc3_core_exit(dwc);
-err0:
+err1:
dwc3_free_event_buffers(dwc);
+err0:
+ /*
+ * restore res->start back to its original value so that, in case the
+ * probe is deferred, we don't end up getting error in request the
+ * memory region the next time probe is called.
+ */
+ res->start -= DWC3_GLOBALS_REGS_START;
+
return ret;
}
static int dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /*
+ * restore res->start back to its original value so that, in case the
+ * probe is deferred, we don't end up getting error in request the
+ * memory region the next time probe is called.
+ */
+ res->start -= DWC3_GLOBALS_REGS_START;
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index d201910b892f..c0eafa6fd403 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -339,7 +339,7 @@
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
-#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
+#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
#define DWC3_DGCMD_CMDACT (1 << 10)
#define DWC3_DGCMD_CMDIOC (1 << 8)
@@ -355,7 +355,7 @@
#define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
-#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
+#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
#define DWC3_DEPCMD_CMDIOC (1 << 8)
@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
+ * @usb3_lpm_capable: set if hadrware supports Link Power Management
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -812,6 +813,7 @@ struct dwc3 {
unsigned setup_packet_pending:1;
unsigned start_config_issued:1;
unsigned three_stage_setup:1;
+ unsigned usb3_lpm_capable:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a03a485205c7..8946c32047e9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int i;
int ret;
+ req = next_request(&dep->req_queued);
+ if (!req) {
+ WARN_ON_ONCE(1);
+ return 1;
+ }
+ i = 0;
do {
- req = next_request(&dep->req_queued);
- if (!req) {
- WARN_ON_ONCE(1);
- return 1;
- }
- i = 0;
- do {
- slot = req->start_slot + i;
- if ((slot == DWC3_TRB_NUM - 1) &&
+ slot = req->start_slot + i;
+ if ((slot == DWC3_TRB_NUM - 1) &&
usb_endpoint_xfer_isoc(dep->endpoint.desc))
- slot++;
- slot %= DWC3_TRB_NUM;
- trb = &dep->trb_pool[slot];
-
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status);
- if (ret)
- break;
- }while (++i < req->request.num_mapped_sgs);
-
- dwc3_gadget_giveback(dep, req, status);
+ slot++;
+ slot %= DWC3_TRB_NUM;
+ trb = &dep->trb_pool[slot];
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status);
if (ret)
break;
- } while (1);
+ } while (++i < req->request.num_mapped_sgs);
+
+ dwc3_gadget_giveback(dep, req, status);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) {
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
index a3a3b6d5668c..a2bd464be828 100644
--- a/drivers/usb/dwc3/platform_data.h
+++ b/drivers/usb/dwc3/platform_data.h
@@ -24,6 +24,7 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode;
bool tx_fifo_resize;
+ bool usb3_lpm_capable;
unsigned is_utmi_l1_suspend:1;
u8 hird_threshold;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 05/64] usb: dwc3: import from kernel v4.2
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (3 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 04/64] usb: dwc3: import from kernel v4.1 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 06/64] usb: dwc3: import from kernel v4.3 Jens Wiklander
` (62 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.1 to v4.2.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.1..v4.2
Commits imported:
aebda6187181 usb: dwc3: Reset the transfer resource index on SET_INTERFACE
43cacb03aabe usb: dwc3: core: avoid NULL pointer dereference
50641056d833 usb: dwc3: Use ASCII space in Kconfig
19915e623458 Merge 4.1-rc7 into usb-next
e152813ff5fe Merge tag 'usb-for-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
e18b7975c885 usb: dwc3: gadget: don't clear EP_BUSY too early
76e838c9f776 usb: dwc3: gadget: return error if command sent to DEPCMD register fails
891b1dc02295 usb: dwc3: gadget: return error if command sent to DGCMD register fails
909eacd748b3 Merge 4.1-rc4 into usb-next
88bc9d194ff6 usb: dwc3: add ULPI interface support
a89d977cc04c usb: dwc3: pci: add quirk for Baytrails
3e10a2ce98d1 usb: dwc3: add hsphy_interface property
45bb7de213d8 usb: dwc3: setup phys earlier
c5cc74e8c12b usb: dwc3: soft reset to it's own function
2917e7181589 usb: dwc3: cache hwparams earlier
6c89cce0476f usb: dwc3: store driver data earlier
f699b94789a6 usb: dwc3: ULPI or UTMI+ select
b5699eeee68f usb: dwc3: USB2 PHY register access bits
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 110 ++++++++++++++++++++++++-------
drivers/usb/dwc3/core.h | 26 ++++++++
drivers/usb/dwc3/ep0.c | 4 ++
drivers/usb/dwc3/gadget.c | 10 ++-
drivers/usb/dwc3/platform_data.h | 2 +
5 files changed, 126 insertions(+), 26 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2bbab3d86fff..ff5773c66b84 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -116,6 +116,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
return 0;
}
+/**
+ * dwc3_soft_reset - Issue soft reset
+ * @dwc: Pointer to our controller context structure
+ */
+static int dwc3_soft_reset(struct dwc3 *dwc)
+{
+ unsigned long timeout;
+ u32 reg;
+
+ timeout = jiffies + msecs_to_jiffies(500);
+ dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(dwc->dev, "Reset Timed Out\n");
+ return -ETIMEDOUT;
+ }
+
+ cpu_relax();
+ } while (true);
+
+ return 0;
+}
+
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
@@ -367,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success. The USB PHY interfaces are configured but not
+ * initialized. The PHY interfaces and the PHYs get initialized together with
+ * the core in dwc3_core_init.
*/
-static void dwc3_phy_setup(struct dwc3 *dwc)
+static int dwc3_phy_setup(struct dwc3 *dwc)
{
u32 reg;
+ int ret;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
@@ -409,10 +441,43 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
- mdelay(100);
-
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ /* Select the HS PHY interface */
+ switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
+ case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
+ if (dwc->hsphy_interface &&
+ !strncmp(dwc->hsphy_interface, "utmi", 4)) {
+ reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
+ break;
+ } else if (dwc->hsphy_interface &&
+ !strncmp(dwc->hsphy_interface, "ulpi", 4)) {
+ reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ } else {
+ dev_warn(dwc->dev, "HSPHY Interface not defined\n");
+
+ /* Relying on default value. */
+ if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
+ break;
+ }
+ /* FALLTHROUGH */
+ case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
+ /* Making sure the interface and PHY are operational */
+ ret = dwc3_soft_reset(dwc);
+ if (ret)
+ return ret;
+
+ udelay(1);
+
+ ret = dwc3_ulpi_init(dwc);
+ if (ret)
+ return ret;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
@@ -427,7 +492,7 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
- mdelay(100);
+ return 0;
}
/**
@@ -438,7 +503,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
- unsigned long timeout;
u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
int ret;
@@ -466,21 +530,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
/* issue device SoftReset too */
- timeout = jiffies + msecs_to_jiffies(500);
- dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
- do {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (!(reg & DWC3_DCTL_CSFTRST))
- break;
-
- if (time_after(jiffies, timeout)) {
- dev_err(dwc->dev, "Reset Timed Out\n");
- ret = -ETIMEDOUT;
- goto err0;
- }
-
- cpu_relax();
- } while (true);
+ ret = dwc3_soft_reset(dwc);
+ if (ret)
+ goto err0;
ret = dwc3_core_soft_reset(dwc);
if (ret)
@@ -555,8 +607,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
- dwc3_phy_setup(dwc);
-
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
goto err1;
@@ -836,6 +886,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,tx_de_emphasis_quirk");
of_property_read_u8(node, "snps,tx_de_emphasis",
&tx_de_emphasis);
+ of_property_read_string(node, "snps,hsphy_interface",
+ &dwc->hsphy_interface);
} else if (pdata) {
dwc->maximum_speed = pdata->maximum_speed;
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
@@ -863,6 +915,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
if (pdata->tx_de_emphasis)
tx_de_emphasis = pdata->tx_de_emphasis;
+
+ dwc->hsphy_interface = pdata->hsphy_interface;
}
/* default to superspeed if no maximum_speed passed */
@@ -875,12 +929,18 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
+ platform_set_drvdata(pdev, dwc);
+ dwc3_cache_hwparams(dwc);
+
+ ret = dwc3_phy_setup(dwc);
+ if (ret)
+ goto err0;
+
ret = dwc3_core_get_phy(dwc);
if (ret)
goto err0;
spin_lock_init(&dwc->lock);
- platform_set_drvdata(pdev, dwc);
if (!dev->dma_mask) {
dev->dma_mask = dev->parent->dma_mask;
@@ -892,8 +952,6 @@ static int dwc3_probe(struct platform_device *pdev)
pm_runtime_get_sync(dev);
pm_runtime_forbid(dev);
- dwc3_cache_hwparams(dwc);
-
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
@@ -964,6 +1022,7 @@ err2:
err1:
dwc3_free_event_buffers(dwc);
+ dwc3_ulpi_exit(dwc);
err0:
/*
@@ -999,6 +1058,7 @@ static int dwc3_remove(struct platform_device *pdev)
phy_power_off(dwc->usb3_generic_phy);
dwc3_core_exit(dwc);
+ dwc3_ulpi_exit(dwc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index c0eafa6fd403..044778884585 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -30,6 +30,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@@ -173,6 +174,15 @@
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
+#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
+
+/* Global USB2 PHY Vendor Control Register */
+#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
+#define DWC3_GUSB2PHYACC_BUSY (1 << 23)
+#define DWC3_GUSB2PHYACC_WRITE (1 << 22)
+#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
+#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
+#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
@@ -652,6 +662,7 @@ struct dwc3_scratchpad_array {
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
* @usb3_generic_phy: pointer to USB3 PHY
+ * @ulpi: pointer to ulpi interface
* @dcfg: saved contents of DCFG register
* @gctl: saved contents of GCTL register
* @isoch_delay: wValue from Set Isochronous Delay request;
@@ -673,6 +684,7 @@ struct dwc3_scratchpad_array {
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
+ * @hsphy_interface: "utmi" or "ulpi"
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
@@ -739,6 +751,8 @@ struct dwc3 {
struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy;
+ struct ulpi *ulpi;
+
void __iomem *regs;
size_t regs_size;
@@ -800,6 +814,8 @@ struct dwc3 {
u8 lpm_nyet_threshold;
u8 hird_threshold;
+ const char *hsphy_interface;
+
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
@@ -1035,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
}
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
+#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
+int dwc3_ulpi_init(struct dwc3 *dwc);
+void dwc3_ulpi_exit(struct dwc3 *dwc);
+#else
+static inline int dwc3_ulpi_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
+{ }
+#endif
+
#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 2ef3c8d6a9db..69e769c35cf5 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -727,6 +727,10 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
+ case USB_REQ_SET_INTERFACE:
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_INTERFACE");
+ dwc->start_config_issued = false;
+ /* Fall through */
default:
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8946c32047e9..333a7c0078fc 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
dwc3_trace(trace_dwc3_gadget,
"Command Complete --> %d",
DWC3_DGCMD_STATUS(reg));
+ if (DWC3_DGCMD_STATUS(reg))
+ return -EINVAL;
return 0;
}
@@ -328,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
dwc3_trace(trace_dwc3_gadget,
"Command Complete --> %d",
DWC3_DEPCMD_STATUS(reg));
+ if (DWC3_DEPCMD_STATUS(reg))
+ return -EINVAL;
return 0;
}
@@ -1902,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
{
unsigned status = 0;
int clean_busy;
+ u32 is_xfer_complete;
+
+ is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
- if (clean_busy)
+ if (clean_busy && (is_xfer_complete ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc)))
dep->flags &= ~DWC3_EP_BUSY;
/*
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
index a2bd464be828..d3614ecbb9ca 100644
--- a/drivers/usb/dwc3/platform_data.h
+++ b/drivers/usb/dwc3/platform_data.h
@@ -45,4 +45,6 @@ struct dwc3_platform_data {
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
+
+ const char *hsphy_interface;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 06/64] usb: dwc3: import from kernel v4.3
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (4 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 05/64] usb: dwc3: import from kernel v4.2 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 07/64] usb: dwc3: import from kernel v4.4 Jens Wiklander
` (61 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.2 to v4.3.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.2..v4.3
Commits imported:
a66c275b3d5d usb: dwc3: gadget: Fix BUG in RT config
e2ae0692bf6f usb: dwc3: omap: enable irqs lately
a474d3b73ba7 usb: dwc3: gadget: add ep capabilities support
c0bd5456a470 usb: dwc3: ep0: handle non maxpacket aligned transfers > 512
2abd9d5fa60f usb: dwc3: ep0: Add chained TRB support
368ca113ca0a usb: dwc3; ep0: Modify _dwc3_ep0_start_trans_ API to take 'chain' parameter
8a3442205630 usb: dwc3: ep0: preparation for handling non maxpacket aligned transfers > 512
2e5464da4e7d usb: dwc3: ep0: use _roundup_ to calculate the transfer size
b2fb5b1a0f50 usb: dwc3: ep0: Fix mem corruption on OUT transfers of more than 512 bytes
aa7399744dd0 usb: dwc3: gadget: defer endpoint name change
7eaeac5c0e44 usb: dwc3: gadget: add a trace when disabling EPs
d9972f470bcb usb: dwc3: core: remove unnecessary dev_warn()
2df033ca39b5 usb: dwc3: pci: make better use of gpiod API
5960387a2fb8 usb: dwc3: omap: Replace deprecated API of extcon
9fcfa463e17a usb: dwc3: drop CONFIG_USB_DWC3_DEBUG
e746b06cc76a usb: dwc3: st: remove two unnecessary messages
2babd0d148a3 usb: dwc3: qcom: switch dev_dbg() to dev_info()
42f69a02e7ab usb: dwc3: exynos: switch dev_dbg() to dev_info()
9ff0fdca3b62 usb: dwc3: keystone: convert dev_dbg() to dev_err()
e4f756675467 usb: dwc3: omap: drop dev_dbg() usage
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 2 -
drivers/usb/dwc3/ep0.c | 92 ++++++++++++++++++++++++++++-----------
drivers/usb/dwc3/gadget.c | 61 +++++++++++++++-----------
3 files changed, 101 insertions(+), 54 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index ff5773c66b84..064123e44566 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -455,8 +455,6 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
} else {
- dev_warn(dwc->dev, "HSPHY Interface not defined\n");
-
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
break;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 69e769c35cf5..5320e939e090 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -56,7 +56,7 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
}
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type)
+ u32 len, u32 type, bool chain)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
@@ -70,7 +70,10 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
return 0;
}
- trb = dwc->ep0_trb;
+ trb = &dwc->ep0_trb[dep->free_slot];
+
+ if (chain)
+ dep->free_slot++;
trb->bpl = lower_32_bits(buf_dma);
trb->bph = upper_32_bits(buf_dma);
@@ -78,10 +81,17 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trb->ctrl = type;
trb->ctrl |= (DWC3_TRB_CTRL_HWO
- | DWC3_TRB_CTRL_LST
- | DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_ISP_IMI);
+ if (chain)
+ trb->ctrl |= DWC3_TRB_CTRL_CHN;
+ else
+ trb->ctrl |= (DWC3_TRB_CTRL_IOC
+ | DWC3_TRB_CTRL_LST);
+
+ if (chain)
+ return 0;
+
memset(¶ms, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
@@ -302,7 +312,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
int ret;
ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
- DWC3_TRBCTL_CONTROL_SETUP);
+ DWC3_TRBCTL_CONTROL_SETUP, false);
WARN_ON(ret < 0);
}
@@ -783,7 +793,11 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
- u32 transferred;
+ unsigned transfer_size = 0;
+ unsigned maxp;
+ unsigned remaining_ur_length;
+ void *buf;
+ u32 transferred = 0;
u32 status;
u32 length;
u8 epnum;
@@ -812,17 +826,37 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
}
ur = &r->request;
+ buf = ur->buf;
+ remaining_ur_length = ur->length;
length = trb->size & DWC3_TRB_SIZE_MASK;
+ maxp = ep0->endpoint.maxpacket;
+
if (dwc->ep0_bounced) {
- unsigned transfer_size = ur->length;
- unsigned maxp = ep0->endpoint.maxpacket;
+ /*
+ * Handle the first TRB before handling the bounce buffer if
+ * the request length is greater than the bounce buffer size
+ */
+ if (ur->length > DWC3_EP0_BOUNCE_SIZE) {
+ transfer_size = ALIGN(ur->length - maxp, maxp);
+ transferred = transfer_size - length;
+ buf = (u8 *)buf + transferred;
+ ur->actual += transferred;
+ remaining_ur_length -= transferred;
+
+ trb++;
+ length = trb->size & DWC3_TRB_SIZE_MASK;
- transfer_size += (maxp - (transfer_size % maxp));
- transferred = min_t(u32, ur->length,
- transfer_size - length);
- memcpy(ur->buf, dwc->ep0_bounce, transferred);
+ ep0->free_slot = 0;
+ }
+
+ transfer_size = roundup((ur->length - transfer_size),
+ maxp);
+
+ transferred = min_t(u32, remaining_ur_length,
+ transfer_size - length);
+ memcpy(buf, dwc->ep0_bounce, transferred);
} else {
transferred = ur->length - length;
}
@@ -844,7 +878,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dwc, epnum,
dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA);
+ DWC3_TRBCTL_CONTROL_DATA, false);
WARN_ON(ret < 0);
}
}
@@ -928,10 +962,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
if (req->request.length == 0) {
ret = dwc3_ep0_start_trans(dwc, dep->number,
dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA);
+ DWC3_TRBCTL_CONTROL_DATA, false);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
- u32 transfer_size;
+ u32 transfer_size = 0;
u32 maxpacket;
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
@@ -941,21 +975,26 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
}
- WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
-
maxpacket = dep->endpoint.maxpacket;
- transfer_size = roundup(req->request.length, maxpacket);
+
+ if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
+ transfer_size = ALIGN(req->request.length - maxpacket,
+ maxpacket);
+ ret = dwc3_ep0_start_trans(dwc, dep->number,
+ req->request.dma,
+ transfer_size,
+ DWC3_TRBCTL_CONTROL_DATA,
+ true);
+ }
+
+ transfer_size = roundup((req->request.length - transfer_size),
+ maxpacket);
dwc->ep0_bounced = true;
- /*
- * REVISIT in case request length is bigger than
- * DWC3_EP0_BOUNCE_SIZE we will need two chained
- * TRBs to handle the transfer.
- */
ret = dwc3_ep0_start_trans(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
- DWC3_TRBCTL_CONTROL_DATA);
+ DWC3_TRBCTL_CONTROL_DATA, false);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
@@ -965,7 +1004,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
}
ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
- req->request.length, DWC3_TRBCTL_CONTROL_DATA);
+ req->request.length, DWC3_TRBCTL_CONTROL_DATA,
+ false);
}
WARN_ON(ret < 0);
@@ -980,7 +1020,7 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
: DWC3_TRBCTL_CONTROL_STATUS2;
return dwc3_ep0_start_trans(dwc, dep->number,
- dwc->ctrl_req_addr, 0, type);
+ dwc->ctrl_req_addr, 0, type, false);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 333a7c0078fc..1e8bdf817811 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -547,6 +547,23 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
}
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ strlcat(dep->name, "-control", sizeof(dep->name));
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ strlcat(dep->name, "-isoc", sizeof(dep->name));
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ strlcat(dep->name, "-bulk", sizeof(dep->name));
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ strlcat(dep->name, "-int", sizeof(dep->name));
+ break;
+ default:
+ dev_err(dwc->dev, "invalid endpoint transfer type\n");
+ }
+
return 0;
}
@@ -586,6 +603,8 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
struct dwc3 *dwc = dep->dwc;
u32 reg;
+ dwc3_trace(trace_dwc3_gadget, "Disabling %s", dep->name);
+
dwc3_remove_requests(dwc, dep);
/* make sure HW endpoint isn't stalled */
@@ -602,6 +621,10 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->type = 0;
dep->flags = 0;
+ snprintf(dep->name, sizeof(dep->name), "ep%d%s",
+ dep->number >> 1,
+ (dep->number & 1) ? "in" : "out");
+
return 0;
}
@@ -647,23 +670,6 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return 0;
}
- switch (usb_endpoint_type(desc)) {
- case USB_ENDPOINT_XFER_CONTROL:
- strlcat(dep->name, "-control", sizeof(dep->name));
- break;
- case USB_ENDPOINT_XFER_ISOC:
- strlcat(dep->name, "-isoc", sizeof(dep->name));
- break;
- case USB_ENDPOINT_XFER_BULK:
- strlcat(dep->name, "-bulk", sizeof(dep->name));
- break;
- case USB_ENDPOINT_XFER_INT:
- strlcat(dep->name, "-int", sizeof(dep->name));
- break;
- default:
- dev_err(dwc->dev, "invalid endpoint transfer type\n");
- }
-
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -692,10 +698,6 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
return 0;
}
- snprintf(dep->name, sizeof(dep->name), "ep%d%s",
- dep->number >> 1,
- (dep->number & 1) ? "in" : "out");
-
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_disable(dep);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1713,6 +1715,17 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
return ret;
}
+ if (epnum == 0 || epnum == 1) {
+ dep->endpoint.caps.type_control = true;
+ } else {
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
+ }
+
+ dep->endpoint.caps.dir_in = !!direction;
+ dep->endpoint.caps.dir_out = !direction;
+
INIT_LIST_HEAD(&dep->request_list);
INIT_LIST_HEAD(&dep->req_queued);
}
@@ -2652,8 +2665,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
int i;
irqreturn_t ret = IRQ_NONE;
- spin_lock(&dwc->lock);
-
for (i = 0; i < dwc->num_event_buffers; i++) {
irqreturn_t status;
@@ -2662,8 +2673,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
ret = status;
}
- spin_unlock(&dwc->lock);
-
return ret;
}
@@ -2685,7 +2694,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err0;
}
- dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
&dwc->ep0_trb_addr, GFP_KERNEL);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 07/64] usb: dwc3: import from kernel v4.4
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (5 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 06/64] usb: dwc3: import from kernel v4.3 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 08/64] usb: dwc3: import from kernel v4.5 Jens Wiklander
` (60 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.3 to v4.4.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.3..v4.4
Commits imported:
62e345ae5b6e usb: dwc3: gadget: don't prestart interrupt endpoints
b9e51b2b1fda usb: dwc3: gadget: let us set lower max_speed
b4c580a43d52 usb: dwc3: pci: add support for Intel Broxton SOC
d115d7050a0d Revert "usb: dwc3: gadget: drop unnecessary loop when cleaning up TRBs"
e5f68b4a3e7b Revert "usb: dwc3: gadget: remove unnecessary _irqsave()"
94218ee31ba5 usb: dwc3: pci: Set enblslpm quirk for Synopsys platforms
ec791d149bca usb: dwc3: Add dis_enblslpm_quirk
bb7f3d6d323a usb: dwc3: pci: Add platform data for Synopsys HAPS
9a5a0783e491 usb: dwc3: pci: trivial: Formatting
690fb3718a70 usb: dwc3: Support Synopsys USB 3.1 IP
e8095a25364a usb: dwc3: pci: Add the PCI Product ID for Synopsys USB 3.1
41adc59caece usb: dwc3: pci: Add the Synopsys HAPS AXI Product ID
70f3a9caa116 usb: dwc3: gadget: remove unnecessary _irqsave()
e6e709b7ab89 usb: dwc3: gadget: use Update Transfer from Xfer In Progress
6bb4fe12ea08 usb: dwc3: gadget: use update transfer command
8a1a9c9e4503 usb: dwc3: gadget: start transfer on XFER_COMPLETE
9caeb06ebd46 usb: dwc3: pci: passing forward the ACPI companion
3d128919b7e5 usb: dwc3: core: convert to unified device property interface
06e7114f0d82 usb: common: of_usb_get_dr_mode to usb_get_dr_mode
666472733b8c usb: dwc3: st: prepare the driver for generic usb_get_dr_mode function
63863b988eec usb: common: of_usb_get_maximum_speed to usb_get_maximum_speed
6344475f5397 usb: dwc3: support for pinctrl state change during system sleep
db2be4e9e30c usb: dwc3: Add frame length adjustment quirk
a8f32817eeb0 usb: dwc3: gadget: improve ep_queue's error reporting
89185916d229 usb: dwc3: gadget: clear DWC3_PENDING_REQUEST when request is queued
1d6a39186b37 usb: dwc3: gadget: start requests as soon as they come
fe84f522ed6d usb: dwc3: gadget: move trace_dwc3_ep_queue()
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 104 ++++++++++++++++++-------
drivers/usb/dwc3/core.h | 27 +++++++
drivers/usb/dwc3/gadget.c | 125 +++++++++++++++++++++----------
drivers/usb/dwc3/platform_data.h | 3 +
4 files changed, 194 insertions(+), 65 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 064123e44566..22b4797383cd 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -34,6 +34,7 @@
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/acpi.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -143,6 +144,32 @@ static int dwc3_soft_reset(struct dwc3 *dwc)
return 0;
}
+/*
+ * dwc3_frame_length_adjustment - Adjusts frame length if required
+ * @dwc3: Pointer to our controller context structure
+ * @fladj: Value of GFLADJ_30MHZ to adjust frame length
+ */
+static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
+{
+ u32 reg;
+ u32 dft;
+
+ if (dwc->revision < DWC3_REVISION_250A)
+ return;
+
+ if (fladj == 0)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ dft = reg & DWC3_GFLADJ_30MHZ_MASK;
+ if (!dev_WARN_ONCE(dwc->dev, dft == fladj,
+ "request value same as default, ignoring\n")) {
+ reg &= ~DWC3_GFLADJ_30MHZ_MASK;
+ reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj;
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ }
+}
+
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
@@ -488,6 +515,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ if (dwc->dis_enblslpm_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0;
@@ -507,12 +537,18 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
/* This should read as U3 followed by revision number */
- if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) {
+ if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
+ /* Detected DWC_usb3 IP */
+ dwc->revision = reg;
+ } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
+ /* Detected DWC_usb31 IP */
+ dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
+ dwc->revision |= DWC3_REVISION_IS_DWC31;
+ } else {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
goto err0;
}
- dwc->revision = reg;
/*
* Write Linux Version Code to our GUID register so it's easy to figure
@@ -773,12 +809,12 @@ static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3_platform_data *pdata = dev_get_platdata(dev);
- struct device_node *node = dev->of_node;
struct resource *res;
struct dwc3 *dwc;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
+ u32 fladj = 0;
int ret;
@@ -842,51 +878,56 @@ static int dwc3_probe(struct platform_device *pdev)
*/
hird_threshold = 12;
- if (node) {
- dwc->maximum_speed = of_usb_get_maximum_speed(node);
- dwc->has_lpm_erratum = of_property_read_bool(node,
+ dwc->maximum_speed = usb_get_maximum_speed(dev);
+ dwc->dr_mode = usb_get_dr_mode(dev);
+
+ dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
- of_property_read_u8(node, "snps,lpm-nyet-threshold",
+ device_property_read_u8(dev, "snps,lpm-nyet-threshold",
&lpm_nyet_threshold);
- dwc->is_utmi_l1_suspend = of_property_read_bool(node,
+ dwc->is_utmi_l1_suspend = device_property_read_bool(dev,
"snps,is-utmi-l1-suspend");
- of_property_read_u8(node, "snps,hird-threshold",
+ device_property_read_u8(dev, "snps,hird-threshold",
&hird_threshold);
- dwc->usb3_lpm_capable = of_property_read_bool(node,
+ dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
- dwc->needs_fifo_resize = of_property_read_bool(node,
+ dwc->needs_fifo_resize = device_property_read_bool(dev,
"tx-fifo-resize");
- dwc->dr_mode = of_usb_get_dr_mode(node);
- dwc->disable_scramble_quirk = of_property_read_bool(node,
+ dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
- dwc->u2exit_lfps_quirk = of_property_read_bool(node,
+ dwc->u2exit_lfps_quirk = device_property_read_bool(dev,
"snps,u2exit_lfps_quirk");
- dwc->u2ss_inp3_quirk = of_property_read_bool(node,
+ dwc->u2ss_inp3_quirk = device_property_read_bool(dev,
"snps,u2ss_inp3_quirk");
- dwc->req_p1p2p3_quirk = of_property_read_bool(node,
+ dwc->req_p1p2p3_quirk = device_property_read_bool(dev,
"snps,req_p1p2p3_quirk");
- dwc->del_p1p2p3_quirk = of_property_read_bool(node,
+ dwc->del_p1p2p3_quirk = device_property_read_bool(dev,
"snps,del_p1p2p3_quirk");
- dwc->del_phy_power_chg_quirk = of_property_read_bool(node,
+ dwc->del_phy_power_chg_quirk = device_property_read_bool(dev,
"snps,del_phy_power_chg_quirk");
- dwc->lfps_filter_quirk = of_property_read_bool(node,
+ dwc->lfps_filter_quirk = device_property_read_bool(dev,
"snps,lfps_filter_quirk");
- dwc->rx_detect_poll_quirk = of_property_read_bool(node,
+ dwc->rx_detect_poll_quirk = device_property_read_bool(dev,
"snps,rx_detect_poll_quirk");
- dwc->dis_u3_susphy_quirk = of_property_read_bool(node,
+ dwc->dis_u3_susphy_quirk = device_property_read_bool(dev,
"snps,dis_u3_susphy_quirk");
- dwc->dis_u2_susphy_quirk = of_property_read_bool(node,
+ dwc->dis_u2_susphy_quirk = device_property_read_bool(dev,
"snps,dis_u2_susphy_quirk");
+ dwc->dis_enblslpm_quirk = device_property_read_bool(dev,
+ "snps,dis_enblslpm_quirk");
- dwc->tx_de_emphasis_quirk = of_property_read_bool(node,
+ dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
- of_property_read_u8(node, "snps,tx_de_emphasis",
+ device_property_read_u8(dev, "snps,tx_de_emphasis",
&tx_de_emphasis);
- of_property_read_string(node, "snps,hsphy_interface",
- &dwc->hsphy_interface);
- } else if (pdata) {
+ device_property_read_string(dev, "snps,hsphy_interface",
+ &dwc->hsphy_interface);
+ device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
+ &fladj);
+
+ if (pdata) {
dwc->maximum_speed = pdata->maximum_speed;
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
if (pdata->lpm_nyet_threshold)
@@ -909,12 +950,14 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
+ dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk;
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
if (pdata->tx_de_emphasis)
tx_de_emphasis = pdata->tx_de_emphasis;
dwc->hsphy_interface = pdata->hsphy_interface;
+ fladj = pdata->fladj_value;
}
/* default to superspeed if no maximum_speed passed */
@@ -971,6 +1014,9 @@ static int dwc3_probe(struct platform_device *pdev)
goto err1;
}
+ /* Adjust Frame Length */
+ dwc3_frame_length_adjustment(dwc, fladj);
+
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
@@ -1091,6 +1137,8 @@ static int dwc3_suspend(struct device *dev)
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
@@ -1100,6 +1148,8 @@ static int dwc3_resume(struct device *dev)
unsigned long flags;
int ret;
+ pinctrl_pm_select_default_state(dev);
+
usb_phy_init(dwc->usb3_phy);
usb_phy_init(dwc->usb2_phy);
ret = phy_init(dwc->usb2_generic_phy);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 044778884585..36f1cb74588c 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -108,6 +108,9 @@
#define DWC3_GPRTBIMAP_FS0 0xc188
#define DWC3_GPRTBIMAP_FS1 0xc18c
+#define DWC3_VER_NUMBER 0xc1a0
+#define DWC3_VER_TYPE 0xc1a4
+
#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04))
@@ -124,6 +127,7 @@
#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
#define DWC3_GHWPARAMS8 0xc600
+#define DWC3_GFLADJ 0xc630
/* Device Registers */
#define DWC3_DCFG 0xc700
@@ -175,6 +179,7 @@
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
+#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
@@ -234,6 +239,10 @@
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
+/* Global Frame Length Adjustment Register */
+#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
+#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -712,6 +721,8 @@ struct dwc3_scratchpad_array {
* @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk
* @dis_u3_susphy_quirk: set if we disable usb3 suspend phy
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
+ * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
+ * disabling the suspend signal to the PHY.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -766,6 +777,14 @@ struct dwc3 {
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
+
+ /*
+ * All 3.1 IP version constants are greater than the 3.0 IP
+ * version constants. This works for most version checks in
+ * dwc3. However, in the future, this may not apply as
+ * features may be developed on newer versions of the 3.0 IP
+ * that are not in the 3.1 IP.
+ */
u32 revision;
#define DWC3_REVISION_173A 0x5533173a
@@ -788,6 +807,13 @@ struct dwc3 {
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+/*
+ * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
+ * just so dwc31 revisions are always larger than dwc3.
+ */
+#define DWC3_REVISION_IS_DWC31 0x80000000
+#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31)
+
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
enum dwc3_link_state link_state;
@@ -841,6 +867,7 @@ struct dwc3 {
unsigned rx_detect_poll_quirk:1;
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
+ unsigned dis_enblslpm_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1e8bdf817811..a58376fd65fe 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -948,7 +948,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name);
return -EBUSY;
}
- dep->flags &= ~DWC3_EP_PENDING_REQUEST;
/*
* If we are getting here after a short-out-packet we don't enqueue any
@@ -1050,6 +1049,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
req->direction = dep->direction;
req->epnum = dep->number;
+ trace_dwc3_ep_queue(req);
+
/*
* We only add to our list of requests now and
* start consuming the list once we get XferNotReady
@@ -1069,6 +1070,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->request_list);
+ /*
+ * If there are no pending requests and the endpoint isn't already
+ * busy, we will just start the request straight away.
+ *
+ * This will save one IRQ (XFER_NOT_READY) and possibly make it a
+ * little bit faster.
+ */
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ !usb_endpoint_xfer_int(dep->endpoint.desc) &&
+ !(dep->flags & DWC3_EP_BUSY)) {
+ ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ goto out;
+ }
+
/*
* There are a few special cases:
*
@@ -1096,10 +1111,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
}
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- if (ret && ret != -EBUSY)
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
- dep->name);
- return ret;
+ if (!ret)
+ dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+
+ goto out;
}
/*
@@ -1113,10 +1128,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
WARN_ON_ONCE(!dep->resource_index);
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
false);
- if (ret && ret != -EBUSY)
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
- dep->name);
- return ret;
+ goto out;
}
/*
@@ -1124,14 +1136,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* right away, otherwise host will not know we have streams to be
* handled.
*/
- if (dep->stream_capable) {
+ if (dep->stream_capable)
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- if (ret && ret != -EBUSY)
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
- dep->name);
- }
- return 0;
+out:
+ if (ret && ret != -EBUSY)
+ dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dep->name);
+ if (ret == -EBUSY)
+ ret = 0;
+
+ return ret;
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
@@ -1159,8 +1174,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
- trace_dwc3_ep_queue(req);
-
ret = __dwc3_gadget_ep_queue(dep, req);
out:
@@ -1872,27 +1885,32 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int i;
int ret;
- req = next_request(&dep->req_queued);
- if (!req) {
- WARN_ON_ONCE(1);
- return 1;
- }
- i = 0;
do {
- slot = req->start_slot + i;
- if ((slot == DWC3_TRB_NUM - 1) &&
+ req = next_request(&dep->req_queued);
+ if (!req) {
+ WARN_ON_ONCE(1);
+ return 1;
+ }
+ i = 0;
+ do {
+ slot = req->start_slot + i;
+ if ((slot == DWC3_TRB_NUM - 1) &&
usb_endpoint_xfer_isoc(dep->endpoint.desc))
- slot++;
- slot %= DWC3_TRB_NUM;
- trb = &dep->trb_pool[slot];
+ slot++;
+ slot %= DWC3_TRB_NUM;
+ trb = &dep->trb_pool[slot];
+
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status);
+ if (ret)
+ break;
+ } while (++i < req->request.num_mapped_sgs);
+
+ dwc3_gadget_giveback(dep, req, status);
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status);
if (ret)
break;
- } while (++i < req->request.num_mapped_sgs);
-
- dwc3_gadget_giveback(dep, req, status);
+ } while (1);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) {
@@ -1955,6 +1973,14 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dwc->u1u2 = 0;
}
+
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ int ret;
+
+ ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete);
+ if (!ret || ret == -EBUSY)
+ return;
+ }
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -1992,15 +2018,16 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
+ int active;
int ret;
+ active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
+
dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
- dep->name, event->status &
- DEPEVT_STATUS_TRANSFER_ACTIVE
- ? "Transfer Active"
+ dep->name, active ? "Transfer Active"
: "Transfer Not Active");
- ret = __dwc3_gadget_kick_transfer(dep, 0, 1);
+ ret = __dwc3_gadget_kick_transfer(dep, 0, !active);
if (!ret || ret == -EBUSY)
return;
@@ -2718,11 +2745,33 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.max_speed = USB_SPEED_SUPER;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
+ /*
+ * FIXME We might be setting max_speed to <SUPER, however versions
+ * <2.20a of dwc3 have an issue with metastability (documented
+ * elsewhere in this driver) which tells us we can't set max speed to
+ * anything lower than SUPER.
+ *
+ * Because gadget.max_speed is only used by composite.c and function
+ * drivers (i.e. it won't go into dwc3's registers) we are allowing this
+ * to happen so we avoid sending SuperSpeed Capability descriptor
+ * together with our BOS descriptor as that could confuse host into
+ * thinking we can handle super speed.
+ *
+ * Note that, in fact, we won't even support GetBOS requests when speed
+ * is less than super speed because we don't have means, yet, to tell
+ * composite.c that we are USB 2.0 + LPM ECN.
+ */
+ if (dwc->revision < DWC3_REVISION_220A)
+ dwc3_trace(trace_dwc3_gadget,
+ "Changing max_speed on rev %08x\n",
+ dwc->revision);
+
+ dwc->gadget.max_speed = dwc->maximum_speed;
+
/*
* Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
* on ep out.
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
index d3614ecbb9ca..2bb4d3ad0e6b 100644
--- a/drivers/usb/dwc3/platform_data.h
+++ b/drivers/usb/dwc3/platform_data.h
@@ -42,9 +42,12 @@ struct dwc3_platform_data {
unsigned rx_detect_poll_quirk:1;
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
+ unsigned dis_enblslpm_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
+ u32 fladj_value;
+
const char *hsphy_interface;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 08/64] usb: dwc3: import from kernel v4.5
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (6 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 07/64] usb: dwc3: import from kernel v4.4 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 09/64] usb: dwc3: import from kernel v4.6 Jens Wiklander
` (59 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.4 to v4.5.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.4..v4.5
Commits imported:
c450960187f4 usb: dwc3: Fix assignment of EP transfer resources
6a4290cc28be usb: dwc3: gadget: set the OTG flag in dwc3 gadget driver.
5072cfc40a80 usb: dwc3: of-simple: fix build warning on !PM
d9261898a4b2 usb: dwc3: gadget: don't send extra ZLP
95ca961c758c usb: dwc3: gadget: pass a condition to dev_WARN_ONCE()
46a01427e969 usb: dwc3: trace: show request flags
04c03d10e507 usb: dwc3: gadget: handle request->zero
b084662776be usb: dwc3: remove dwc3-qcom in favor of dwc3-of-simple
16adc674d0d6 usb: dwc3: add generic OF glue layer
b5d335e5ea6a usb: dwc3: ep0: fix setup_packet_pending initialization
ac7bdcc1b3ad usb: dwc3: gadget: simplify next_request() return check
acc38c4970ca usb: dwc3: ep0: purge dev_dbg() calls
1407bf13e3bf usb: dwc3: core: purge dev_dbg() calls
ec5e795cdefb usb: dwc3: gadget: purge dev_dbg() calls
bb423984c28e usb: dwc3: gadget: simplify dwc3_gadget_ep_queue()
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 8 +-
drivers/usb/dwc3/core.h | 4 +-
drivers/usb/dwc3/ep0.c | 17 ++--
drivers/usb/dwc3/gadget.c | 204 ++++++++++++++++++++++++++------------
4 files changed, 157 insertions(+), 76 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 22b4797383cd..de5e01f41bc2 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -272,7 +272,8 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
for (n = 0; n < dwc->num_event_buffers; n++) {
evt = dwc->ev_buffs[n];
- dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
+ dwc3_trace(trace_dwc3_core,
+ "Event buf %p dma %08llx length %d\n",
evt->buf, (unsigned long long) evt->dma,
evt->length);
@@ -608,12 +609,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
- dev_dbg(dwc->dev, "No power optimization available\n");
+ dwc3_trace(trace_dwc3_core, "No power optimization available\n");
}
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dev_dbg(dwc->dev, "it is on FPGA board\n");
+ dwc3_trace(trace_dwc3_core,
+ "running on FPGA platform\n");
dwc->is_fpga = true;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 36f1cb74588c..e4f8b90d9627 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -37,6 +37,7 @@
#define DWC3_MSG_MAX 500
/* Global constants */
+#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -647,6 +648,7 @@ struct dwc3_scratchpad_array {
* @ctrl_req: usb control request which is used for ep0
* @ep0_trb: trb which is used for the ctrl_req
* @ep0_bounce: bounce buffer for ep0
+ * @zlp_buf: used when request->zero is set
* @setup_buf: used while precessing STD USB requests
* @ctrl_req_addr: dma address of ctrl_req
* @ep0_trb: dma address of ep0_trb
@@ -734,6 +736,7 @@ struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
void *ep0_bounce;
+ void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
@@ -853,7 +856,6 @@ struct dwc3 {
unsigned pullups_connected:1;
unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
- unsigned start_config_issued:1;
unsigned three_stage_setup:1;
unsigned usb3_lpm_capable:1;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 5320e939e090..8d6b75c2f53b 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -555,7 +555,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
int ret;
u32 reg;
- dwc->start_config_issued = false;
cfg = le16_to_cpu(ctrl->wValue);
switch (state) {
@@ -737,10 +736,6 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
- case USB_REQ_SET_INTERFACE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_INTERFACE");
- dwc->start_config_issued = false;
- /* Fall through */
default:
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
@@ -817,6 +812,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dwc->setup_packet_pending = true;
+
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
if (r)
@@ -916,8 +913,10 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
}
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (status == DWC3_TRBSTS_SETUP_PENDING)
+ if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dwc->setup_packet_pending = true;
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
+ }
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
@@ -971,7 +970,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
if (ret) {
- dev_dbg(dwc->dev, "failed to map request\n");
+ dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
return;
}
@@ -999,7 +998,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
if (ret) {
- dev_dbg(dwc->dev, "failed to map request\n");
+ dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
return;
}
@@ -1063,8 +1062,6 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- dwc->setup_packet_pending = true;
-
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
dwc3_trace(trace_dwc3_ep0, "Control Data");
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a58376fd65fe..2363bad45af8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -265,9 +265,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_unmap_request(&dwc->gadget, &req->request,
req->direction);
- dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
- req, dep->name, req->request.actual,
- req->request.length, status);
trace_dwc3_gadget_giveback(req);
spin_unlock(&dwc->lock);
@@ -388,24 +385,66 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
dep->trb_pool_dma = 0;
}
+static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
+
+/**
+ * dwc3_gadget_start_config - Configure EP resources
+ * @dwc: pointer to our controller context structure
+ * @dep: endpoint that is being enabled
+ *
+ * The assignment of transfer resources cannot perfectly follow the
+ * data book due to the fact that the controller driver does not have
+ * all knowledge of the configuration in advance. It is given this
+ * information piecemeal by the composite gadget framework after every
+ * SET_CONFIGURATION and SET_INTERFACE. Trying to follow the databook
+ * programming model in this scenario can cause errors. For two
+ * reasons:
+ *
+ * 1) The databook says to do DEPSTARTCFG for every SET_CONFIGURATION
+ * and SET_INTERFACE (8.1.5). This is incorrect in the scenario of
+ * multiple interfaces.
+ *
+ * 2) The databook does not mention doing more DEPXFERCFG for new
+ * endpoint on alt setting (8.1.6).
+ *
+ * The following simplified method is used instead:
+ *
+ * All hardware endpoints can be assigned a transfer resource and this
+ * setting will stay persistent until either a core reset or
+ * hibernation. So whenever we do a DEPSTARTCFG(0) we can go ahead and
+ * do DEPXFERCFG for every hardware endpoint as well. We are
+ * guaranteed that there are as many transfer resources as endpoints.
+ *
+ * This function is called for each endpoint when it is being enabled
+ * but is triggered only when called for EP0-out, which always happens
+ * first, and which should only happen in one of the above conditions.
+ */
static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
+ int i;
+ int ret;
+
+ if (dep->number)
+ return 0;
memset(¶ms, 0x00, sizeof(params));
+ cmd = DWC3_DEPCMD_DEPSTARTCFG;
- if (dep->number != 1) {
- cmd = DWC3_DEPCMD_DEPSTARTCFG;
- /* XferRscIdx == 0 for ep0 and 2 for the remaining */
- if (dep->number > 1) {
- if (dwc->start_config_issued)
- return 0;
- dwc->start_config_issued = true;
- cmd |= DWC3_DEPCMD_PARAM(2);
- }
+ ret = dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms);
+ if (ret)
+ return ret;
- return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms);
+ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
+ struct dwc3_ep *dep = dwc->eps[i];
+
+ if (!dep)
+ continue;
+
+ ret = dwc3_gadget_set_xfer_resource(dwc, dep);
+ if (ret)
+ return ret;
}
return 0;
@@ -519,10 +558,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
struct dwc3_trb *trb_st_hw;
struct dwc3_trb *trb_link;
- ret = dwc3_gadget_set_xfer_resource(dwc, dep);
- if (ret)
- return ret;
-
dep->endpoint.desc = desc;
dep->comp_desc = comp_desc;
dep->type = usb_endpoint_type(desc);
@@ -664,11 +699,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (dep->flags & DWC3_EP_ENABLED) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
- dep->name);
+ if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
+ "%s is already enabled\n",
+ dep->name))
return 0;
- }
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
@@ -692,11 +726,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (!(dep->flags & DWC3_EP_ENABLED)) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
- dep->name);
+ if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
+ "%s is already disabled\n",
+ dep->name))
return 0;
- }
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_disable(dep);
@@ -985,8 +1018,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
cmd |= DWC3_DEPCMD_PARAM(cmd_param);
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
if (ret < 0) {
- dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
-
/*
* FIXME we need to iterate over the list of requests
* here and stop, unmap, free and del each of the linked
@@ -1044,6 +1075,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
struct dwc3 *dwc = dep->dwc;
int ret;
+ if (!dep->endpoint.desc) {
+ dwc3_trace(trace_dwc3_gadget,
+ "trying to queue request %p to disabled %s\n",
+ &req->request, dep->endpoint.name);
+ return -ESHUTDOWN;
+ }
+
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
+ &req->request, req->dep->name)) {
+ dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n",
+ &req->request, req->dep->name);
+ return -EINVAL;
+ }
+
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->direction = dep->direction;
@@ -1141,7 +1186,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
out:
if (ret && ret != -EBUSY)
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: failed to kick transfers\n",
dep->name);
if (ret == -EBUSY)
ret = 0;
@@ -1149,6 +1195,32 @@ out:
return ret;
}
+static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ dwc3_gadget_ep_free_request(ep, request);
+}
+
+static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct usb_request *request;
+ struct usb_ep *ep = &dep->endpoint;
+
+ dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
+ request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!request)
+ return -ENOMEM;
+
+ request->length = 0;
+ request->buf = dwc->zlp_buf;
+ request->complete = __dwc3_gadget_ep_zlp_complete;
+
+ req = to_dwc3_request(request);
+
+ return __dwc3_gadget_ep_queue(dep, req);
+}
+
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
@@ -1161,22 +1233,18 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc) {
- dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
- request, ep->name);
- ret = -ESHUTDOWN;
- goto out;
- }
-
- if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
- request, req->dep->name)) {
- ret = -EINVAL;
- goto out;
- }
-
ret = __dwc3_gadget_ep_queue(dep, req);
-out:
+ /*
+ * Okay, here's the thing, if gadget driver has requested for a ZLP by
+ * setting request->zero, instead of doing magic, we will just queue an
+ * extra usb_request ourselves so that it gets handled the same way as
+ * any other request.
+ */
+ if (ret == 0 && request->zero && request->length &&
+ (request->length % ep->maxpacket == 0))
+ ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
+
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1246,7 +1314,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
(!list_empty(&dep->req_queued) ||
!list_empty(&dep->request_list)))) {
- dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: pending request, cannot halt\n",
dep->name);
return -EAGAIN;
}
@@ -1373,7 +1442,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
speed = reg & DWC3_DSTS_CONNECTSPD;
if (speed == DWC3_DSTS_SUPERSPEED) {
- dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
+ dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
ret = -EINVAL;
goto out;
}
@@ -1385,8 +1454,9 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
break;
default:
- dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
- link_state);
+ dwc3_trace(trace_dwc3_gadget,
+ "can't wakeup from '%s'\n",
+ dwc3_gadget_link_string(link_state));
ret = -EINVAL;
goto out;
}
@@ -1604,8 +1674,6 @@ static int dwc3_gadget_start(struct usb_gadget *g,
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
- dwc->start_config_issued = false;
-
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -1825,7 +1893,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (count) {
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
- dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: incomplete IN transfer\n",
dep->name);
/*
* If missed isoc occurred and there is
@@ -1887,10 +1956,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
do {
req = next_request(&dep->req_queued);
- if (!req) {
- WARN_ON_ONCE(1);
+ if (WARN_ON_ONCE(!req))
return 1;
- }
+
i = 0;
do {
slot = req->start_slot + i;
@@ -2004,7 +2072,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->resource_index = 0;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s is an Isochronous endpoint\n",
dep->name);
return;
}
@@ -2031,7 +2100,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (!ret || ret == -EBUSY)
return;
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: failed to kick transfers\n",
dep->name);
}
@@ -2053,11 +2123,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
case DEPEVT_STREAMEVT_NOTFOUND:
/* FALLTHROUGH */
default:
- dev_dbg(dwc->dev, "Couldn't find suitable stream\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "unable to find suitable stream\n");
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
@@ -2202,7 +2273,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
dwc3_disconnect_gadget(dwc);
- dwc->start_config_issued = false;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
@@ -2230,8 +2300,8 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
*
* Our suggested workaround is to follow the Disconnect
* Event steps here, instead, based on a setup_packet_pending
- * flag. Such flag gets set whenever we have a XferNotReady
- * event on EP0 and gets cleared on XferComplete for the
+ * flag. Such flag gets set whenever we have a SETUP_PENDING
+ * status for EP0 TRBs and gets cleared on XferComplete for the
* same endpoint.
*
* Refers to:
@@ -2253,7 +2323,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_stop_active_transfers(dwc);
dwc3_clear_stall_all_ep(dwc);
- dwc->start_config_issued = false;
/* Reset device address to zero */
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -2744,10 +2813,17 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}
+ dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
+ if (!dwc->zlp_buf) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
+ dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -2785,16 +2861,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err4;
+ goto err5;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err4;
+ goto err5;
}
return 0;
+err5:
+ kfree(dwc->zlp_buf);
+
err4:
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
@@ -2827,6 +2906,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
+ kfree(dwc->zlp_buf);
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
dwc->ep0_trb, dwc->ep0_trb_addr);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 09/64] usb: dwc3: import from kernel v4.6
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (7 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 08/64] usb: dwc3: import from kernel v4.5 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 10/64] usb: dwc3: import from kernel v4.7 Jens Wiklander
` (58 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.5 to v4.6.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.5..v4.6
Commits imported:
9772b47a4c29 usb: dwc3: gadget: Fix suspend/resume during device mode
e6bdf8195b4a usb: dwc3: fix memory leak of dwc->regset
5c4ad318de3b usb: dwc3: core: fix PHY handling during suspend
45d49cb706e5 usb: dwc3: omap: fix up error path on probe()
adf9a3ab90eb usb: dwc3: keystone: drop dma_mask configuration
1ffb4d5cc78a usb: dwc3: pci: add ID for one more Intel Broxton platform
e901aa159dac usb: dwc3: gadget: fix endpoint renaming
ad14d4e0308a usb: dwc3: gadget: release spin lock during gadget resume
f59dcab17629 usb: dwc3: core: improve reset sequence
33c1f638a0fe Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
77966eb85e6d usb: dwc3: Validate the maximum_speed parameter
7580862b3e80 usb: dwc3: Enable SuperSpeedPlus
2c7f1bd9127a usb: dwc3: Update maximum_speed for SuperSpeedPlus
ee5cd41c9117 usb: dwc3: Update speed checks for SuperSpeedPlus
1f38f88a24c8 usb: dwc3: Update register fields for SuperSpeedPlus
c4137a9c841e usb: dwc3: DWC_usb31 controller check
71e41bbb4379 Merge 4.5-rc6 into usb-next
3d755dcc20dd usb: dwc3: Remove impossible check for of_clk_get_parent_count() < 0
172ad9af55d2 Merge 4.5-rc4 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 102 +++++++++++++++++++++++++-------------
drivers/usb/dwc3/core.h | 11 +++-
drivers/usb/dwc3/ep0.c | 9 ++--
drivers/usb/dwc3/gadget.c | 47 ++++++++++++++----
4 files changed, 119 insertions(+), 50 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index de5e01f41bc2..34277ced26bd 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -67,23 +67,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
static int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
+ int retries = 1000;
int ret;
- /* Before Resetting PHY, put Core in Reset */
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg |= DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
-
- /* Assert USB3 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
-
- /* Assert USB2 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
-
usb_phy_init(dwc->usb2_phy);
usb_phy_init(dwc->usb3_phy);
ret = phy_init(dwc->usb2_generic_phy);
@@ -95,26 +81,28 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
phy_exit(dwc->usb2_generic_phy);
return ret;
}
- mdelay(100);
- /* Clear USB3 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ /*
+ * We're resetting only the device side because, if we're in host mode,
+ * XHCI driver will reset the host block. If dwc3 was configured for
+ * host-only mode, then we can return early.
+ */
+ if (dwc->dr_mode == USB_DR_MODE_HOST)
+ return 0;
- /* Clear USB2 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= DWC3_DCTL_CSFTRST;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- mdelay(100);
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ return 0;
- /* After PHYs are stable we can take Core out of reset state */
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg &= ~DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ udelay(1);
+ } while (--retries);
- return 0;
+ return -ETIMEDOUT;
}
/**
@@ -962,10 +950,6 @@ static int dwc3_probe(struct platform_device *pdev)
fladj = pdata->fladj_value;
}
- /* default to superspeed if no maximum_speed passed */
- if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
- dwc->maximum_speed = USB_SPEED_SUPER;
-
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -1016,6 +1000,33 @@ static int dwc3_probe(struct platform_device *pdev)
goto err1;
}
+ /* Check the maximum_speed parameter */
+ switch (dwc->maximum_speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ break;
+ default:
+ dev_err(dev, "invalid maximum_speed parameter %d\n",
+ dwc->maximum_speed);
+ /* fall through */
+ case USB_SPEED_UNKNOWN:
+ /* default to superspeed */
+ dwc->maximum_speed = USB_SPEED_SUPER;
+
+ /*
+ * default to superspeed plus if we are capable.
+ */
+ if (dwc3_is_usb31(dwc) &&
+ (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+
+ break;
+ }
+
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc, fladj);
@@ -1139,6 +1150,11 @@ static int dwc3_suspend(struct device *dev)
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0);
+ WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0);
+
pinctrl_pm_select_sleep_state(dev);
return 0;
@@ -1152,11 +1168,21 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+ ret = phy_power_on(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_power_on(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_usb2phy_power;
+
usb_phy_init(dwc->usb3_phy);
usb_phy_init(dwc->usb2_phy);
ret = phy_init(dwc->usb2_generic_phy);
if (ret < 0)
- return ret;
+ goto err_usb3phy_power;
ret = phy_init(dwc->usb3_generic_phy);
if (ret < 0)
@@ -1189,6 +1215,12 @@ static int dwc3_resume(struct device *dev)
err_usb2phy_init:
phy_exit(dwc->usb2_generic_phy);
+err_usb3phy_power:
+ phy_power_off(dwc->usb3_generic_phy);
+
+err_usb2phy_power:
+ phy_power_off(dwc->usb2_generic_phy);
+
return ret;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e4f8b90d9627..6254b2ff9080 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -223,7 +223,8 @@
/* Global HWPARAMS3 Register */
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
-#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1
+#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1
+#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */
#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
@@ -249,6 +250,7 @@
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
#define DWC3_DCFG_SPEED_MASK (7 << 0)
+#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
#define DWC3_DCFG_FULLSPEED2 (1 << 0)
@@ -339,6 +341,7 @@
#define DWC3_DSTS_CONNECTSPD (7 << 0)
+#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
#define DWC3_DSTS_FULLSPEED2 (1 << 0)
@@ -1024,6 +1027,12 @@ struct dwc3_gadget_ep_cmd_params {
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+/* check whether we are on the DWC_usb31 core */
+static inline bool dwc3_is_usb31(struct dwc3 *dwc)
+{
+ return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 8d6b75c2f53b..eca2e6d8e041 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -356,7 +356,8 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
*/
usb_status |= dwc->gadget.is_selfpowered;
- if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
+ if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
+ (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU1ENA)
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
@@ -426,7 +427,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
case USB_DEVICE_U1_ENABLE:
if (state != USB_STATE_CONFIGURED)
return -EINVAL;
- if (dwc->speed != DWC3_DSTS_SUPERSPEED)
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -440,7 +442,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
case USB_DEVICE_U2_ENABLE:
if (state != USB_STATE_CONFIGURED)
return -EINVAL;
- if (dwc->speed != DWC3_DSTS_SUPERSPEED)
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2363bad45af8..8e4a1b195e9b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -463,7 +463,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ if (dwc->gadget.speed >= USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst - 1;
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
@@ -568,7 +568,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
if (!usb_endpoint_xfer_isoc(desc))
- return 0;
+ goto out;
/* Link TRB for ISOC. The HWO bit is never reset */
trb_st_hw = &dep->trb_pool[0];
@@ -582,9 +582,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
}
+out:
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
- strlcat(dep->name, "-control", sizeof(dep->name));
+ /* don't change name */
break;
case USB_ENDPOINT_XFER_ISOC:
strlcat(dep->name, "-isoc", sizeof(dep->name));
@@ -1441,7 +1442,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
- if (speed == DWC3_DSTS_SUPERSPEED) {
+ if ((speed == DWC3_DSTS_SUPERSPEED) ||
+ (speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
ret = -EINVAL;
goto out;
@@ -1666,10 +1668,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
case USB_SPEED_HIGH:
reg |= DWC3_DSTS_HIGHSPEED;
break;
- case USB_SPEED_SUPER: /* FALLTHROUGH */
- case USB_SPEED_UNKNOWN: /* FALTHROUGH */
+ case USB_SPEED_SUPER_PLUS:
+ reg |= DWC3_DSTS_SUPERSPEED_PLUS;
+ break;
default:
- reg |= DWC3_DSTS_SUPERSPEED;
+ dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
+ dwc->maximum_speed);
+ /* fall through */
+ case USB_SPEED_SUPER:
+ reg |= DWC3_DCFG_SUPERSPEED;
+ break;
}
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2340,7 +2348,8 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
* this. Maybe it becomes part of the power saving plan.
*/
- if (speed != DWC3_DSTS_SUPERSPEED)
+ if ((speed != DWC3_DSTS_SUPERSPEED) &&
+ (speed != DWC3_DSTS_SUPERSPEED_PLUS))
return;
/*
@@ -2369,6 +2378,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_update_ram_clk_sel(dwc, speed);
switch (speed) {
+ case DWC3_DCFG_SUPERSPEED_PLUS:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ dwc->gadget.ep0->maxpacket = 512;
+ dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+ break;
case DWC3_DCFG_SUPERSPEED:
/*
* WORKAROUND: DWC3 revisions <1.90a have an issue which
@@ -2410,8 +2424,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
/* Enable USB2 LPM Capability */
- if ((dwc->revision > DWC3_REVISION_194A)
- && (speed != DWC3_DCFG_SUPERSPEED)) {
+ if ((dwc->revision > DWC3_REVISION_194A) &&
+ (speed != DWC3_DCFG_SUPERSPEED) &&
+ (speed != DWC3_DCFG_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg |= DWC3_DCFG_LPM_CAP;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2473,7 +2488,11 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
* implemented.
*/
- dwc->gadget_driver->resume(&dwc->gadget);
+ if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->resume(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
}
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
@@ -2917,6 +2936,9 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
+ if (!dwc->gadget_driver)
+ return 0;
+
if (dwc->pullups_connected) {
dwc3_gadget_disable_irq(dwc);
dwc3_gadget_run_stop(dwc, true, true);
@@ -2935,6 +2957,9 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
struct dwc3_ep *dep;
int ret;
+ if (!dwc->gadget_driver)
+ return 0;
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 10/64] usb: dwc3: import from kernel v4.7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (8 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 09/64] usb: dwc3: import from kernel v4.6 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 11/64] usb: dwc3: import from kernel v4.8 Jens Wiklander
` (57 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.6 to v4.7.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.6..v4.7
Commits imported:
0d064a7b9c43 Merge tag 'mfd-fixes-4.7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
5baaf3b9efe1 usb: dwc3: st: Use explicit reset_control_get_exclusive() API
4aa34ce3c1ee Merge tag 'mfd-fixes-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
002f17bc54ff usb: dwc3: st: Inform the reset framework that our reset line may be shared
50c763f8c1ba usb: dwc3: Set the ClearPendIN bit on Clear Stall EP command
27a0faafdca5 usb: dwc3: st: Fix USB_DR_MODE_PERIPHERAL configuration.
4879efb34f7d usb: dwc3: exynos: Fix deferred probing storm.
973986126a41 usb: dwc3: gadget: fix mask and shift order in DWC3_DCFG_NUMP()
ce15bda10121 Merge tag 'usb-for-v4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
2a58f9c12bb3 usb: dwc3: gadget: disable automatic calculation of ACK TP NUMP
676e34974481 usb: dwc3: gadget: update DCFG.NumP to max burst size
7b9cc7a2b101 usb: dwc3: gadget: give better command return code
ce046d323d74 usb: dwc3: pci: pass the platform device as a parameter to dwc3_pci_quirks()
474799f07376 usb: dwc3: pci: make build-in device properties available
ae4114137607 usb: dwc3: ep0: sanity check test mode selector
69b4269eff5f Merge 4.6-rc5 into usb-next
818ec3aba883 usb: dwc3: debugfs: dump out endpoint details
b058f3e8a399 usb: dwc3: core: add helper to extract trb type
cf6d867d3b57 usb: dwc3: core: add fifo space helper
4e9f311833a9 usb: dwc3: make dwc3_debugfs_init return value be void
af566a0be6e4 usb: dwc3: omap: get rid of dma_status
8e7046b71dae usb: dwc3: gadget: don't interrupt when chained
052ba52efa17 usb: dwc3: gadget: remove newline from trace
36b68aae8e39 usb: dwc3: gadget: use link TRB for all endpoint types
4faf75504a7d usb: dwc3: gadget: move % operation to increment helpers
ef966b9d3353 usb: dwc3: gadget: add trb enqueue/dequeue helpers
70fdb273db37 usb: dwc3: get rid of DWC3_TRB_MASK
c28f82595dde usb: dwc3: switch trb enqueue/dequeue and first_trb_index to u8
5ef68c56e169 usb: dwc3: core: document struct dwc3_request
53fd88189e08 usb: dwc3: gadget: rename busy/free_slot to trb_enqueue/dequeue
495dd5f78145 usb: dwc3: omap: drop dma_mask configuration
e58dd357741b usb: dwc3: add disable receiver detection in P3 quirk
c36d8e947a56 usb: dwc3: gadget: put link to U0 before Start Transfer
218ef7b647e3 usb: dwc3: gadget: extract unlocked dwc3_gadget_wakeup()
2b0f11df84bb usb: dwc3: gadget: clear SUSPHY bit before ep cmds
c0ca324d09a0 usb: dwc3: gadget: combine return points into a single one
dea520a4a283 usb: dwc3: gadget: pass ev_buff as cookie to irq handler
696c8b128220 usb: dwc3: drop ev_buffs array
660e9bde74d6 usb: dwc3: remove num_event_buffers
aa3342c8bb61 usb: dwc3: better name for our request management lists
8495036e986b usb: dwc3: increase maximum number of TRBs per endpoint
ca4d44ea2a91 usb: dwc3: gadget: always enable CSP
bc5081617fae usb: dwc3: drop FIFO resizing logic
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 118 ++++----
drivers/usb/dwc3/core.h | 85 ++++--
drivers/usb/dwc3/debug.h | 6 +-
drivers/usb/dwc3/ep0.c | 43 +--
drivers/usb/dwc3/gadget.c | 457 +++++++++++++++----------------
drivers/usb/dwc3/gadget.h | 6 +-
drivers/usb/dwc3/platform_data.h | 2 +-
7 files changed, 374 insertions(+), 343 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 34277ced26bd..a590cd225bb7 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -60,6 +60,20 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
+u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+
+ dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
+ DWC3_GDBGFIFOSPACE_NUM(dep->number) |
+ DWC3_GDBGFIFOSPACE_TYPE(type));
+
+ reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
+
+ return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
+}
+
/**
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
@@ -203,13 +217,10 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
static void dwc3_free_event_buffers(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
- int i;
- for (i = 0; i < dwc->num_event_buffers; i++) {
- evt = dwc->ev_buffs[i];
- if (evt)
- dwc3_free_one_event_buffer(dwc, evt);
- }
+ evt = dwc->ev_buf;
+ if (evt)
+ dwc3_free_one_event_buffer(dwc, evt);
}
/**
@@ -222,27 +233,14 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
*/
static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
{
- int num;
- int i;
-
- num = DWC3_NUM_INT(dwc->hwparams.hwparams1);
- dwc->num_event_buffers = num;
-
- dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
- GFP_KERNEL);
- if (!dwc->ev_buffs)
- return -ENOMEM;
-
- for (i = 0; i < num; i++) {
- struct dwc3_event_buffer *evt;
+ struct dwc3_event_buffer *evt;
- evt = dwc3_alloc_one_event_buffer(dwc, length);
- if (IS_ERR(evt)) {
- dev_err(dwc->dev, "can't allocate event buffer\n");
- return PTR_ERR(evt);
- }
- dwc->ev_buffs[i] = evt;
+ evt = dwc3_alloc_one_event_buffer(dwc, length);
+ if (IS_ERR(evt)) {
+ dev_err(dwc->dev, "can't allocate event buffer\n");
+ return PTR_ERR(evt);
}
+ dwc->ev_buf = evt;
return 0;
}
@@ -256,25 +254,22 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
static int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
- int n;
- for (n = 0; n < dwc->num_event_buffers; n++) {
- evt = dwc->ev_buffs[n];
- dwc3_trace(trace_dwc3_core,
- "Event buf %p dma %08llx length %d\n",
- evt->buf, (unsigned long long) evt->dma,
- evt->length);
-
- evt->lpos = 0;
-
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
- lower_32_bits(evt->dma));
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
- upper_32_bits(evt->dma));
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
- DWC3_GEVNTSIZ_SIZE(evt->length));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
- }
+ evt = dwc->ev_buf;
+ dwc3_trace(trace_dwc3_core,
+ "Event buf %p dma %08llx length %d\n",
+ evt->buf, (unsigned long long) evt->dma,
+ evt->length);
+
+ evt->lpos = 0;
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
+ lower_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
+ upper_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
return 0;
}
@@ -282,19 +277,16 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
- int n;
- for (n = 0; n < dwc->num_event_buffers; n++) {
- evt = dwc->ev_buffs[n];
+ evt = dwc->ev_buf;
- evt->lpos = 0;
+ evt->lpos = 0;
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK
- | DWC3_GEVNTSIZ_SIZE(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
- }
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
+ | DWC3_GEVNTSIZ_SIZE(0));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
}
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
@@ -434,6 +426,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
+ if (dwc->dis_rxdet_inp3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+
if (dwc->req_p1p2p3_quirk)
reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
@@ -882,9 +877,6 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
- dwc->needs_fifo_resize = device_property_read_bool(dev,
- "tx-fifo-resize");
-
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
dwc->u2exit_lfps_quirk = device_property_read_bool(dev,
@@ -907,6 +899,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,dis_u2_susphy_quirk");
dwc->dis_enblslpm_quirk = device_property_read_bool(dev,
"snps,dis_enblslpm_quirk");
+ dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
+ "snps,dis_rxdet_inp3_quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
@@ -926,7 +920,6 @@ static int dwc3_probe(struct platform_device *pdev)
if (pdata->hird_threshold)
hird_threshold = pdata->hird_threshold;
- dwc->needs_fifo_resize = pdata->tx_fifo_resize;
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
dwc->dr_mode = pdata->dr_mode;
@@ -941,6 +934,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk;
+ dwc->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_quirk;
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
if (pdata->tx_de_emphasis)
@@ -1050,19 +1044,11 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret)
goto err5;
- ret = dwc3_debugfs_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize debugfs\n");
- goto err6;
- }
-
+ dwc3_debugfs_init(dwc);
pm_runtime_allow(dev);
return 0;
-err6:
- dwc3_core_exit_mode(dwc);
-
err5:
dwc3_event_buffers_cleanup(dwc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6254b2ff9080..654050684f4f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -152,6 +152,24 @@
/* Bit fields */
+/* Global Debug Queue/FIFO Space Available Register */
+#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
+#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
+#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
+
+#define DWC3_TXFIFOQ 1
+#define DWC3_RXFIFOQ 3
+#define DWC3_TXREQQ 5
+#define DWC3_RXREQQ 7
+#define DWC3_RXINFOQ 9
+#define DWC3_DESCFETCHQ 13
+#define DWC3_EVENTQ 15
+
+/* Global RX Threshold Configuration Register */
+#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
+#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
+#define DWC3_GRXTHRCFG_PKTCNTSEL (1 << 29)
+
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN (1 << 16)
@@ -193,6 +211,7 @@
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
+#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
@@ -257,6 +276,9 @@
#define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_FULLSPEED1 (3 << 0)
+#define DWC3_DCFG_NUMP_SHIFT 17
+#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
+#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
#define DWC3_DCFG_LPM_CAP (1 << 22)
/* Device Control Register */
@@ -380,6 +402,7 @@
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
+#define DWC3_DEPCMD_CLEARPENDIN (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
#define DWC3_DEPCMD_CMDIOC (1 << 8)
@@ -438,18 +461,17 @@ struct dwc3_event_buffer {
#define DWC3_EP_DIRECTION_TX true
#define DWC3_EP_DIRECTION_RX false
-#define DWC3_TRB_NUM 32
-#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1)
+#define DWC3_TRB_NUM 256
/**
* struct dwc3_ep - device side endpoint representation
* @endpoint: usb endpoint
- * @request_list: list of requests for this endpoint
- * @req_queued: list of requests on this ep which have TRBs setup
+ * @pending_list: list of pending requests for this endpoint
+ * @started_list: list of started requests on this endpoint
* @trb_pool: array of transaction buffers
* @trb_pool_dma: dma address of @trb_pool
- * @free_slot: next slot which is going to be used
- * @busy_slot: first slot which is owned by HW
+ * @trb_enqueue: enqueue 'pointer' into TRB array
+ * @trb_dequeue: dequeue 'pointer' into TRB array
* @desc: usb_endpoint_descriptor pointer
* @dwc: pointer to DWC controller
* @saved_state: ep state saved during hibernation
@@ -464,13 +486,11 @@ struct dwc3_event_buffer {
*/
struct dwc3_ep {
struct usb_ep endpoint;
- struct list_head request_list;
- struct list_head req_queued;
+ struct list_head pending_list;
+ struct list_head started_list;
struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma;
- u32 free_slot;
- u32 busy_slot;
const struct usb_ss_ep_comp_descriptor *comp_desc;
struct dwc3 *dwc;
@@ -486,6 +506,18 @@ struct dwc3_ep {
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
+ /*
+ * IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
+ * use a u8 type here. If anybody decides to increase number of TRBs to
+ * anything larger than 256 - I can't see why people would want to do
+ * this though - then this type needs to be changed.
+ *
+ * By using u8 types we ensure that our % operator when incrementing
+ * enqueue and dequeue get optimized away by the compiler.
+ */
+ u8 trb_enqueue;
+ u8 trb_dequeue;
+
u8 number;
u8 type;
u8 resource_index;
@@ -557,6 +589,7 @@ enum dwc3_link_state {
#define DWC3_TRB_CTRL_IOC (1 << 11)
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
+#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2)
#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3)
@@ -623,19 +656,32 @@ struct dwc3_hwparams {
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
+/**
+ * struct dwc3_request - representation of a transfer request
+ * @request: struct usb_request to be transferred
+ * @list: a list_head used for request queueing
+ * @dep: struct dwc3_ep owning this request
+ * @first_trb_index: index to first trb used by this request
+ * @epnum: endpoint number to which this request refers
+ * @trb: pointer to struct dwc3_trb
+ * @trb_dma: DMA address of @trb
+ * @direction: IN or OUT direction flag
+ * @mapped: true when request has been dma-mapped
+ * @queued: true when request has been queued to HW
+ */
struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
- u32 start_slot;
+ u8 first_trb_index;
u8 epnum;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
unsigned direction:1;
unsigned mapped:1;
- unsigned queued:1;
+ unsigned started:1;
};
/*
@@ -667,7 +713,6 @@ struct dwc3_scratchpad_array {
* @regs: base address for our registers
* @regs_size: address space size
* @nr_scratch: number of scratch buffers
- * @num_event_buffers: calculated number of event buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
@@ -709,9 +754,7 @@ struct dwc3_scratchpad_array {
* 0 - utmi_sleep_n
* 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
- * @needs_fifo_resize: not all users might want fifo resizing, flag it
* @pullups_connected: true when Run/Stop bit is set
- * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
@@ -756,7 +799,7 @@ struct dwc3 {
struct platform_device *xhci;
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
- struct dwc3_event_buffer **ev_buffs;
+ struct dwc3_event_buffer *ev_buf;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
struct usb_gadget gadget;
@@ -780,7 +823,6 @@ struct dwc3 {
u32 gctl;
u32 nr_scratch;
- u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
@@ -855,9 +897,7 @@ struct dwc3 {
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
- unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
- unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
unsigned three_stage_setup:1;
unsigned usb3_lpm_capable:1;
@@ -873,6 +913,7 @@ struct dwc3 {
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1;
+ unsigned dis_rxdet_inp3_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
@@ -938,6 +979,10 @@ struct dwc3_event_depevt {
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
+/* In response to Start Transfer */
+#define DEPEVT_TRANSFER_NO_RESOURCE 1
+#define DEPEVT_TRANSFER_BUS_EXPIRY 2
+
u32 parameters:16;
} __packed;
@@ -1025,7 +1070,7 @@ struct dwc3_gadget_ep_cmd_params {
/* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
/* check whether we are on the DWC_usb31 core */
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 07fbc2d94fd4..71e318025964 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -217,11 +217,11 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
#ifdef CONFIG_DEBUG_FS
-extern int dwc3_debugfs_init(struct dwc3 *);
+extern void dwc3_debugfs_init(struct dwc3 *);
extern void dwc3_debugfs_exit(struct dwc3 *);
#else
-static inline int dwc3_debugfs_init(struct dwc3 *d)
-{ return 0; }
+static inline void dwc3_debugfs_init(struct dwc3 *d)
+{ }
static inline void dwc3_debugfs_exit(struct dwc3 *d)
{ }
#endif
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index eca2e6d8e041..51b52a79dfec 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -70,10 +70,10 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
return 0;
}
- trb = &dwc->ep0_trb[dep->free_slot];
+ trb = &dwc->ep0_trb[dep->trb_enqueue];
if (chain)
- dep->free_slot++;
+ dep->trb_enqueue++;
trb->bpl = lower_32_bits(buf_dma);
trb->bph = upper_32_bits(buf_dma);
@@ -124,7 +124,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
req->request.status = -EINPROGRESS;
req->epnum = dep->number;
- list_add_tail(&req->list, &dep->request_list);
+ list_add_tail(&req->list, &dep->pending_list);
/*
* Gadget driver might not be quick enough to queue a request
@@ -240,7 +240,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
}
/* we share one TRB for ep0/1 */
- if (!list_empty(&dep->request_list)) {
+ if (!list_empty(&dep->pending_list)) {
ret = -EBUSY;
goto out;
}
@@ -272,10 +272,10 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
dep->flags = DWC3_EP_ENABLED;
dwc->delayed_status = false;
- if (!list_empty(&dep->request_list)) {
+ if (!list_empty(&dep->pending_list)) {
struct dwc3_request *req;
- req = next_request(&dep->request_list);
+ req = next_request(&dep->pending_list);
dwc3_gadget_giveback(dep, req, -ECONNRESET);
}
@@ -463,8 +463,18 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (!set)
return -EINVAL;
- dwc->test_mode_nr = wIndex >> 8;
- dwc->test_mode = true;
+ switch (wIndex >> 8) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ dwc->test_mode_nr = wIndex >> 8;
+ dwc->test_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -586,9 +596,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
- dwc->resize_fifos = true;
- dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
}
break;
@@ -809,7 +816,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
trace_dwc3_complete_trb(ep0, trb);
- r = next_request(&ep0->request_list);
+ r = next_request(&ep0->pending_list);
if (!r)
return;
@@ -848,7 +855,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
trb++;
length = trb->size & DWC3_TRB_SIZE_MASK;
- ep0->free_slot = 0;
+ ep0->trb_enqueue = 0;
}
transfer_size = roundup((ur->length - transfer_size),
@@ -897,8 +904,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
trace_dwc3_complete_trb(dep, trb);
- if (!list_empty(&dep->request_list)) {
- r = next_request(&dep->request_list);
+ if (!list_empty(&dep->pending_list)) {
+ r = next_request(&dep->pending_list);
dwc3_gadget_giveback(dep, r, 0);
}
@@ -1027,12 +1034,6 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
- if (dwc->resize_fifos) {
- dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
- dwc3_gadget_resize_tx_fifos(dwc);
- dwc->resize_fifos = 0;
- }
-
WARN_ON(dwc3_ep0_start_control_status(dep));
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8e4a1b195e9b..07248ff1be5c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -145,90 +145,21 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
return -ETIMEDOUT;
}
-/**
- * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
- * @dwc: pointer to our context structure
- *
- * This function will a best effort FIFO allocation in order
- * to improve FIFO usage and throughput, while still allowing
- * us to enable as many endpoints as possible.
- *
- * Keep in mind that this operation will be highly dependent
- * on the configured size for RAM1 - which contains TxFifo -,
- * the amount of endpoints enabled on coreConsultant tool, and
- * the width of the Master Bus.
- *
- * In the ideal world, we would always be able to satisfy the
- * following equation:
- *
- * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \
- * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes
- *
- * Unfortunately, due to many variables that's not always the case.
- */
-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
+static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
{
- int last_fifo_depth = 0;
- int ram1_depth;
- int fifo_size;
- int mdwidth;
- int num;
-
- if (!dwc->needs_fifo_resize)
- return 0;
-
- ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
-
- /* MDWIDTH is represented in bits, we need it in bytes */
- mdwidth >>= 3;
-
- /*
- * FIXME For now we will only allocate 1 wMaxPacketSize space
- * for each enabled endpoint, later patches will come to
- * improve this algorithm so that we better use the internal
- * FIFO space
- */
- for (num = 0; num < dwc->num_in_eps; num++) {
- /* bit0 indicates direction; 1 means IN ep */
- struct dwc3_ep *dep = dwc->eps[(num << 1) | 1];
- int mult = 1;
- int tmp;
-
- if (!(dep->flags & DWC3_EP_ENABLED))
- continue;
-
- if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
- || usb_endpoint_xfer_isoc(dep->endpoint.desc))
- mult = 3;
-
- /*
- * REVISIT: the following assumes we will always have enough
- * space available on the FIFO RAM for all possible use cases.
- * Make sure that's true somehow and change FIFO allocation
- * accordingly.
- *
- * If we have Bulk or Isochronous endpoints, we want
- * them to be able to be very, very fast. So we're giving
- * those endpoints a fifo_size which is enough for 3 full
- * packets
- */
- tmp = mult * (dep->endpoint.maxpacket + mdwidth);
- tmp += mdwidth;
-
- fifo_size = DIV_ROUND_UP(tmp, mdwidth);
-
- fifo_size |= (last_fifo_depth << 16);
-
- dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d",
- dep->name, last_fifo_depth, fifo_size & 0xffff);
-
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
+ dep->trb_enqueue++;
+ dep->trb_enqueue %= DWC3_TRB_NUM;
+}
- last_fifo_depth += (fifo_size & 0xffff);
- }
+static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
+{
+ dep->trb_dequeue++;
+ dep->trb_dequeue %= DWC3_TRB_NUM;
+}
- return 0;
+static int dwc3_ep_is_last_trb(unsigned int index)
+{
+ return index == DWC3_TRB_NUM - 1;
}
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
@@ -237,21 +168,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
struct dwc3 *dwc = dep->dwc;
int i;
- if (req->queued) {
+ if (req->started) {
i = 0;
do {
- dep->busy_slot++;
+ dwc3_ep_inc_deq(dep);
/*
* Skip LINK TRB. We can't use req->trb and check for
* DWC3_TRBCTL_LINK_TRB because it points the TRB we
* just completed (not the LINK TRB).
*/
- if (((dep->busy_slot & DWC3_TRB_MASK) ==
- DWC3_TRB_NUM- 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dep->busy_slot++;
+ if (dwc3_ep_is_last_trb(dep->trb_dequeue))
+ dwc3_ep_inc_deq(dep);
} while(++i < req->request.num_mapped_sgs);
- req->queued = false;
+ req->started = false;
}
list_del(&req->list);
req->trb = NULL;
@@ -307,6 +236,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
} while (1);
}
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
+
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
@@ -314,8 +245,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
u32 timeout = 500;
u32 reg;
+ int susphy = false;
+ int ret = -EINVAL;
+
trace_dwc3_gadget_ep_cmd(dep, cmd, params);
+ /*
+ * Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
+ * we're issuing an endpoint command, we must check if
+ * GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
+ *
+ * We will also set SUSPHY bit to what it was before returning as stated
+ * by the same section on Synopsys databook.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
+ susphy = true;
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
+ int needs_wakeup;
+
+ needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
+ dwc->link_state == DWC3_LINK_STATE_U2 ||
+ dwc->link_state == DWC3_LINK_STATE_U3);
+
+ if (unlikely(needs_wakeup)) {
+ ret = __dwc3_gadget_wakeup(dwc);
+ dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
+ ret);
+ }
+ }
+
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2);
@@ -324,12 +287,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
do {
reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
if (!(reg & DWC3_DEPCMD_CMDACT)) {
+ int cmd_status = DWC3_DEPCMD_STATUS(reg);
+
dwc3_trace(trace_dwc3_gadget,
"Command Complete --> %d",
- DWC3_DEPCMD_STATUS(reg));
- if (DWC3_DEPCMD_STATUS(reg))
- return -EINVAL;
- return 0;
+ cmd_status);
+
+ switch (cmd_status) {
+ case 0:
+ ret = 0;
+ break;
+ case DEPEVT_TRANSFER_NO_RESOURCE:
+ dwc3_trace(trace_dwc3_gadget, "%s: no resource available");
+ ret = -EINVAL;
+ break;
+ case DEPEVT_TRANSFER_BUS_EXPIRY:
+ /*
+ * SW issues START TRANSFER command to
+ * isochronous ep with future frame interval. If
+ * future interval time has already passed when
+ * core receives the command, it will respond
+ * with an error status of 'Bus Expiry'.
+ *
+ * Instead of always returning -EINVAL, let's
+ * give a hint to the gadget driver that this is
+ * the case by returning -EAGAIN.
+ */
+ dwc3_trace(trace_dwc3_gadget, "%s: bus expiry");
+ ret = -EAGAIN;
+ break;
+ default:
+ dev_WARN(dwc->dev, "UNKNOWN cmd status\n");
+ }
+
+ break;
}
/*
@@ -340,11 +331,42 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
if (!timeout) {
dwc3_trace(trace_dwc3_gadget,
"Command Timed Out");
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ break;
}
udelay(1);
} while (1);
+
+ if (unlikely(susphy)) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ return ret;
+}
+
+static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd = DWC3_DEPCMD_CLEARSTALL;
+
+ /*
+ * As of core revision 2.60a the recommended programming model
+ * is to set the ClearPendIN bit when issuing a Clear Stall EP
+ * command for IN endpoints. This is to prevent an issue where
+ * some (non-compliant) hosts may not send ACK TPs for pending
+ * IN transfers due to a mishandled error condition. Synopsys
+ * STAR 9000614252.
+ */
+ if (dep->direction && (dwc->revision >= DWC3_REVISION_260A))
+ cmd |= DWC3_DEPCMD_CLEARPENDIN;
+
+ memset(¶ms, 0, sizeof(params));
+
+ return dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
}
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
@@ -464,9 +486,19 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
/* Burst size is only needed in SuperSpeed mode */
if (dwc->gadget.speed >= USB_SPEED_SUPER) {
- u32 burst = dep->endpoint.maxburst - 1;
+ u32 burst = dep->endpoint.maxburst;
+ u32 nump;
+ u32 reg;
+
+ /* update NumP */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ nump = DWC3_DCFG_NUMP(reg);
+ nump = max(nump, burst);
+ reg &= ~DWC3_DCFG_NUMP_MASK;
+ reg |= nump << DWC3_DCFG_NUMP_SHIFT;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
- params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+ params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
if (ignore)
@@ -567,10 +599,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
- if (!usb_endpoint_xfer_isoc(desc))
+ if (usb_endpoint_xfer_control(desc))
goto out;
- /* Link TRB for ISOC. The HWO bit is never reset */
+ /* Link TRB. The HWO bit is never reset */
trb_st_hw = &dep->trb_pool[0];
trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
@@ -608,19 +640,19 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
- if (!list_empty(&dep->req_queued)) {
+ if (!list_empty(&dep->started_list)) {
dwc3_stop_active_transfer(dwc, dep->number, true);
/* - giveback all requests to gadget driver */
- while (!list_empty(&dep->req_queued)) {
- req = next_request(&dep->req_queued);
+ while (!list_empty(&dep->started_list)) {
+ req = next_request(&dep->started_list);
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
}
- while (!list_empty(&dep->request_list)) {
- req = next_request(&dep->request_list);
+ while (!list_empty(&dep->pending_list)) {
+ req = next_request(&dep->pending_list);
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
@@ -783,20 +815,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
chain ? " chain" : "");
- trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+ trb = &dep->trb_pool[dep->trb_enqueue];
if (!req->trb) {
- dwc3_gadget_move_request_queued(req);
+ dwc3_gadget_move_started_request(req);
req->trb = trb;
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- req->start_slot = dep->free_slot & DWC3_TRB_MASK;
+ req->first_trb_index = dep->trb_enqueue;
}
- dep->free_slot++;
- /* Skip the LINK-TRB on ISOC */
- if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dep->free_slot++;
+ dwc3_ep_inc_enq(dep);
+ /* Skip the LINK-TRB */
+ if (dwc3_ep_is_last_trb(dep->trb_enqueue))
+ dwc3_ep_inc_enq(dep);
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
@@ -812,6 +843,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
else
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+
+ /* always enable Interrupt on Missed ISOC */
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
break;
case USB_ENDPOINT_XFER_BULK:
@@ -826,15 +860,14 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
BUG();
}
+ /* always enable Continue on Short Packet */
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
+
if (!req->request.no_interrupt && !chain)
- trb->ctrl |= DWC3_TRB_CTRL_IOC;
+ trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
- trb->ctrl |= DWC3_TRB_CTRL_CSP;
- } else if (last) {
+ if (last)
trb->ctrl |= DWC3_TRB_CTRL_LST;
- }
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
@@ -860,55 +893,29 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
{
struct dwc3_request *req, *n;
u32 trbs_left;
- u32 max;
unsigned int last_one = 0;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
- /* the first request must not be queued */
- trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
-
- /* Can't wrap around on a non-isoc EP since there's no link TRB */
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
- if (trbs_left > max)
- trbs_left = max;
- }
+ trbs_left = dep->trb_dequeue - dep->trb_enqueue;
/*
- * If busy & slot are equal than it is either full or empty. If we are
- * starting to process requests then we are empty. Otherwise we are
+ * If enqueue & dequeue are equal than it is either full or empty. If we
+ * are starting to process requests then we are empty. Otherwise we are
* full and don't do anything
*/
if (!trbs_left) {
if (!starting)
return;
+
trbs_left = DWC3_TRB_NUM;
- /*
- * In case we start from scratch, we queue the ISOC requests
- * starting from slot 1. This is done because we use ring
- * buffer and have no LST bit to stop us. Instead, we place
- * IOC bit every TRB_NUM/4. We try to avoid having an interrupt
- * after the first request so we start at slot 1 and have
- * 7 requests proceed before we hit the first IOC.
- * Other transfer types don't use the ring buffer and are
- * processed from the first TRB until the last one. Since we
- * don't wrap around we have to start at the beginning.
- */
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dep->busy_slot = 1;
- dep->free_slot = 1;
- } else {
- dep->busy_slot = 0;
- dep->free_slot = 0;
- }
}
/* The last TRB is a link TRB, not used for xfer */
- if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ if (trbs_left <= 1)
return;
- list_for_each_entry_safe(req, n, &dep->request_list, list) {
+ list_for_each_entry_safe(req, n, &dep->pending_list, list) {
unsigned length;
dma_addr_t dma;
last_one = false;
@@ -927,7 +934,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
if (i == (request->num_mapped_sgs - 1) ||
sg_is_last(s)) {
- if (list_empty(&dep->request_list))
+ if (list_empty(&dep->pending_list))
last_one = true;
chain = false;
}
@@ -957,7 +964,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
last_one = 1;
/* Is this the last request? */
- if (list_is_last(&req->list, &dep->request_list))
+ if (list_is_last(&req->list, &dep->pending_list))
last_one = 1;
dwc3_prepare_one_trb(dep, req, dma, length,
@@ -988,18 +995,18 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
* new requests as we try to set the IOC bit only on the last request.
*/
if (start_new) {
- if (list_empty(&dep->req_queued))
+ if (list_empty(&dep->started_list))
dwc3_prepare_trbs(dep, start_new);
/* req points to the first request which will be sent */
- req = next_request(&dep->req_queued);
+ req = next_request(&dep->started_list);
} else {
dwc3_prepare_trbs(dep, start_new);
/*
* req points to the first request where HWO changed from 0 to 1
*/
- req = next_request(&dep->req_queued);
+ req = next_request(&dep->started_list);
}
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
@@ -1046,7 +1053,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
{
u32 uf;
- if (list_empty(&dep->request_list)) {
+ if (list_empty(&dep->pending_list)) {
dwc3_trace(trace_dwc3_gadget,
"ISOC ep %s run out for requests",
dep->name);
@@ -1114,7 +1121,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (ret)
return ret;
- list_add_tail(&req->list, &dep->request_list);
+ list_add_tail(&req->list, &dep->pending_list);
/*
* If there are no pending requests and the endpoint isn't already
@@ -1149,7 +1156,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* notion of current microframe.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if (list_empty(&dep->req_queued)) {
+ if (list_empty(&dep->started_list)) {
dwc3_stop_active_transfer(dwc, dep->number, true);
dep->flags = DWC3_EP_ENABLED;
}
@@ -1267,13 +1274,13 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
spin_lock_irqsave(&dwc->lock, flags);
- list_for_each_entry(r, &dep->request_list, list) {
+ list_for_each_entry(r, &dep->pending_list, list) {
if (r == req)
break;
}
if (r != req) {
- list_for_each_entry(r, &dep->req_queued, list) {
+ list_for_each_entry(r, &dep->started_list, list) {
if (r == req)
break;
}
@@ -1313,10 +1320,10 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (value) {
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
- (!list_empty(&dep->req_queued) ||
- !list_empty(&dep->request_list)))) {
+ (!list_empty(&dep->started_list) ||
+ !list_empty(&dep->pending_list)))) {
dwc3_trace(trace_dwc3_gadget,
- "%s: pending request, cannot halt\n",
+ "%s: pending request, cannot halt",
dep->name);
return -EAGAIN;
}
@@ -1329,8 +1336,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
- DWC3_DEPCMD_CLEARSTALL, ¶ms);
+ ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
dev_err(dwc->dev, "failed to clear STALL on %s\n",
dep->name);
@@ -1417,22 +1423,16 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return DWC3_DSTS_SOFFN(reg);
}
-static int dwc3_gadget_wakeup(struct usb_gadget *g)
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
-
unsigned long timeout;
- unsigned long flags;
+ int ret;
u32 reg;
- int ret = 0;
-
u8 link_state;
u8 speed;
- spin_lock_irqsave(&dwc->lock, flags);
-
/*
* According to the Databook Remote wakeup request should
* be issued only when the device is in early suspend state.
@@ -1445,8 +1445,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
if ((speed == DWC3_DSTS_SUPERSPEED) ||
(speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
link_state = DWC3_DSTS_USBLNKST(reg);
@@ -1459,14 +1458,13 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
dwc3_trace(trace_dwc3_gadget,
"can't wakeup from '%s'\n",
dwc3_gadget_link_string(link_state));
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
dev_err(dwc->dev, "failed to put link in Recovery\n");
- goto out;
+ return ret;
}
/* Recent versions do this automatically */
@@ -1490,10 +1488,20 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
dev_err(dwc->dev, "failed to send remote wakeup\n");
- ret = -EINVAL;
+ return -EINVAL;
}
-out:
+ return 0;
+}
+
+static int dwc3_gadget_wakeup(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_wakeup(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1620,7 +1628,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
- IRQF_SHARED, "dwc3", dwc);
+ IRQF_SHARED, "dwc3", dwc->ev_buf);
if (ret) {
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
irq, ret);
@@ -1682,6 +1690,17 @@ static int dwc3_gadget_start(struct usb_gadget *g,
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ /*
+ * We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP
+ * field instead of letting dwc3 itself calculate that automatically.
+ *
+ * This way, we maximize the chances that we'll be able to get several
+ * bursts of data without going through any sort of endpoint throttling.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -1720,7 +1739,7 @@ err2:
err1:
spin_unlock_irqrestore(&dwc->lock, flags);
- free_irq(irq, dwc);
+ free_irq(irq, dwc->ev_buf);
err0:
return ret;
@@ -1743,7 +1762,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
spin_unlock_irqrestore(&dwc->lock, flags);
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- free_irq(irq, dwc);
+ free_irq(irq, dwc->ev_buf);
return 0;
}
@@ -1815,8 +1834,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.caps.dir_in = !!direction;
dep->endpoint.caps.dir_out = !direction;
- INIT_LIST_HEAD(&dep->request_list);
- INIT_LIST_HEAD(&dep->req_queued);
+ INIT_LIST_HEAD(&dep->pending_list);
+ INIT_LIST_HEAD(&dep->started_list);
}
return 0;
@@ -1913,11 +1932,11 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
* If there are still queued request
* then wait, do not issue either END
* or UPDATE TRANSFER, just attach next
- * request in request_list during
+ * request in pending_list during
* giveback.If any future queued request
* is successfully transferred then we
* will issue UPDATE TRANSFER for all
- * request in the request_list.
+ * request in the pending_list.
*/
dep->flags |= DWC3_EP_MISSED_ISOC;
} else {
@@ -1963,15 +1982,14 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
int ret;
do {
- req = next_request(&dep->req_queued);
+ req = next_request(&dep->started_list);
if (WARN_ON_ONCE(!req))
return 1;
i = 0;
do {
- slot = req->start_slot + i;
- if ((slot == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ slot = req->first_trb_index + i;
+ if (slot == DWC3_TRB_NUM - 1)
slot++;
slot %= DWC3_TRB_NUM;
trb = &dep->trb_pool[slot];
@@ -1989,8 +2007,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
} while (1);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- list_empty(&dep->req_queued)) {
- if (list_empty(&dep->request_list)) {
+ list_empty(&dep->started_list)) {
+ if (list_empty(&dep->pending_list)) {
/*
* If there is no entry in request list then do
* not issue END TRANSFER now. Just set PENDING
@@ -2039,7 +2057,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
- if (!list_empty(&dep->req_queued))
+ if (!list_empty(&dep->started_list))
return;
}
@@ -2250,7 +2268,6 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
struct dwc3_ep *dep;
- struct dwc3_gadget_ep_cmd_params params;
int ret;
dep = dwc->eps[epnum];
@@ -2262,9 +2279,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
dep->flags &= ~DWC3_EP_STALL;
- memset(¶ms, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
- DWC3_DEPCMD_CLEARSTALL, ¶ms);
+ ret = dwc3_send_clear_stall_ep_cmd(dep);
WARN_ON_ONCE(ret);
}
}
@@ -2686,14 +2701,13 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
}
}
-static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
+static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
{
- struct dwc3_event_buffer *evt;
+ struct dwc3 *dwc = evt->dwc;
irqreturn_t ret = IRQ_NONE;
int left;
u32 reg;
- evt = dwc->ev_buffs[buf];
left = evt->count;
if (!(evt->flags & DWC3_EVENT_PENDING))
@@ -2718,7 +2732,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
left -= 4;
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4);
}
evt->count = 0;
@@ -2726,39 +2740,34 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
ret = IRQ_HANDLED;
/* Unmask interrupt */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
reg &= ~DWC3_GEVNTSIZ_INTMASK;
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
return ret;
}
-static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
+static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
{
- struct dwc3 *dwc = _dwc;
+ struct dwc3_event_buffer *evt = _evt;
+ struct dwc3 *dwc = evt->dwc;
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
- int i;
spin_lock_irqsave(&dwc->lock, flags);
-
- for (i = 0; i < dwc->num_event_buffers; i++)
- ret |= dwc3_process_event_buf(dwc, i);
-
+ ret = dwc3_process_event_buf(evt);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
}
-static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
+static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
{
- struct dwc3_event_buffer *evt;
+ struct dwc3 *dwc = evt->dwc;
u32 count;
u32 reg;
- evt = dwc->ev_buffs[buf];
-
- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
+ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
return IRQ_NONE;
@@ -2767,28 +2776,18 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
evt->flags |= DWC3_EVENT_PENDING;
/* Mask interrupt */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
reg |= DWC3_GEVNTSIZ_INTMASK;
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
return IRQ_WAKE_THREAD;
}
-static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
+static irqreturn_t dwc3_interrupt(int irq, void *_evt)
{
- struct dwc3 *dwc = _dwc;
- int i;
- irqreturn_t ret = IRQ_NONE;
+ struct dwc3_event_buffer *evt = _evt;
- for (i = 0; i < dwc->num_event_buffers; i++) {
- irqreturn_t status;
-
- status = dwc3_check_event_buf(dwc, i);
- if (status == IRQ_WAKE_THREAD)
- ret = status;
- }
-
- return ret;
+ return dwc3_check_event_buf(evt);
}
/**
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 18ae3eaa8b6f..f21c0fccbebd 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -68,12 +68,12 @@ static inline struct dwc3_request *next_request(struct list_head *list)
return list_first_entry(list, struct dwc3_request, list);
}
-static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
+static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
{
struct dwc3_ep *dep = req->dep;
- req->queued = true;
- list_move_tail(&req->list, &dep->req_queued);
+ req->started = true;
+ list_move_tail(&req->list, &dep->started_list);
}
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
index 2bb4d3ad0e6b..8826cca5fc6f 100644
--- a/drivers/usb/dwc3/platform_data.h
+++ b/drivers/usb/dwc3/platform_data.h
@@ -23,7 +23,6 @@
struct dwc3_platform_data {
enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode;
- bool tx_fifo_resize;
bool usb3_lpm_capable;
unsigned is_utmi_l1_suspend:1;
@@ -43,6 +42,7 @@ struct dwc3_platform_data {
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1;
+ unsigned dis_rxdet_inp3_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 11/64] usb: dwc3: import from kernel v4.8
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (9 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 10/64] usb: dwc3: import from kernel v4.7 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 12/64] usb: dwc3: import from kernel v4.9 Jens Wiklander
` (56 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.7 to v4.8.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.7..v4.8
Commits imported:
696118c016dd usb: dwc3: pci: fix build warning on !PM_SLEEP
9d7aba7786b6 Revert "usb: dwc3: gadget: always decrement by 1"
6f8245b4e37c usb: dwc3: gadget: always decrement by 1
696fe69d7e63 usb: dwc3: debug: fix ep name on trace output
d6011f6fc21b usb: dwc3: gadget: don't rely on jiffies while holding spinlock
b74c2d875baa usb: dwc3: core: allow device to runtime_suspend several times
f6c274e11e3b usb: dwc3: pci: runtime_resume child device
a0ad85ae866f usb: dwc3: gadget: stop processing on HWO set
79d17482a409 usb: dwc3: don't set last bit for ISOC endpoints
4c4f106c032f usb: dwc3: fix missing platform_set_drvdata() in dwc3_of_simple_probe()
4491ed5042f0 usb: dwc3: pci: add Intel Kabylake PCI ID
7c705dfe2ebe usb: dwc3: gadget: always cleanup all TRBs
e5b36ae2f851 usb: dwc3: gadget: fix for short pkts during chained xfers
c7de57347183 usb: dwc3: gadget: increment request->actual once
c318a821b982 Merge 4.7-rc6 into usb-next
322832f2f19e usb: dwc3: host: Fix broken XHCI host
215db9481814 Merge tag 'usb-for-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
95b57df45062 usb: dwc3: host: use build-in property instead of platform data
72704f876f50 dwc3: gadget: Implement the suspend entry event handler
9522def40065 usb: dwc3: core: cleanup IRQ resources
da1410be21bf usb: dwc3: gadget: Add the suspend state checking when stopping gadget
0e146028eebf usb: dwc3: gadget: issue ENDTRANSFER conditional on resource_index
328082376aea usb: dwc3: fix runtime PM in error path
b6d4e16e8313 usb: dwc3: gadget: simplify run_stop() break condition
f2df679b6c55 usb: dwc3: gadget: avoid while(1) in run_stop()
d807bdd02845 usb: dwc3: gadget: remove udelay() from run_stop()
5f82279a0c76 usb: dwc3: core: fixup dr_mode fallback selection
e6fe66fe08cd usb: dwc3: pci: add dr-mode for Intel dwc3
21e64bf20df5 usb: dwc3: gadget: rename 'ignore' argument to 'modify'
bc71c2df45e5 Merge 4.7-rc4 into usb-next
d6dc2e76a860 usb: dwc3: gadget: decrement trbs_left for each sg entry
ba62c09d5cc2 usb: dwc3: gadget: start Bulk endpoints more frequently
13fa2e69b1dd usb: dwc3: gadget: disable XFER_NOT_READY
55a0237f8f47 usb: dwc3: gadget: use allocated/queued reqs for LST bit
69450c4dc164 usb: dwc3: gadget: halt and stop based HWO bit
68d34c8a744d usb: dwc3: gadget: keep track of allocated and queued reqs
b6b1c6db4c7f usb: dwc3: gadget: update transfer needs transfer resource
d18e65470a4d usb: dwc3: remove handling of platform data
cf48305de0c6 usb: dwc3: pci: use build-in properties instead of platform data
60cfb37ac9bc usb: dwc3: remove trailing newline from dwc3_trace
d2728fb3e01f usb: dwc3: omap: Pass VBUS and ID events transparently
9ab330bf4dfd usb: dwc3: omap: Don't set POWERPRESENT
12da8eae208d usb: dwc3: omap: Mark the interrupt handler as shared
3f586c92d877 usb: dwc3: omap: use request_threaded_irq()
475c8beb35e1 usb: dwc3: add DWC3_GUCTL1 reg for debug
e77c561432bc usb: dwc3: Fix DWC3_USB31_REVISION_110A definition
2da9ad761e70 usb: dwc3: Use the correct speed macros for DSTS/DCFG
3de2685f0c39 usb: dwc3: gadget: Fix truncated cast issue
361572b5f7a9 usb: dwc3: gadget: Handle TRB index 0 when full or empty
7d0a038b130c usb: dwc3: gadget: Account for link TRB in TRBs left
32db3d9437b6 usb: dwc3: gadget: Account for max size in TRB space
89bc856e5a74 usb: dwc3: gadget: Don't prepare TRBs if no space
0d25744ad107 usb: dwc3: gadget: Initialize the TRB ring
dca0119c3ab6 usb: dwc3: gadget: Simplify skipping of link TRBs
96bedb637a1e usb: dwc3: Endianness issue on dwc3_log_ctrl
d07fa665c79d usb: dwc3: gadget: Fix usage of bitwise operator
501058edebf9 usb: dwc3: ep0: Use the correct type for SET_SEL data
958b9fa7f8cf usb: dwc3: ep0: Fix endianness of wIndex passed to dwc3_wIndex_to_dep
71f7e7027028 usb: dwc3: gadget: improve gcmd trace
88811f7b722c usb: dwc3: gadget: remove udelay() from generic cmd
0fe886cdb07a usb: dwc3: gadget: single return point on generic commands
0933df159c5c usb: dwc3: trace: print ep cmd status with a single trace
f6bb225bb3ca usb: dwc3: gadget: loop while (timeout)
ba1598410eff usb: dwc3: gadget: fix trace output when command fails
f75cacc468ed usb: dwc3: trace: fully decode IRQ events
7ab373aadbd0 usb: dwc3: gadget: no more tracking endpoint type with its name
22f2c619a116 usb: dwc3: trace: pretty-print TRB's ctrl field
74674cbf858f usb: dwc3: gadget: add a per-endpoint request queue lock
e9af9229098d usb: dwc3: pci: add Power Management dummy hooks
fc8bb91bc83e usb: dwc3: implement runtime PM
4cb4221764ef usb: dwc3: gadget: fix for possible endpoint disable race
9cad39fe4e4a usb: dwc3: fix for the isoc transfer EP_BUSY flag
ab2a92e7a608 usb: dwc3: gadget: only resume USB2 PHY in <=HIGHSPEED
3f308d17d7ab usb: dwc3: gadget: hold gadget IRQ in dwc->irq_gadget
51f5d49ad6f0 usb: dwc3: core: simplify suspend/resume operations
c499ff71ff2a usb: dwc3: core: re-factor init and exit paths
bcdb3272e889 usb: dwc3: core: move fladj to dwc3 structure
2eb8801650b3 usb: dwc3: gadget: add a pointer to endpoint registers
2cd4718d0bbe usb: dwc3: gadget: pass dep as argument to endpoint command
4e99472bc10b usb: dwc3: gadget: initialize NUMP based on RxFIFO Size
5ee85d890f8d usb: dwc3: gadget: split __dwc3_gadget_kick_transfer()
6b74289937f6 usb: dwc3: gadget: return 0 if we try to Wakeup in superspeed
b43bba96b903 usb: dwc3: gadget: remove udelay(1) when sending ep cmds
6aff48329595 usb: dwc3: gadget: rely on sg_is_last() and list_is_last()
4fae2e3e1515 usb: dwc3: gadget: simplify __dwc3_gadget_kick_transfer()
c4233573f6ee usb: dwc3: gadget: prepare TRBs on update transfers too
7f370ed0cfe9 usb: dwc3: core: get rid of DWC3_PM_OPS macro
9f8a67b65a49 usb: dwc3: gadget: fix gadget suspend/resume
d7be295243bb usb: dwc3: gadget: re-factor ->udc_start and ->udc_stop
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 389 +++++++------
drivers/usb/dwc3/core.h | 59 +-
drivers/usb/dwc3/debug.h | 140 ++++-
drivers/usb/dwc3/ep0.c | 26 +-
drivers/usb/dwc3/gadget.c | 924 ++++++++++++++++++-------------
drivers/usb/dwc3/gadget.h | 4 +-
drivers/usb/dwc3/io.h | 7 +-
drivers/usb/dwc3/platform_data.h | 53 --
8 files changed, 926 insertions(+), 676 deletions(-)
delete mode 100644 drivers/usb/dwc3/platform_data.h
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a590cd225bb7..35d092456bec 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -41,14 +41,13 @@
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
-#include "platform_data.h"
#include "core.h"
#include "gadget.h"
#include "io.h"
#include "debug.h"
-/* -------------------------------------------------------------------------- */
+#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
@@ -149,9 +148,8 @@ static int dwc3_soft_reset(struct dwc3 *dwc)
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
- * @fladj: Value of GFLADJ_30MHZ to adjust frame length
*/
-static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
+static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
{
u32 reg;
u32 dft;
@@ -159,15 +157,15 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
if (dwc->revision < DWC3_REVISION_250A)
return;
- if (fladj == 0)
+ if (dwc->fladj == 0)
return;
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
- if (!dev_WARN_ONCE(dwc->dev, dft == fladj,
+ if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
"request value same as default, ignoring\n")) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
- reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj;
+ reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
}
}
@@ -507,6 +505,21 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
return 0;
}
+static void dwc3_core_exit(struct dwc3 *dwc)
+{
+ dwc3_event_buffers_cleanup(dwc);
+
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
+}
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -556,6 +569,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err0;
+ ret = dwc3_phy_setup(dwc);
+ if (ret)
+ goto err0;
+
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
@@ -622,22 +639,45 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->revision < DWC3_REVISION_190A)
reg |= DWC3_GCTL_U2RSTECN;
- dwc3_core_num_eps(dwc);
-
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
- ret = dwc3_alloc_scratch_buffers(dwc);
- if (ret)
- goto err1;
+ dwc3_core_num_eps(dwc);
ret = dwc3_setup_scratch_buffers(dwc);
if (ret)
+ goto err1;
+
+ /* Adjust Frame Length */
+ dwc3_frame_length_adjustment(dwc);
+
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+ ret = phy_power_on(dwc->usb2_generic_phy);
+ if (ret < 0)
goto err2;
+ ret = phy_power_on(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err3;
+
+ ret = dwc3_event_buffers_setup(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
+ goto err4;
+ }
+
return 0;
+err4:
+ phy_power_off(dwc->usb2_generic_phy);
+
+err3:
+ phy_power_off(dwc->usb3_generic_phy);
+
err2:
- dwc3_free_scratch_buffers(dwc);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ dwc3_core_exit(dwc);
err1:
usb_phy_shutdown(dwc->usb2_phy);
@@ -649,15 +689,6 @@ err0:
return ret;
}
-static void dwc3_core_exit(struct dwc3 *dwc)
-{
- dwc3_free_scratch_buffers(dwc);
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-}
-
static int dwc3_core_get_phy(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@@ -735,7 +766,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize gadget\n");
return ret;
}
break;
@@ -743,7 +775,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize host\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize host\n");
return ret;
}
break;
@@ -751,13 +784,15 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize host\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize host\n");
return ret;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize gadget\n");
return ret;
}
break;
@@ -793,13 +828,11 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct dwc3_platform_data *pdata = dev_get_platdata(dev);
struct resource *res;
struct dwc3 *dwc;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
- u32 fladj = 0;
int ret;
@@ -814,16 +847,6 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->mem = mem;
dwc->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "missing IRQ\n");
- return -ENODEV;
- }
- dwc->xhci_resources[1].start = res->start;
- dwc->xhci_resources[1].end = res->end;
- dwc->xhci_resources[1].flags = res->flags;
- dwc->xhci_resources[1].name = res->name;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
@@ -909,40 +932,7 @@ static int dwc3_probe(struct platform_device *pdev)
device_property_read_string(dev, "snps,hsphy_interface",
&dwc->hsphy_interface);
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
- &fladj);
-
- if (pdata) {
- dwc->maximum_speed = pdata->maximum_speed;
- dwc->has_lpm_erratum = pdata->has_lpm_erratum;
- if (pdata->lpm_nyet_threshold)
- lpm_nyet_threshold = pdata->lpm_nyet_threshold;
- dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
- if (pdata->hird_threshold)
- hird_threshold = pdata->hird_threshold;
-
- dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
- dwc->dr_mode = pdata->dr_mode;
-
- dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
- dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
- dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
- dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
- dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
- dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
- dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
- dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
- dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
- dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
- dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk;
- dwc->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_quirk;
-
- dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
- if (pdata->tx_de_emphasis)
- tx_de_emphasis = pdata->tx_de_emphasis;
-
- dwc->hsphy_interface = pdata->hsphy_interface;
- fladj = pdata->fladj_value;
- }
+ &dwc->fladj);
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -953,10 +943,6 @@ static int dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
- ret = dwc3_phy_setup(dwc);
- if (ret)
- goto err0;
-
ret = dwc3_core_get_phy(dwc);
if (ret)
goto err0;
@@ -969,29 +955,43 @@ static int dwc3_probe(struct platform_device *pdev)
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
}
+ pm_runtime_set_active(dev);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
pm_runtime_enable(dev);
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err1;
+
pm_runtime_forbid(dev);
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
- goto err1;
+ goto err2;
}
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST) &&
+ (dwc->dr_mode == USB_DR_MODE_OTG ||
+ dwc->dr_mode == USB_DR_MODE_UNKNOWN))
dwc->dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
+ (dwc->dr_mode == USB_DR_MODE_OTG ||
+ dwc->dr_mode == USB_DR_MODE_UNKNOWN))
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
dwc->dr_mode = USB_DR_MODE_OTG;
+ ret = dwc3_alloc_scratch_buffers(dwc);
+ if (ret)
+ goto err3;
+
ret = dwc3_core_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize core\n");
- goto err1;
+ goto err4;
}
/* Check the maximum_speed parameter */
@@ -1021,31 +1021,12 @@ static int dwc3_probe(struct platform_device *pdev)
break;
}
- /* Adjust Frame Length */
- dwc3_frame_length_adjustment(dwc, fladj);
-
- usb_phy_set_suspend(dwc->usb2_phy, 0);
- usb_phy_set_suspend(dwc->usb3_phy, 0);
- ret = phy_power_on(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err2;
-
- ret = phy_power_on(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err3;
-
- ret = dwc3_event_buffers_setup(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err4;
- }
-
ret = dwc3_core_init_mode(dwc);
if (ret)
goto err5;
dwc3_debugfs_init(dwc);
- pm_runtime_allow(dev);
+ pm_runtime_put(dev);
return 0;
@@ -1053,19 +1034,18 @@ err5:
dwc3_event_buffers_cleanup(dwc);
err4:
- phy_power_off(dwc->usb3_generic_phy);
+ dwc3_free_scratch_buffers(dwc);
err3:
- phy_power_off(dwc->usb2_generic_phy);
+ dwc3_free_event_buffers(dwc);
+ dwc3_ulpi_exit(dwc);
err2:
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- dwc3_core_exit(dwc);
+ pm_runtime_allow(&pdev->dev);
err1:
- dwc3_free_event_buffers(dwc);
- dwc3_ulpi_exit(dwc);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
err0:
/*
@@ -1083,6 +1063,7 @@ static int dwc3_remove(struct platform_device *pdev)
struct dwc3 *dwc = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pm_runtime_get_sync(&pdev->dev);
/*
* restore res->start back to its original value so that, in case the
* probe is deferred, we don't end up getting error in request the
@@ -1092,133 +1073,193 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
- dwc3_event_buffers_cleanup(dwc);
- dwc3_free_event_buffers(dwc);
-
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- phy_power_off(dwc->usb2_generic_phy);
- phy_power_off(dwc->usb3_generic_phy);
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ dwc3_free_event_buffers(dwc);
+ dwc3_free_scratch_buffers(dwc);
+
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int dwc3_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int dwc3_suspend_common(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
- spin_lock_irqsave(&dwc->lock, flags);
-
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
+ spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
- /* FALLTHROUGH */
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
case USB_DR_MODE_HOST:
default:
- dwc3_event_buffers_cleanup(dwc);
+ /* do nothing */
break;
}
- dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_core_exit(dwc);
- usb_phy_shutdown(dwc->usb3_phy);
- usb_phy_shutdown(dwc->usb2_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
+ return 0;
+}
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0);
- WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0);
+static int dwc3_resume_common(struct dwc3 *dwc)
+{
+ unsigned long flags;
+ int ret;
- pinctrl_pm_select_sleep_state(dev);
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_resume(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ /* FALLTHROUGH */
+ case USB_DR_MODE_HOST:
+ default:
+ /* do nothing */
+ break;
+ }
return 0;
}
-static int dwc3_resume(struct device *dev)
+static int dwc3_runtime_checks(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
- unsigned long flags;
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
+ if (dwc->connected)
+ return -EBUSY;
+ break;
+ case USB_DR_MODE_HOST:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ return 0;
+}
+
+static int dwc3_runtime_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
- pinctrl_pm_select_default_state(dev);
+ if (dwc3_runtime_checks(dwc))
+ return -EBUSY;
- usb_phy_set_suspend(dwc->usb2_phy, 0);
- usb_phy_set_suspend(dwc->usb3_phy, 0);
- ret = phy_power_on(dwc->usb2_generic_phy);
- if (ret < 0)
+ ret = dwc3_suspend_common(dwc);
+ if (ret)
return ret;
- ret = phy_power_on(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_usb2phy_power;
+ device_init_wakeup(dev, true);
- usb_phy_init(dwc->usb3_phy);
- usb_phy_init(dwc->usb2_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err_usb3phy_power;
+ return 0;
+}
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_usb2phy_init;
+static int dwc3_runtime_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ int ret;
- spin_lock_irqsave(&dwc->lock, flags);
+ device_init_wakeup(dev, false);
- dwc3_event_buffers_setup(dwc);
- dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
+ ret = dwc3_resume_common(dwc);
+ if (ret)
+ return ret;
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
- dwc3_gadget_resume(dwc);
- /* FALLTHROUGH */
+ dwc3_gadget_process_pending_events(dwc);
+ break;
case USB_DR_MODE_HOST:
default:
/* do nothing */
break;
}
- spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put(dev);
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ return 0;
+}
+
+static int dwc3_runtime_idle(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
+ if (dwc3_runtime_checks(dwc))
+ return -EBUSY;
+ break;
+ case USB_DR_MODE_HOST:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_autosuspend(dev);
return 0;
+}
+#endif /* CONFIG_PM */
-err_usb2phy_init:
- phy_exit(dwc->usb2_generic_phy);
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ int ret;
-err_usb3phy_power:
- phy_power_off(dwc->usb3_generic_phy);
+ ret = dwc3_suspend_common(dwc);
+ if (ret)
+ return ret;
-err_usb2phy_power:
- phy_power_off(dwc->usb2_generic_phy);
+ pinctrl_pm_select_sleep_state(dev);
- return ret;
+ return 0;
}
+static int dwc3_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ int ret;
+
+ pinctrl_pm_select_default_state(dev);
+
+ ret = dwc3_resume_common(dwc);
+ if (ret)
+ return ret;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
static const struct dev_pm_ops dwc3_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+ SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
+ dwc3_runtime_idle)
};
-#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
-#else
-#define DWC3_PM_OPS NULL
-#endif
-
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
@@ -1250,7 +1291,7 @@ static struct platform_driver dwc3_driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
.acpi_match_table = ACPI_PTR(dwc3_acpi_match),
- .pm = DWC3_PM_OPS,
+ .pm = &dwc3_dev_pm_ops,
},
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 654050684f4f..45d6de5107c7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -86,6 +86,7 @@
#define DWC3_GCTL 0xc110
#define DWC3_GEVTEN 0xc114
#define DWC3_GSTS 0xc118
+#define DWC3_GUCTL1 0xc11c
#define DWC3_GSNPSID 0xc120
#define DWC3_GGPIO 0xc124
#define DWC3_GUID 0xc128
@@ -138,10 +139,12 @@
#define DWC3_DGCMDPAR 0xc710
#define DWC3_DGCMD 0xc714
#define DWC3_DALEPENA 0xc720
-#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10))
-#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10))
-#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
-#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
+
+#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10))
+#define DWC3_DEPCMDPAR2 0x00
+#define DWC3_DEPCMDPAR1 0x04
+#define DWC3_DEPCMDPAR0 0x08
+#define DWC3_DEPCMD 0x0c
/* OTG Registers */
#define DWC3_OCFG 0xcc00
@@ -231,6 +234,14 @@
#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
+/* Global HWPARAMS0 Register */
+#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
+#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
+#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
+#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff)
+#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff)
+
/* Global HWPARAMS1 Register */
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
@@ -260,6 +271,10 @@
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
+/* Global HWPARAMS7 Register */
+#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
+#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
+
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
@@ -468,6 +483,8 @@ struct dwc3_event_buffer {
* @endpoint: usb endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
+ * @lock: spinlock for endpoint request queue traversal
+ * @regs: pointer to first endpoint register
* @trb_pool: array of transaction buffers
* @trb_pool_dma: dma address of @trb_pool
* @trb_enqueue: enqueue 'pointer' into TRB array
@@ -480,6 +497,8 @@ struct dwc3_event_buffer {
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
* @interval: the interval on which the ISOC transfer is started
+ * @allocated_requests: number of requests allocated
+ * @queued_requests: number of requests queued for transfer
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
* @stream_capable: true when streams are enabled
@@ -489,6 +508,9 @@ struct dwc3_ep {
struct list_head pending_list;
struct list_head started_list;
+ spinlock_t lock;
+ void __iomem *regs;
+
struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma;
const struct usb_ss_ep_comp_descriptor *comp_desc;
@@ -521,6 +543,8 @@ struct dwc3_ep {
u8 number;
u8 type;
u8 resource_index;
+ u32 allocated_requests;
+ u32 queued_requests;
u32 interval;
char name[20];
@@ -712,6 +736,8 @@ struct dwc3_scratchpad_array {
* @gadget_driver: pointer to the gadget driver
* @regs: base address for our registers
* @regs_size: address space size
+ * @fladj: frame length adjustment
+ * @irq_gadget: peripheral controller's IRQ number
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -744,6 +770,7 @@ struct dwc3_scratchpad_array {
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
* @hsphy_interface: "utmi" or "ulpi"
+ * @connected: true when we're connected to a host, false otherwise
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
@@ -754,6 +781,7 @@ struct dwc3_scratchpad_array {
* 0 - utmi_sleep_n
* 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
+ * @pending_events: true when we have pending IRQs to be handled
* @pullups_connected: true when Run/Stop bit is set
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
@@ -818,10 +846,8 @@ struct dwc3 {
enum usb_dr_mode dr_mode;
- /* used for suspend/resume */
- u32 dcfg;
- u32 gctl;
-
+ u32 fladj;
+ u32 irq_gadget;
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
@@ -860,7 +886,7 @@ struct dwc3 {
* just so dwc31 revisions are always larger than dwc3.
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
-#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31)
+#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -890,6 +916,7 @@ struct dwc3 {
const char *hsphy_interface;
+ unsigned connected:1;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
@@ -897,6 +924,7 @@ struct dwc3 {
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
+ unsigned pending_events:1;
unsigned pullups_connected:1;
unsigned setup_packet_pending:1;
unsigned three_stage_setup:1;
@@ -1094,8 +1122,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
-int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
- unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+ struct dwc3_gadget_ep_cmd_params *params);
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
@@ -1110,8 +1138,8 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
enum dwc3_link_state state)
{ return 0; }
-static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
- unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+ struct dwc3_gadget_ep_cmd_params *params)
{ return 0; }
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
int cmd, u32 param)
@@ -1122,6 +1150,7 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_suspend(struct dwc3 *dwc);
int dwc3_gadget_resume(struct dwc3 *dwc);
+void dwc3_gadget_process_pending_events(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
{
@@ -1132,6 +1161,10 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
{
return 0;
}
+
+static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
+{
+}
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 71e318025964..33ab2a203c1b 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -128,56 +128,112 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
* dwc3_gadget_event_string - returns event name
* @event: the event code
*/
-static inline const char *dwc3_gadget_event_string(u8 event)
+static inline const char *
+dwc3_gadget_event_string(const struct dwc3_event_devt *event)
{
- switch (event) {
+ static char str[256];
+ enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
+
+ switch (event->type) {
case DWC3_DEVICE_EVENT_DISCONNECT:
- return "Disconnect";
+ sprintf(str, "Disconnect: [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_RESET:
- return "Reset";
+ sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_CONNECT_DONE:
- return "Connection Done";
+ sprintf(str, "Connection Done [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
- return "Link Status Change";
+ sprintf(str, "Link Change [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_WAKEUP:
- return "WakeUp";
+ sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_EOPF:
- return "End-Of-Frame";
+ sprintf(str, "End-Of-Frame [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_SOF:
- return "Start-Of-Frame";
+ sprintf(str, "Start-Of-Frame [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- return "Erratic Error";
+ sprintf(str, "Erratic Error [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- return "Command Complete";
+ sprintf(str, "Command Complete [%s]",
+ dwc3_gadget_link_string(state));
+ break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- return "Overflow";
+ sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state));
+ break;
+ default:
+ sprintf(str, "UNKNOWN");
}
- return "UNKNOWN";
+ return str;
}
/**
* dwc3_ep_event_string - returns event name
* @event: then event code
*/
-static inline const char *dwc3_ep_event_string(u8 event)
+static inline const char *
+dwc3_ep_event_string(const struct dwc3_event_depevt *event)
{
- switch (event) {
+ u8 epnum = event->endpoint_number;
+ static char str[256];
+ int status;
+ int ret;
+
+ ret = sprintf(str, "ep%d%s: ", epnum >> 1,
+ (epnum & 1) ? "in" : "out");
+ if (ret < 0)
+ return "UNKNOWN";
+
+ switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- return "Transfer Complete";
+ strcat(str, "Transfer Complete");
+ break;
case DWC3_DEPEVT_XFERINPROGRESS:
- return "Transfer In-Progress";
+ strcat(str, "Transfer In-Progress");
+ break;
case DWC3_DEPEVT_XFERNOTREADY:
- return "Transfer Not Ready";
+ strcat(str, "Transfer Not Ready");
+ status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
+ strcat(str, status ? " (Active)" : " (Not Active)");
+ break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- return "FIFO";
+ strcat(str, "FIFO");
+ break;
case DWC3_DEPEVT_STREAMEVT:
- return "Stream";
+ status = event->status;
+
+ switch (status) {
+ case DEPEVT_STREAMEVT_FOUND:
+ sprintf(str + ret, " Stream %d Found",
+ event->parameters);
+ break;
+ case DEPEVT_STREAMEVT_NOTFOUND:
+ default:
+ strcat(str, " Stream Not Found");
+ break;
+ }
+
+ break;
case DWC3_DEPEVT_EPCMDCMPLT:
- return "Endpoint Command Complete";
+ strcat(str, "Endpoint Command Complete");
+ break;
+ default:
+ sprintf(str, "UNKNOWN");
}
- return "UNKNOWN";
+ return str;
}
/**
@@ -214,6 +270,46 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
}
}
+static inline const char *dwc3_decode_event(u32 event)
+{
+ const union dwc3_event evt = (union dwc3_event) event;
+
+ if (evt.type.is_devspec)
+ return dwc3_gadget_event_string(&evt.devt);
+ else
+ return dwc3_ep_event_string(&evt.depevt);
+}
+
+static inline const char *dwc3_ep_cmd_status_string(int status)
+{
+ switch (status) {
+ case -ETIMEDOUT:
+ return "Timed Out";
+ case 0:
+ return "Successful";
+ case DEPEVT_TRANSFER_NO_RESOURCE:
+ return "No Resource";
+ case DEPEVT_TRANSFER_BUS_EXPIRY:
+ return "Bus Expiry";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
+{
+ switch (status) {
+ case -ETIMEDOUT:
+ return "Timed Out";
+ case 0:
+ return "Successful";
+ case 1:
+ return "Error";
+ default:
+ return "UNKNOWN";
+ }
+}
+
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 51b52a79dfec..fe79d771dee4 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -98,8 +98,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trace_dwc3_prepare_trb(dep, trb);
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
- DWC3_DEPCMD_STARTTRANSFER, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms);
if (ret < 0) {
dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
dep->name);
@@ -107,9 +106,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
}
dep->flags |= DWC3_EP_BUSY;
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
- dep->number);
-
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
return 0;
@@ -499,7 +496,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
case USB_RECIP_ENDPOINT:
switch (wValue) {
case USB_ENDPOINT_HALT:
- dep = dwc3_wIndex_to_dep(dwc, wIndex);
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
if (!dep)
return -EINVAL;
if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
@@ -622,8 +619,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
struct timing {
u8 u1sel;
u8 u1pel;
- u16 u2sel;
- u16 u2pel;
+ __le16 u2sel;
+ __le16 u2pel;
} __packed timing;
int ret;
@@ -980,7 +977,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
if (ret) {
- dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
+ dwc3_trace(trace_dwc3_ep0, "failed to map request");
return;
}
@@ -1008,7 +1005,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
if (ret) {
- dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
+ dwc3_trace(trace_dwc3_ep0, "failed to map request");
return;
}
@@ -1058,7 +1055,7 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
cmd |= DWC3_DEPCMD_CMDIOC;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(¶ms, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
}
@@ -1112,11 +1109,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- u8 epnum = event->endpoint_number;
-
- dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
- dwc3_ep_event_string(event->endpoint_event),
- epnum >> 1, (epnum & 1) ? "in" : "out",
+ dwc3_trace(trace_dwc3_ep0, "%s: state '%s'",
+ dwc3_ep_event_string(event),
dwc3_ep0_state_string(dwc->ep0state));
switch (event->endpoint_event) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 07248ff1be5c..122e64df2f4d 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -145,21 +145,29 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
return -ETIMEDOUT;
}
-static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
+/**
+ * dwc3_ep_inc_trb() - Increment a TRB index.
+ * @index - Pointer to the TRB index to increment.
+ *
+ * The index should never point to the link TRB. After incrementing,
+ * if it is point to the link TRB, wrap around to the beginning. The
+ * link TRB is always at the last TRB entry.
+ */
+static void dwc3_ep_inc_trb(u8 *index)
{
- dep->trb_enqueue++;
- dep->trb_enqueue %= DWC3_TRB_NUM;
+ (*index)++;
+ if (*index == (DWC3_TRB_NUM - 1))
+ *index = 0;
}
-static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
+static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
{
- dep->trb_dequeue++;
- dep->trb_dequeue %= DWC3_TRB_NUM;
+ dwc3_ep_inc_trb(&dep->trb_enqueue);
}
-static int dwc3_ep_is_last_trb(unsigned int index)
+static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
{
- return index == DWC3_TRB_NUM - 1;
+ dwc3_ep_inc_trb(&dep->trb_dequeue);
}
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
@@ -172,13 +180,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
i = 0;
do {
dwc3_ep_inc_deq(dep);
- /*
- * Skip LINK TRB. We can't use req->trb and check for
- * DWC3_TRBCTL_LINK_TRB because it points the TRB we
- * just completed (not the LINK TRB).
- */
- if (dwc3_ep_is_last_trb(dep->trb_dequeue))
- dwc3_ep_inc_deq(dep);
} while(++i < req->request.num_mapped_sgs);
req->started = false;
}
@@ -199,57 +200,54 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
+
+ if (dep->number > 1)
+ pm_runtime_put(dwc->dev);
}
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
{
u32 timeout = 500;
+ int status = 0;
+ int ret = 0;
u32 reg;
- trace_dwc3_gadget_generic_cmd(cmd, param);
-
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
do {
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) {
- dwc3_trace(trace_dwc3_gadget,
- "Command Complete --> %d",
- DWC3_DGCMD_STATUS(reg));
- if (DWC3_DGCMD_STATUS(reg))
- return -EINVAL;
- return 0;
+ status = DWC3_DGCMD_STATUS(reg);
+ if (status)
+ ret = -EINVAL;
+ break;
}
+ } while (timeout--);
- /*
- * We can't sleep here, because it's also called from
- * interrupt context.
- */
- timeout--;
- if (!timeout) {
- dwc3_trace(trace_dwc3_gadget,
- "Command Timed Out");
- return -ETIMEDOUT;
- }
- udelay(1);
- } while (1);
+ if (!timeout) {
+ ret = -ETIMEDOUT;
+ status = -ETIMEDOUT;
+ }
+
+ trace_dwc3_gadget_generic_cmd(cmd, param, status);
+
+ return ret;
}
static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
-int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
- unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+ struct dwc3_gadget_ep_cmd_params *params)
{
- struct dwc3_ep *dep = dwc->eps[ep];
+ struct dwc3 *dwc = dep->dwc;
u32 timeout = 500;
u32 reg;
+ int cmd_status = 0;
int susphy = false;
int ret = -EINVAL;
- trace_dwc3_gadget_ep_cmd(dep, cmd, params);
-
/*
* Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
* we're issuing an endpoint command, we must check if
@@ -258,11 +256,13 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
* We will also set SUSPHY bit to what it was before returning as stated
* by the same section on Synopsys databook.
*/
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
- susphy = true;
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ if (dwc->gadget.speed <= USB_SPEED_HIGH) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
+ susphy = true;
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
}
if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
@@ -279,26 +279,21 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
}
}
- dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
- dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
- dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
- dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT);
+ dwc3_writel(dep->regs, DWC3_DEPCMD, cmd | DWC3_DEPCMD_CMDACT);
do {
- reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
+ reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
if (!(reg & DWC3_DEPCMD_CMDACT)) {
- int cmd_status = DWC3_DEPCMD_STATUS(reg);
-
- dwc3_trace(trace_dwc3_gadget,
- "Command Complete --> %d",
- cmd_status);
+ cmd_status = DWC3_DEPCMD_STATUS(reg);
switch (cmd_status) {
case 0:
ret = 0;
break;
case DEPEVT_TRANSFER_NO_RESOURCE:
- dwc3_trace(trace_dwc3_gadget, "%s: no resource available");
ret = -EINVAL;
break;
case DEPEVT_TRANSFER_BUS_EXPIRY:
@@ -313,7 +308,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
* give a hint to the gadget driver that this is
* the case by returning -EAGAIN.
*/
- dwc3_trace(trace_dwc3_gadget, "%s: bus expiry");
ret = -EAGAIN;
break;
default:
@@ -322,21 +316,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
break;
}
+ } while (--timeout);
- /*
- * We can't sleep here, because it is also called from
- * interrupt context.
- */
- timeout--;
- if (!timeout) {
- dwc3_trace(trace_dwc3_gadget,
- "Command Timed Out");
- ret = -ETIMEDOUT;
- break;
- }
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ cmd_status = -ETIMEDOUT;
+ }
- udelay(1);
- } while (1);
+ trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
if (unlikely(susphy)) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@@ -366,7 +353,7 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
memset(¶ms, 0, sizeof(params));
- return dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
+ return dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
}
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
@@ -454,7 +441,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
memset(¶ms, 0x00, sizeof(params));
cmd = DWC3_DEPCMD_DEPSTARTCFG;
- ret = dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret)
return ret;
@@ -475,10 +462,14 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
const struct usb_ss_ep_comp_descriptor *comp_desc,
- bool ignore, bool restore)
+ bool modify, bool restore)
{
struct dwc3_gadget_ep_cmd_params params;
+ if (dev_WARN_ONCE(dwc->dev, modify && restore,
+ "Can't modify and restore\n"))
+ return -EINVAL;
+
memset(¶ms, 0x00, sizeof(params));
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
@@ -487,30 +478,22 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
/* Burst size is only needed in SuperSpeed mode */
if (dwc->gadget.speed >= USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst;
- u32 nump;
- u32 reg;
-
- /* update NumP */
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
- nump = DWC3_DCFG_NUMP(reg);
- nump = max(nump, burst);
- reg &= ~DWC3_DCFG_NUMP_MASK;
- reg |= nump << DWC3_DCFG_NUMP_SHIFT;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
-
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
- if (ignore)
- params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
-
- if (restore) {
+ if (modify) {
+ params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;
+ } else if (restore) {
params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
params.param2 |= dep->saved_state;
+ } else {
+ params.param0 |= DWC3_DEPCFG_ACTION_INIT;
}
- params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
- | DWC3_DEPCFG_XFER_NOT_READY_EN;
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
+
+ if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
+ params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
@@ -541,8 +524,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
dep->interval = 1 << (desc->bInterval - 1);
}
- return dwc3_send_gadget_ep_cmd(dwc, dep->number,
- DWC3_DEPCMD_SETEPCONFIG, ¶ms);
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
}
static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
@@ -553,8 +535,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
- return dwc3_send_gadget_ep_cmd(dwc, dep->number,
- DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms);
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
+ ¶ms);
}
/**
@@ -567,7 +549,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
const struct usb_ss_ep_comp_descriptor *comp_desc,
- bool ignore, bool restore)
+ bool modify, bool restore)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
@@ -581,7 +563,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, modify,
restore);
if (ret)
return ret;
@@ -600,38 +582,24 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
if (usb_endpoint_xfer_control(desc))
- goto out;
+ return 0;
+
+ /* Initialize the TRB ring */
+ dep->trb_dequeue = 0;
+ dep->trb_enqueue = 0;
+ memset(dep->trb_pool, 0,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
/* Link TRB. The HWO bit is never reset */
trb_st_hw = &dep->trb_pool[0];
trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
- memset(trb_link, 0, sizeof(*trb_link));
-
trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
}
-out:
- switch (usb_endpoint_type(desc)) {
- case USB_ENDPOINT_XFER_CONTROL:
- /* don't change name */
- break;
- case USB_ENDPOINT_XFER_ISOC:
- strlcat(dep->name, "-isoc", sizeof(dep->name));
- break;
- case USB_ENDPOINT_XFER_BULK:
- strlcat(dep->name, "-bulk", sizeof(dep->name));
- break;
- case USB_ENDPOINT_XFER_INT:
- strlcat(dep->name, "-int", sizeof(dep->name));
- break;
- default:
- dev_err(dwc->dev, "invalid endpoint transfer type\n");
- }
-
return 0;
}
@@ -640,15 +608,13 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
- if (!list_empty(&dep->started_list)) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
+ dwc3_stop_active_transfer(dwc, dep->number, true);
- /* - giveback all requests to gadget driver */
- while (!list_empty(&dep->started_list)) {
- req = next_request(&dep->started_list);
+ /* - giveback all requests to gadget driver */
+ while (!list_empty(&dep->started_list)) {
+ req = next_request(&dep->started_list);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
- }
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
while (!list_empty(&dep->pending_list)) {
@@ -689,10 +655,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->type = 0;
dep->flags = 0;
- snprintf(dep->name, sizeof(dep->name), "ep%d%s",
- dep->number >> 1,
- (dep->number & 1) ? "in" : "out");
-
return 0;
}
@@ -784,6 +746,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
req->epnum = dep->number;
req->dep = dep;
+ dep->allocated_requests++;
+
trace_dwc3_alloc_request(req);
return &req->request;
@@ -793,7 +757,9 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request)
{
struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ dep->allocated_requests--;
trace_dwc3_free_request(req);
kfree(req);
}
@@ -825,9 +791,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
}
dwc3_ep_inc_enq(dep);
- /* Skip the LINK-TRB */
- if (dwc3_ep_is_last_trb(dep->trb_enqueue))
- dwc3_ep_inc_enq(dep);
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
@@ -866,7 +829,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
if (!req->request.no_interrupt && !chain)
trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
- if (last)
+ if (last && !usb_endpoint_xfer_isoc(dep->endpoint.desc))
trb->ctrl |= DWC3_TRB_CTRL_LST;
if (chain)
@@ -877,137 +840,169 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl |= DWC3_TRB_CTRL_HWO;
+ dep->queued_requests++;
+
trace_dwc3_prepare_trb(dep, trb);
}
-/*
- * dwc3_prepare_trbs - setup TRBs from requests
- * @dep: endpoint for which requests are being prepared
- * @starting: true if the endpoint is idle and no requests are queued.
+/**
+ * dwc3_ep_prev_trb() - Returns the previous TRB in the ring
+ * @dep: The endpoint with the TRB ring
+ * @index: The index of the current TRB in the ring
*
- * The function goes through the requests list and sets up TRBs for the
- * transfers. The function returns once there are no more TRBs available or
- * it runs out of requests.
+ * Returns the TRB prior to the one pointed to by the index. If the
+ * index is 0, we will wrap backwards, skip the link TRB, and return
+ * the one just before that.
*/
-static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
+static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
{
- struct dwc3_request *req, *n;
- u32 trbs_left;
- unsigned int last_one = 0;
+ if (!index)
+ index = DWC3_TRB_NUM - 2;
+ else
+ index = dep->trb_enqueue - 1;
- BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
+ return &dep->trb_pool[index];
+}
- trbs_left = dep->trb_dequeue - dep->trb_enqueue;
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
+{
+ struct dwc3_trb *tmp;
+ u8 trbs_left;
/*
- * If enqueue & dequeue are equal than it is either full or empty. If we
- * are starting to process requests then we are empty. Otherwise we are
- * full and don't do anything
+ * If enqueue & dequeue are equal than it is either full or empty.
+ *
+ * One way to know for sure is if the TRB right before us has HWO bit
+ * set or not. If it has, then we're definitely full and can't fit any
+ * more transfers in our ring.
*/
- if (!trbs_left) {
- if (!starting)
- return;
+ if (dep->trb_enqueue == dep->trb_dequeue) {
+ tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+ if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ return 0;
- trbs_left = DWC3_TRB_NUM;
+ return DWC3_TRB_NUM - 1;
}
- /* The last TRB is a link TRB, not used for xfer */
- if (trbs_left <= 1)
- return;
+ trbs_left = dep->trb_dequeue - dep->trb_enqueue;
+ trbs_left &= (DWC3_TRB_NUM - 1);
- list_for_each_entry_safe(req, n, &dep->pending_list, list) {
- unsigned length;
- dma_addr_t dma;
- last_one = false;
-
- if (req->request.num_mapped_sgs > 0) {
- struct usb_request *request = &req->request;
- struct scatterlist *sg = request->sg;
- struct scatterlist *s;
- int i;
-
- for_each_sg(sg, s, request->num_mapped_sgs, i) {
- unsigned chain = true;
-
- length = sg_dma_len(s);
- dma = sg_dma_address(s);
-
- if (i == (request->num_mapped_sgs - 1) ||
- sg_is_last(s)) {
- if (list_empty(&dep->pending_list))
- last_one = true;
- chain = false;
- }
+ if (dep->trb_dequeue < dep->trb_enqueue)
+ trbs_left--;
- trbs_left--;
- if (!trbs_left)
- last_one = true;
+ return trbs_left;
+}
- if (last_one)
- chain = false;
+static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int trbs_left,
+ unsigned int more_coming)
+{
+ struct usb_request *request = &req->request;
+ struct scatterlist *sg = request->sg;
+ struct scatterlist *s;
+ unsigned int last = false;
+ unsigned int length;
+ dma_addr_t dma;
+ int i;
- dwc3_prepare_one_trb(dep, req, dma, length,
- last_one, chain, i);
+ for_each_sg(sg, s, request->num_mapped_sgs, i) {
+ unsigned chain = true;
- if (last_one)
- break;
- }
+ length = sg_dma_len(s);
+ dma = sg_dma_address(s);
- if (last_one)
- break;
- } else {
- dma = req->request.dma;
- length = req->request.length;
- trbs_left--;
+ if (sg_is_last(s)) {
+ if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
+ !more_coming)
+ last = true;
- if (!trbs_left)
- last_one = 1;
+ chain = false;
+ }
- /* Is this the last request? */
- if (list_is_last(&req->list, &dep->pending_list))
- last_one = 1;
+ if (!trbs_left--)
+ last = true;
- dwc3_prepare_one_trb(dep, req, dma, length,
- last_one, false, 0);
+ if (last)
+ chain = false;
- if (last_one)
- break;
- }
+ dwc3_prepare_one_trb(dep, req, dma, length,
+ last, chain, i);
+
+ if (last)
+ break;
}
}
-static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
- int start_new)
+static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int trbs_left,
+ unsigned int more_coming)
+{
+ unsigned int last = false;
+ unsigned int length;
+ dma_addr_t dma;
+
+ dma = req->request.dma;
+ length = req->request.length;
+
+ if (!trbs_left)
+ last = true;
+
+ /* Is this the last request? */
+ if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming)
+ last = true;
+
+ dwc3_prepare_one_trb(dep, req, dma, length,
+ last, false, 0);
+}
+
+/*
+ * dwc3_prepare_trbs - setup TRBs from requests
+ * @dep: endpoint for which requests are being prepared
+ *
+ * The function goes through the requests list and sets up TRBs for the
+ * transfers. The function returns once there are no more TRBs available or
+ * it runs out of requests.
+ */
+static void dwc3_prepare_trbs(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req, *n;
+ unsigned int more_coming;
+ u32 trbs_left;
+
+ BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
+
+ trbs_left = dwc3_calc_trbs_left(dep);
+ if (!trbs_left)
+ return;
+
+ more_coming = dep->allocated_requests - dep->queued_requests;
+
+ list_for_each_entry_safe(req, n, &dep->pending_list, list) {
+ if (req->request.num_mapped_sgs > 0)
+ dwc3_prepare_one_trb_sg(dep, req, trbs_left--,
+ more_coming);
+ else
+ dwc3_prepare_one_trb_linear(dep, req, trbs_left--,
+ more_coming);
+
+ if (!trbs_left)
+ return;
+ }
+}
+
+static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_request *req;
struct dwc3 *dwc = dep->dwc;
+ int starting;
int ret;
u32 cmd;
- if (start_new && (dep->flags & DWC3_EP_BUSY)) {
- dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name);
- return -EBUSY;
- }
-
- /*
- * If we are getting here after a short-out-packet we don't enqueue any
- * new requests as we try to set the IOC bit only on the last request.
- */
- if (start_new) {
- if (list_empty(&dep->started_list))
- dwc3_prepare_trbs(dep, start_new);
-
- /* req points to the first request which will be sent */
- req = next_request(&dep->started_list);
- } else {
- dwc3_prepare_trbs(dep, start_new);
+ starting = !(dep->flags & DWC3_EP_BUSY);
- /*
- * req points to the first request where HWO changed from 0 to 1
- */
- req = next_request(&dep->started_list);
- }
+ dwc3_prepare_trbs(dep);
+ req = next_request(&dep->started_list);
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
return 0;
@@ -1015,16 +1010,17 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
memset(¶ms, 0, sizeof(params));
- if (start_new) {
+ if (starting) {
params.param0 = upper_32_bits(req->trb_dma);
params.param1 = lower_32_bits(req->trb_dma);
- cmd = DWC3_DEPCMD_STARTTRANSFER;
+ cmd = DWC3_DEPCMD_STARTTRANSFER |
+ DWC3_DEPCMD_PARAM(cmd_param);
} else {
- cmd = DWC3_DEPCMD_UPDATETRANSFER;
+ cmd = DWC3_DEPCMD_UPDATETRANSFER |
+ DWC3_DEPCMD_PARAM(dep->resource_index);
}
- cmd |= DWC3_DEPCMD_PARAM(cmd_param);
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret < 0) {
/*
* FIXME we need to iterate over the list of requests
@@ -1039,9 +1035,8 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
dep->flags |= DWC3_EP_BUSY;
- if (start_new) {
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
- dep->number);
+ if (starting) {
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
WARN_ON_ONCE(!dep->resource_index);
}
@@ -1064,7 +1059,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
/* 4 micro frames in the future */
uf = cur_uf + dep->interval * 4;
- __dwc3_gadget_kick_transfer(dep, uf, 1);
+ __dwc3_gadget_kick_transfer(dep, uf);
}
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
@@ -1085,18 +1080,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (!dep->endpoint.desc) {
dwc3_trace(trace_dwc3_gadget,
- "trying to queue request %p to disabled %s\n",
+ "trying to queue request %p to disabled %s",
&req->request, dep->endpoint.name);
return -ESHUTDOWN;
}
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
&req->request, req->dep->name)) {
- dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n",
+ dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'",
&req->request, req->dep->name);
return -EINVAL;
}
+ pm_runtime_get(dwc->dev);
+
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->direction = dep->direction;
@@ -1131,9 +1128,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* little bit faster.
*/
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !usb_endpoint_xfer_int(dep->endpoint.desc) &&
- !(dep->flags & DWC3_EP_BUSY)) {
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ !usb_endpoint_xfer_int(dep->endpoint.desc)) {
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
goto out;
}
@@ -1163,7 +1159,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return 0;
}
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
if (!ret)
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
@@ -1179,8 +1175,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
(dep->flags & DWC3_EP_BUSY) &&
!(dep->flags & DWC3_EP_MISSED_ISOC)) {
WARN_ON_ONCE(!dep->resource_index);
- ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
- false);
+ ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index);
goto out;
}
@@ -1190,12 +1185,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* handled.
*/
if (dep->stream_capable)
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
out:
if (ret && ret != -EBUSY)
dwc3_trace(trace_dwc3_gadget,
- "%s: failed to kick transfers\n",
+ "%s: failed to kick transfers",
dep->name);
if (ret == -EBUSY)
ret = 0;
@@ -1215,7 +1210,7 @@ static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
struct usb_request *request;
struct usb_ep *ep = &dep->endpoint;
- dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
+ dwc3_trace(trace_dwc3_gadget, "queueing ZLP");
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
if (!request)
return -ENOMEM;
@@ -1319,23 +1314,36 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
memset(¶ms, 0x00, sizeof(params));
if (value) {
- if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
- (!list_empty(&dep->started_list) ||
- !list_empty(&dep->pending_list)))) {
+ struct dwc3_trb *trb;
+
+ unsigned transfer_in_flight;
+ unsigned started;
+
+ if (dep->number > 1)
+ trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+ else
+ trb = &dwc->ep0_trb[dep->trb_enqueue];
+
+ transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO;
+ started = !list_empty(&dep->started_list);
+
+ if (!protocol && ((dep->direction && transfer_in_flight) ||
+ (!dep->direction && started))) {
dwc3_trace(trace_dwc3_gadget,
"%s: pending request, cannot halt",
dep->name);
return -EAGAIN;
}
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
- DWC3_DEPCMD_SETSTALL, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL,
+ ¶ms);
if (ret)
dev_err(dwc->dev, "failed to set STALL on %s\n",
dep->name);
else
dep->flags |= DWC3_EP_STALL;
} else {
+
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
dev_err(dwc->dev, "failed to clear STALL on %s\n",
@@ -1425,7 +1433,7 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
- unsigned long timeout;
+ int retries;
int ret;
u32 reg;
@@ -1444,8 +1452,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
speed = reg & DWC3_DSTS_CONNECTSPD;
if ((speed == DWC3_DSTS_SUPERSPEED) ||
(speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
- dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
- return -EINVAL;
+ dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed");
+ return 0;
}
link_state = DWC3_DSTS_USBLNKST(reg);
@@ -1456,7 +1464,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
break;
default:
dwc3_trace(trace_dwc3_gadget,
- "can't wakeup from '%s'\n",
+ "can't wakeup from '%s'",
dwc3_gadget_link_string(link_state));
return -EINVAL;
}
@@ -1476,9 +1484,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
}
/* poll until Link State changes to ON */
- timeout = jiffies + msecs_to_jiffies(100);
+ retries = 20000;
- while (!time_after(jiffies, timeout)) {
+ while (retries--) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
/* in HS, means ON */
@@ -1525,6 +1533,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
u32 reg;
u32 timeout = 500;
+ if (pm_runtime_suspended(dwc->dev))
+ return 0;
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
if (dwc->revision <= DWC3_REVISION_187A) {
@@ -1553,18 +1564,11 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
do {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
- if (is_on) {
- if (!(reg & DWC3_DSTS_DEVCTRLHLT))
- break;
- } else {
- if (reg & DWC3_DSTS_DEVCTRLHLT)
- break;
- }
- timeout--;
- if (!timeout)
- return -ETIMEDOUT;
- udelay(1);
- } while (1);
+ reg &= DWC3_DSTS_DEVCTRLHLT;
+ } while (--timeout && !(!is_on ^ !reg));
+
+ if (!timeout)
+ return -ETIMEDOUT;
dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
dwc->gadget_driver
@@ -1616,36 +1620,52 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
-static int dwc3_gadget_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+/**
+ * dwc3_gadget_setup_nump - Calculate and initialize NUMP field of DCFG
+ * dwc: pointer to our context structure
+ *
+ * The following looks like complex but it's actually very simple. In order to
+ * calculate the number of packets we can burst at once on OUT transfers, we're
+ * gonna use RxFIFO size.
+ *
+ * To calculate RxFIFO size we need two numbers:
+ * MDWIDTH = size, in bits, of the internal memory bus
+ * RAM2_DEPTH = depth, in MDWIDTH, of internal RAM2 (where RxFIFO sits)
+ *
+ * Given these two numbers, the formula is simple:
+ *
+ * RxFIFO Size = (RAM2_DEPTH * MDWIDTH / 8) - 24 - 16;
+ *
+ * 24 bytes is for 3x SETUP packets
+ * 16 bytes is a clock domain crossing tolerance
+ *
+ * Given RxFIFO Size, NUMP = RxFIFOSize / 1024;
+ */
+static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- struct dwc3_ep *dep;
- unsigned long flags;
- int ret = 0;
- int irq;
- u32 reg;
+ u32 ram2_depth;
+ u32 mdwidth;
+ u32 nump;
+ u32 reg;
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
- IRQF_SHARED, "dwc3", dwc->ev_buf);
- if (ret) {
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
- irq, ret);
- goto err0;
- }
+ ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
+ mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
- spin_lock_irqsave(&dwc->lock, flags);
+ nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
+ nump = min_t(u32, nump, 16);
- if (dwc->gadget_driver) {
- dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
- dwc->gadget_driver->driver.name);
- ret = -EBUSY;
- goto err1;
- }
+ /* update NumP */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_NUMP_MASK;
+ reg |= nump << DWC3_DCFG_NUMP_SHIFT;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
- dwc->gadget_driver = driver;
+static int __dwc3_gadget_start(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret = 0;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -1668,16 +1688,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
} else {
switch (dwc->maximum_speed) {
case USB_SPEED_LOW:
- reg |= DWC3_DSTS_LOWSPEED;
+ reg |= DWC3_DCFG_LOWSPEED;
break;
case USB_SPEED_FULL:
- reg |= DWC3_DSTS_FULLSPEED1;
+ reg |= DWC3_DCFG_FULLSPEED1;
break;
case USB_SPEED_HIGH:
- reg |= DWC3_DSTS_HIGHSPEED;
+ reg |= DWC3_DCFG_HIGHSPEED;
break;
case USB_SPEED_SUPER_PLUS:
- reg |= DWC3_DSTS_SUPERSPEED_PLUS;
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
break;
default:
dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
@@ -1701,6 +1721,8 @@ static int dwc3_gadget_start(struct usb_gadget *g,
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_gadget_setup_nump(dwc);
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -1709,7 +1731,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err2;
+ goto err0;
}
dep = dwc->eps[1];
@@ -1717,7 +1739,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err3;
+ goto err1;
}
/* begin to receive SETUP packets */
@@ -1726,43 +1748,79 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc3_gadget_enable_irq(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
-
return 0;
-err3:
- __dwc3_gadget_ep_disable(dwc->eps[0]);
-
-err2:
- dwc->gadget_driver = NULL;
-
err1:
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- free_irq(irq, dwc->ev_buf);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
err0:
return ret;
}
-static int dwc3_gadget_stop(struct usb_gadget *g)
+static int dwc3_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int ret = 0;
int irq;
+ irq = dwc->irq_gadget;
+ ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
+ IRQF_SHARED, "dwc3", dwc->ev_buf);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ irq, ret);
+ goto err0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->gadget_driver) {
+ dev_err(dwc->dev, "%s is already bound to %s\n",
+ dwc->gadget.name,
+ dwc->gadget_driver->driver.name);
+ ret = -EBUSY;
+ goto err1;
+ }
+
+ dwc->gadget_driver = driver;
+
+ if (pm_runtime_active(dwc->dev))
+ __dwc3_gadget_start(dwc);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+
+err1:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ free_irq(irq, dwc);
+
+err0:
+ return ret;
+}
+
+static void __dwc3_gadget_stop(struct dwc3 *dwc)
+{
+ if (pm_runtime_suspended(dwc->dev))
+ return;
dwc3_gadget_disable_irq(dwc);
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
+}
- dwc->gadget_driver = NULL;
+static int dwc3_gadget_stop(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ spin_lock_irqsave(&dwc->lock, flags);
+ __dwc3_gadget_stop(dwc);
+ dwc->gadget_driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- free_irq(irq, dwc->ev_buf);
+ free_irq(dwc->irq_gadget, dwc->ev_buf);
return 0;
}
@@ -1785,7 +1843,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
u8 i;
for (i = 0; i < num; i++) {
- u8 epnum = (i << 1) | (!!direction);
+ u8 epnum = (i << 1) | (direction ? 1 : 0);
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (!dep)
@@ -1794,12 +1852,14 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->dwc = dwc;
dep->number = epnum;
dep->direction = !!direction;
+ dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
(epnum & 1) ? "in" : "out");
dep->endpoint.name = dep->name;
+ spin_lock_init(&dep->lock);
dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
@@ -1895,25 +1955,32 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_request *req, struct dwc3_trb *trb,
- const struct dwc3_event_depevt *event, int status)
+ const struct dwc3_event_depevt *event, int status,
+ int chain)
{
unsigned int count;
unsigned int s_pkt = 0;
unsigned int trb_status;
+ dep->queued_requests--;
trace_dwc3_complete_trb(dep, trb);
+ /*
+ * If we're in the middle of series of chained TRBs and we
+ * receive a short transfer along the way, DWC3 will skip
+ * through all TRBs including the last TRB in the chain (the
+ * where CHN bit is zero. DWC3 will also avoid clearing HWO
+ * bit and SW has to do it manually.
+ *
+ * We're going to do that here to avoid problems of HW trying
+ * to use bogus TRBs for transfers.
+ */
+ if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
- /*
- * We continue despite the error. There is not much we
- * can do. If we don't clean it up we loop forever. If
- * we skip the TRB then it gets overwritten after a
- * while since we use them in a ring buffer. A BUG()
- * would help. Lets hope that if this occurs, someone
- * fixes the root cause instead of looking away :)
- */
- dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
- dep->name, trb);
+ return 1;
+
count = trb->size & DWC3_TRB_SIZE_MASK;
if (dep->direction) {
@@ -1921,7 +1988,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
dwc3_trace(trace_dwc3_gadget,
- "%s: incomplete IN transfer\n",
+ "%s: incomplete IN transfer",
dep->name);
/*
* If missed isoc occurred and there is
@@ -1952,15 +2019,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
s_pkt = 1;
}
- /*
- * We assume here we will always receive the entire data block
- * which we should receive. Meaning, if we program RX to
- * receive 4K but we receive only 2K, we assume that's all we
- * should receive and we simply bounce the request back to the
- * gadget driver for further processing.
- */
- req->request.actual += req->request.length - count;
- if (s_pkt)
+ if (s_pkt && !chain)
return 1;
if ((event->status & DEPEVT_STATUS_LST) &&
(trb->ctrl & (DWC3_TRB_CTRL_LST |
@@ -1979,13 +2038,17 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_trb *trb;
unsigned int slot;
unsigned int i;
+ int count = 0;
int ret;
do {
+ int chain;
+
req = next_request(&dep->started_list);
if (WARN_ON_ONCE(!req))
return 1;
+ chain = req->request.num_mapped_sgs > 0;
i = 0;
do {
slot = req->first_trb_index + i;
@@ -1993,19 +2056,36 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
slot++;
slot %= DWC3_TRB_NUM;
trb = &dep->trb_pool[slot];
+ count += trb->size & DWC3_TRB_SIZE_MASK;
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status);
+ event, status, chain);
if (ret)
break;
} while (++i < req->request.num_mapped_sgs);
+ /*
+ * We assume here we will always receive the entire data block
+ * which we should receive. Meaning, if we program RX to
+ * receive 4K but we receive only 2K, we assume that's all we
+ * should receive and we simply bounce the request back to the
+ * gadget driver for further processing.
+ */
+ req->request.actual += req->request.length - count;
dwc3_gadget_giveback(dep, req, status);
if (ret)
break;
} while (1);
+ /*
+ * Our endpoint might get disabled by another thread during
+ * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
+ * early on so DWC3_EP_BUSY flag gets cleared
+ */
+ if (!dep->endpoint.desc)
+ return 1;
+
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->started_list)) {
if (list_empty(&dep->pending_list)) {
@@ -2023,6 +2103,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
}
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ return 0;
return 1;
}
@@ -2039,7 +2123,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
status = -ECONNRESET;
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
- if (clean_busy && (is_xfer_complete ||
+ if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
usb_endpoint_xfer_isoc(dep->endpoint.desc)))
dep->flags &= ~DWC3_EP_BUSY;
@@ -2068,10 +2152,18 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dwc->u1u2 = 0;
}
+ /*
+ * Our endpoint might get disabled by another thread during
+ * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
+ * early on so DWC3_EP_BUSY flag gets cleared
+ */
+ if (!dep->endpoint.desc)
+ return;
+
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
int ret;
- ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete);
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
if (!ret || ret == -EBUSY)
return;
}
@@ -2099,7 +2191,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_trace(trace_dwc3_gadget,
- "%s is an Isochronous endpoint\n",
+ "%s is an Isochronous endpoint",
dep->name);
return;
}
@@ -2122,12 +2214,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->name, active ? "Transfer Active"
: "Transfer Not Active");
- ret = __dwc3_gadget_kick_transfer(dep, 0, !active);
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
if (!ret || ret == -EBUSY)
return;
dwc3_trace(trace_dwc3_gadget,
- "%s: failed to kick transfers\n",
+ "%s: failed to kick transfers",
dep->name);
}
@@ -2150,11 +2242,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
/* FALLTHROUGH */
default:
dwc3_trace(trace_dwc3_gadget,
- "unable to find suitable stream\n");
+ "unable to find suitable stream");
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
@@ -2237,7 +2329,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
cmd |= DWC3_DEPCMD_CMDIOC;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(¶ms, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
dep->flags &= ~DWC3_EP_BUSY;
@@ -2300,12 +2392,16 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+
+ dwc->connected = false;
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
+ dwc->connected = true;
+
/*
* WORKAROUND: DWC3 revisions <1.88a have an issue which
* would cause a missing Disconnect Event if there's a
@@ -2393,12 +2489,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_update_ram_clk_sel(dwc, speed);
switch (speed) {
- case DWC3_DCFG_SUPERSPEED_PLUS:
+ case DWC3_DSTS_SUPERSPEED_PLUS:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dwc->gadget.ep0->maxpacket = 512;
dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
break;
- case DWC3_DCFG_SUPERSPEED:
+ case DWC3_DSTS_SUPERSPEED:
/*
* WORKAROUND: DWC3 revisions <1.90a have an issue which
* would cause a missing USB3 Reset event.
@@ -2419,18 +2515,18 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc->gadget.ep0->maxpacket = 512;
dwc->gadget.speed = USB_SPEED_SUPER;
break;
- case DWC3_DCFG_HIGHSPEED:
+ case DWC3_DSTS_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_HIGH;
break;
- case DWC3_DCFG_FULLSPEED2:
- case DWC3_DCFG_FULLSPEED1:
+ case DWC3_DSTS_FULLSPEED2:
+ case DWC3_DSTS_FULLSPEED1:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_FULL;
break;
- case DWC3_DCFG_LOWSPEED:
+ case DWC3_DSTS_LOWSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
dwc->gadget.ep0->maxpacket = 8;
dwc->gadget.speed = USB_SPEED_LOW;
@@ -2440,8 +2536,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
/* Enable USB2 LPM Capability */
if ((dwc->revision > DWC3_REVISION_194A) &&
- (speed != DWC3_DCFG_SUPERSPEED) &&
- (speed != DWC3_DCFG_SUPERSPEED_PLUS)) {
+ (speed != DWC3_DSTS_SUPERSPEED) &&
+ (speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg |= DWC3_DCFG_LPM_CAP;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2610,6 +2706,17 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
dwc->link_state = next;
}
+static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+
+ if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
+ dwc3_suspend_gadget(dwc);
+
+ dwc->link_state = next;
+}
+
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
unsigned int evtinfo)
{
@@ -2661,7 +2768,20 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_EOPF:
- dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+ /* It changed to be suspend event for version 2.30a and above */
+ if (dwc->revision < DWC3_REVISION_230A) {
+ dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+ } else {
+ dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
+
+ /*
+ * Ignore suspend event until the gadget enters into
+ * USB_STATE_CONFIGURED state.
+ */
+ if (dwc->gadget.state >= USB_STATE_CONFIGURED)
+ dwc3_gadget_suspend_interrupt(dwc,
+ event->event_info);
+ }
break;
case DWC3_DEVICE_EVENT_SOF:
dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
@@ -2767,6 +2887,13 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
u32 count;
u32 reg;
+ if (pm_runtime_suspended(dwc->dev)) {
+ pm_runtime_get(dwc->dev);
+ disable_irq_nosync(dwc->irq_gadget);
+ dwc->pending_events = true;
+ return IRQ_HANDLED;
+ }
+
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
@@ -2798,7 +2925,33 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
*/
int dwc3_gadget_init(struct dwc3 *dwc)
{
- int ret;
+ int ret, irq;
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+
+ irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
+ if (irq == -EPROBE_DEFER)
+ return irq;
+
+ if (irq <= 0) {
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq == -EPROBE_DEFER)
+ return irq;
+
+ if (irq <= 0) {
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq <= 0) {
+ if (irq != -EPROBE_DEFER) {
+ dev_err(dwc->dev,
+ "missing peripheral IRQ\n");
+ }
+ if (!irq)
+ irq = -EINVAL;
+ return irq;
+ }
+ }
+ }
+
+ dwc->irq_gadget = irq;
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
@@ -2861,7 +3014,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
*/
if (dwc->revision < DWC3_REVISION_220A)
dwc3_trace(trace_dwc3_gadget,
- "Changing max_speed on rev %08x\n",
+ "Changing max_speed on rev %08x",
dwc->revision);
dwc->gadget.max_speed = dwc->maximum_speed;
@@ -2935,61 +3088,50 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
+ int ret;
+
if (!dwc->gadget_driver)
return 0;
- if (dwc->pullups_connected) {
- dwc3_gadget_disable_irq(dwc);
- dwc3_gadget_run_stop(dwc, true, true);
- }
-
- __dwc3_gadget_ep_disable(dwc->eps[0]);
- __dwc3_gadget_ep_disable(dwc->eps[1]);
+ ret = dwc3_gadget_run_stop(dwc, false, false);
+ if (ret < 0)
+ return ret;
- dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ dwc3_disconnect_gadget(dwc);
+ __dwc3_gadget_stop(dwc);
return 0;
}
int dwc3_gadget_resume(struct dwc3 *dwc)
{
- struct dwc3_ep *dep;
int ret;
if (!dwc->gadget_driver)
return 0;
- /* Start with SuperSpeed Default */
- dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
-
- dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
- false);
- if (ret)
+ ret = __dwc3_gadget_start(dwc);
+ if (ret < 0)
goto err0;
- dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
- false);
- if (ret)
+ ret = dwc3_gadget_run_stop(dwc, true, false);
+ if (ret < 0)
goto err1;
- /* begin to receive SETUP packets */
- dwc->ep0state = EP0_SETUP_PHASE;
- dwc3_ep0_out_start(dwc);
-
- dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
-
- if (dwc->pullups_connected) {
- dwc3_gadget_enable_irq(dwc);
- dwc3_gadget_run_stop(dwc, true, false);
- }
-
return 0;
err1:
- __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_stop(dwc);
err0:
return ret;
}
+
+void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
+{
+ if (dwc->pending_events) {
+ dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf);
+ dwc->pending_events = false;
+ enable_irq(dwc->irq_gadget);
+ }
+}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index f21c0fccbebd..e4a1d974a5ae 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -95,11 +95,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
*
* Caller should take care of locking
*/
-static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
+static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
u32 res_id;
- res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number));
+ res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
return DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 6a79c8e66bbc..a06f9a8fecc7 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -26,7 +26,6 @@
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
{
- u32 offs = offset - DWC3_GLOBALS_REGS_START;
u32 value;
/*
@@ -34,7 +33,7 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
* space, see dwc3_probe in core.c.
* However, the offsets are given starting from xHCI address space.
*/
- value = readl(base + offs);
+ value = readl(base + offset - DWC3_GLOBALS_REGS_START);
/*
* When tracing we want to make it easy to find the correct address on
@@ -49,14 +48,12 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
{
- u32 offs = offset - DWC3_GLOBALS_REGS_START;
-
/*
* We requested the mem region starting from the Globals address
* space, see dwc3_probe in core.c.
* However, the offsets are given starting from xHCI address space.
*/
- writel(value, base + offs);
+ writel(value, base + offset - DWC3_GLOBALS_REGS_START);
/*
* When tracing we want to make it easy to find the correct address on
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
deleted file mode 100644
index 8826cca5fc6f..000000000000
--- a/drivers/usb/dwc3/platform_data.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * platform_data.h - USB DWC3 Platform Data Support
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
- * Author: Felipe Balbi <balbi@ti.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/otg.h>
-
-struct dwc3_platform_data {
- enum usb_device_speed maximum_speed;
- enum usb_dr_mode dr_mode;
- bool usb3_lpm_capable;
-
- unsigned is_utmi_l1_suspend:1;
- u8 hird_threshold;
-
- u8 lpm_nyet_threshold;
-
- unsigned disable_scramble_quirk:1;
- unsigned has_lpm_erratum:1;
- unsigned u2exit_lfps_quirk:1;
- unsigned u2ss_inp3_quirk:1;
- unsigned req_p1p2p3_quirk:1;
- unsigned del_p1p2p3_quirk:1;
- unsigned del_phy_power_chg_quirk:1;
- unsigned lfps_filter_quirk:1;
- unsigned rx_detect_poll_quirk:1;
- unsigned dis_u3_susphy_quirk:1;
- unsigned dis_u2_susphy_quirk:1;
- unsigned dis_enblslpm_quirk:1;
- unsigned dis_rxdet_inp3_quirk:1;
-
- unsigned tx_de_emphasis_quirk:1;
- unsigned tx_de_emphasis:2;
-
- u32 fladj_value;
-
- const char *hsphy_interface;
-};
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 12/64] usb: dwc3: import from kernel v4.9
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (10 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 11/64] usb: dwc3: import from kernel v4.8 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 13/64] usb: dwc3: import from kernel v4.10 Jens Wiklander
` (55 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.8 to v4.9.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.8..v4.9
Commits imported:
4accb8a1ee7d usb: dwc3: st: add missing <linux/pinctrl/consumer.h> include
9b9d7cdd0a20 usb: dwc3: Fix error handling for core init
51fbc7c06c89 usb: dwc3: Fix size used in dma_free_coherent()
d889c23ce4e3 usb: dwc3: gadget: never pre-start Isochronous endpoints
a9c3ca5fae6b usb: dwc3: gadget: properly account queued requests
81522637485d Merge tag 'usb-for-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
b281dc630b41 usb: dwc3: of-simple: add compatible for Cavium
26c9cac402c4 usb: dwc3: of-simple: allow glues without clocks
fc9104d5d717 Merge 4.8-rc6 into usb-next
8d53e6267594 usb: dwc3: of-simple: Fix warning during unbind
5e6c88d28ccb usb: dwc3: fix Clear Stall EP command failure
d6e10bf2ba47 usb: dwc3: avoid -Wmaybe-uninitialized warning
9d6173e125d9 usb: dwc3: Fix dr_mode validation
073c47aca7b7 usb: dwc3: ulpi: make dwc3_ulpi_ops constant
e6f74849784c usb: ulpi: rename operations {read|write}_dev to simply {read|write}
b7cf1dc3414d usb: dwc3: ulpi: use new api
9b0dd49e3565 Merge 4.8-rc5 into usb-testing
594e121f2568 usb: dwc3: gadget: stop kicking if we run out of space
7ae7df4982af usb: dwc3: gadget: abolish trbs_left
06281d460fc5 usb: dwc3: Add ENDXFER command polling
512e47572f7d usb: dwc3: Add revision numbers for the USB 3.0 IP
00af62330c39 usb: dwc3: core: Move the mode setting to the right place
f652191be3f6 usb: dwc3: of-simple: add compatible for rockchip rk3399
00fe081dc3a3 usb: dwc3: add dis_del_phy_power_chg_quirk
32f2ed864dd5 usb: dwc3: make usb2 phy utmi interface configurable
16199f338939 usb: dwc3: add dis_u2_freeclk_exists_quirk
dc55c67e9c95 usb: dwc3: gadget: improve increment request->actual
f99f53f24d87 usb: dwc3: gadget: remove condition that never happens
1f512119a08c usb: dwc3: gadget: add remaining sg entries to ring
2c78c0295fd8 usb: dwc3: gadget: interrupt on ring full too
0b3e4af3c740 usb: dwc3: gadget: add sg and num_pending_sgs to dwc3_request
31162af447d7 usb: dwc3: gadget: avoid while (1) loop on completion
08a36b543803 usb: dwc3: gadget: simplify __dwc3_gadget_ep_queue()
45438a0cd9c2 usb: dwc3: gadget: simplify dwc3_ep_prev_trb()
737f1ae2556a usb: dwc3: gadget: increment dequeue pointer on completion
4bc48c974708 usb: dwc3: gadget: retire LST bit completely
1aaaa9acaea1 Merge 4.8-rc3 into usb-next
a26a142275c6 usb: remove redundant dependency on USB_SUPPORT
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 127 ++++++++++++++---
drivers/usb/dwc3/core.h | 35 ++++-
drivers/usb/dwc3/gadget.c | 292 +++++++++++++++-----------------------
3 files changed, 258 insertions(+), 196 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 35d092456bec..fea446900cad 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -49,6 +49,57 @@
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
+/**
+ * dwc3_get_dr_mode - Validates and sets dr_mode
+ * @dwc: pointer to our context structure
+ */
+static int dwc3_get_dr_mode(struct dwc3 *dwc)
+{
+ enum usb_dr_mode mode;
+ struct device *dev = dwc->dev;
+ unsigned int hw_mode;
+
+ if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+ dwc->dr_mode = USB_DR_MODE_OTG;
+
+ mode = dwc->dr_mode;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
+ switch (hw_mode) {
+ case DWC3_GHWPARAMS0_MODE_GADGET:
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
+ dev_err(dev,
+ "Controller does not support host mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_PERIPHERAL;
+ break;
+ case DWC3_GHWPARAMS0_MODE_HOST:
+ if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
+ dev_err(dev,
+ "Controller does not support device mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_HOST;
+ break;
+ default:
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ if (mode != dwc->dr_mode) {
+ dev_warn(dev,
+ "Configuration mismatch. dr_mode forced to %s\n",
+ mode == USB_DR_MODE_HOST ? "host" : "gadget");
+
+ dwc->dr_mode = mode;
+ }
+
+ return 0;
+}
+
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -448,6 +499,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u3_susphy_quirk)
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ if (dwc->dis_del_phy_power_chg_quirk)
+ reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@@ -485,6 +539,23 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
break;
}
+ switch (dwc->hsphy_mode) {
+ case USBPHY_INTERFACE_MODE_UTMI:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
+ break;
+ case USBPHY_INTERFACE_MODE_UTMIW:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
+ break;
+ default:
+ break;
+ }
+
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
@@ -500,6 +571,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ if (dwc->dis_u2_freeclk_exists_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0;
@@ -666,18 +740,43 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err4;
}
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ break;
+ case USB_DR_MODE_HOST:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+ break;
+ case USB_DR_MODE_OTG:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ break;
+ default:
+ dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
+ break;
+ }
+
+ /*
+ * ENDXFER polling is available on version 3.10a and later of
+ * the DWC_usb3 controller. It is NOT available in the
+ * DWC_usb31 controller.
+ */
+ if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg |= DWC3_GUCTL2_RST_ACTBITLATER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
return 0;
err4:
- phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
err3:
- phy_power_off(dwc->usb3_generic_phy);
+ phy_power_off(dwc->usb2_generic_phy);
err2:
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
- dwc3_core_exit(dwc);
err1:
usb_phy_shutdown(dwc->usb2_phy);
@@ -763,7 +862,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -772,7 +870,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_HOST:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -781,7 +878,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -888,6 +984,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
+ dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
@@ -924,6 +1021,10 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,dis_enblslpm_quirk");
dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
"snps,dis_rxdet_inp3_quirk");
+ dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
+ "snps,dis-u2-freeclk-exists-quirk");
+ dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
+ "snps,dis-del-phy-power-chg-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
@@ -972,17 +1073,9 @@ static int dwc3_probe(struct platform_device *pdev)
goto err2;
}
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST) &&
- (dwc->dr_mode == USB_DR_MODE_OTG ||
- dwc->dr_mode == USB_DR_MODE_UNKNOWN))
- dwc->dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
- (dwc->dr_mode == USB_DR_MODE_OTG ||
- dwc->dr_mode == USB_DR_MODE_UNKNOWN))
- dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
-
- if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
- dwc->dr_mode = USB_DR_MODE_OTG;
+ ret = dwc3_get_dr_mode(dwc);
+ if (ret)
+ goto err3;
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 45d6de5107c7..6b60e42626a2 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -109,6 +109,7 @@
#define DWC3_GPRTBIMAP_HS1 0xc184
#define DWC3_GPRTBIMAP_FS0 0xc188
#define DWC3_GPRTBIMAP_FS1 0xc18c
+#define DWC3_GUCTL2 0xc19c
#define DWC3_VER_NUMBER 0xc1a0
#define DWC3_VER_TYPE 0xc1a4
@@ -199,9 +200,18 @@
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
+#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
+#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
+#define USBTRDTIM_UTMI_8_BIT 9
+#define USBTRDTIM_UTMI_16_BIT 5
+#define UTMI_PHYIF_16_BIT 1
+#define UTMI_PHYIF_8_BIT 0
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
@@ -235,7 +245,10 @@
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
/* Global HWPARAMS0 Register */
-#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE_GADGET 0
+#define DWC3_GHWPARAMS0_MODE_HOST 1
+#define DWC3_GHWPARAMS0_MODE_DRD 2
#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
@@ -279,6 +292,9 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+/* Global User Control Register 2 */
+#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -685,6 +701,8 @@ struct dwc3_hwparams {
* @request: struct usb_request to be transferred
* @list: a list_head used for request queueing
* @dep: struct dwc3_ep owning this request
+ * @sg: pointer to first incomplete sg
+ * @num_pending_sgs: counter to pending sgs
* @first_trb_index: index to first trb used by this request
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
@@ -697,7 +715,9 @@ struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
+ struct scatterlist *sg;
+ unsigned num_pending_sgs;
u8 first_trb_index;
u8 epnum;
struct dwc3_trb *trb;
@@ -743,6 +763,9 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @hsphy_mode: UTMI phy mode, one of following:
+ * - USBPHY_INTERFACE_MODE_UTMI
+ * - USBPHY_INTERFACE_MODE_UTMIW
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -799,6 +822,11 @@ struct dwc3_scratchpad_array {
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
* disabling the suspend signal to the PHY.
+ * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
+ * in GUSB2PHYCFG, specify that USB2 PHY doesn't
+ * provide a free-running PHY clock.
+ * @dis_del_phy_power_chg_quirk: set if we disable delay phy power
+ * change quirk.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -845,6 +873,7 @@ struct dwc3 {
size_t regs_size;
enum usb_dr_mode dr_mode;
+ enum usb_phy_interface hsphy_mode;
u32 fladj;
u32 irq_gadget;
@@ -880,6 +909,8 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_300A 0x5533300a
+#define DWC3_REVISION_310A 0x5533310a
/*
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
@@ -942,6 +973,8 @@ struct dwc3 {
unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1;
unsigned dis_rxdet_inp3_quirk:1;
+ unsigned dis_u2_freeclk_exists_quirk:1;
+ unsigned dis_del_phy_power_chg_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 122e64df2f4d..1dfa56a5f1c5 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -174,15 +174,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
- int i;
- if (req->started) {
- i = 0;
- do {
- dwc3_ep_inc_deq(dep);
- } while(++i < req->request.num_mapped_sgs);
- req->started = false;
- }
+ req->started = false;
list_del(&req->list);
req->trb = NULL;
@@ -348,7 +341,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
* IN transfers due to a mishandled error condition. Synopsys
* STAR 9000614252.
*/
- if (dep->direction && (dwc->revision >= DWC3_REVISION_260A))
+ if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
+ (dwc->gadget.speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(¶ms, 0, sizeof(params));
@@ -490,7 +484,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param0 |= DWC3_DEPCFG_ACTION_INIT;
}
- params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
+ if (usb_endpoint_xfer_control(desc))
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -764,6 +759,8 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
kfree(req);
}
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
+
/**
* dwc3_prepare_one_trb - setup one TRB from one request
* @dep: endpoint for which this request is prepared
@@ -771,15 +768,13 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
- unsigned length, unsigned last, unsigned chain, unsigned node)
+ unsigned length, unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
- dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s",
+ dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
dep->name, req, (unsigned long long) dma,
- length, last ? " last" : "",
- chain ? " chain" : "");
-
+ length, chain ? " chain" : "");
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -788,6 +783,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->trb = trb;
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
req->first_trb_index = dep->trb_enqueue;
+ dep->queued_requests++;
}
dwc3_ep_inc_enq(dep);
@@ -826,12 +822,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
/* always enable Continue on Short Packet */
trb->ctrl |= DWC3_TRB_CTRL_CSP;
- if (!req->request.no_interrupt && !chain)
+ if ((!req->request.no_interrupt && !chain) ||
+ (dwc3_calc_trbs_left(dep) == 0))
trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
- if (last && !usb_endpoint_xfer_isoc(dep->endpoint.desc))
- trb->ctrl |= DWC3_TRB_CTRL_LST;
-
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
@@ -840,8 +834,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl |= DWC3_TRB_CTRL_HWO;
- dep->queued_requests++;
-
trace_dwc3_prepare_trb(dep, trb);
}
@@ -856,12 +848,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
*/
static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
{
- if (!index)
- index = DWC3_TRB_NUM - 2;
- else
- index = dep->trb_enqueue - 1;
+ u8 tmp = index;
+
+ if (!tmp)
+ tmp = DWC3_TRB_NUM - 1;
- return &dep->trb_pool[index];
+ return &dep->trb_pool[tmp - 1];
}
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
@@ -894,65 +886,42 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
}
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
- struct dwc3_request *req, unsigned int trbs_left,
- unsigned int more_coming)
+ struct dwc3_request *req)
{
- struct usb_request *request = &req->request;
- struct scatterlist *sg = request->sg;
+ struct scatterlist *sg = req->sg;
struct scatterlist *s;
- unsigned int last = false;
unsigned int length;
dma_addr_t dma;
int i;
- for_each_sg(sg, s, request->num_mapped_sgs, i) {
+ for_each_sg(sg, s, req->num_pending_sgs, i) {
unsigned chain = true;
length = sg_dma_len(s);
dma = sg_dma_address(s);
- if (sg_is_last(s)) {
- if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
- !more_coming)
- last = true;
-
- chain = false;
- }
-
- if (!trbs_left--)
- last = true;
-
- if (last)
+ if (sg_is_last(s))
chain = false;
dwc3_prepare_one_trb(dep, req, dma, length,
- last, chain, i);
+ chain, i);
- if (last)
+ if (!dwc3_calc_trbs_left(dep))
break;
}
}
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
- struct dwc3_request *req, unsigned int trbs_left,
- unsigned int more_coming)
+ struct dwc3_request *req)
{
- unsigned int last = false;
unsigned int length;
dma_addr_t dma;
dma = req->request.dma;
length = req->request.length;
- if (!trbs_left)
- last = true;
-
- /* Is this the last request? */
- if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming)
- last = true;
-
dwc3_prepare_one_trb(dep, req, dma, length,
- last, false, 0);
+ false, 0);
}
/*
@@ -966,26 +935,19 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
static void dwc3_prepare_trbs(struct dwc3_ep *dep)
{
struct dwc3_request *req, *n;
- unsigned int more_coming;
- u32 trbs_left;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
- trbs_left = dwc3_calc_trbs_left(dep);
- if (!trbs_left)
+ if (!dwc3_calc_trbs_left(dep))
return;
- more_coming = dep->allocated_requests - dep->queued_requests;
-
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
- if (req->request.num_mapped_sgs > 0)
- dwc3_prepare_one_trb_sg(dep, req, trbs_left--,
- more_coming);
+ if (req->num_pending_sgs > 0)
+ dwc3_prepare_one_trb_sg(dep, req);
else
- dwc3_prepare_one_trb_linear(dep, req, trbs_left--,
- more_coming);
+ dwc3_prepare_one_trb_linear(dep, req);
- if (!trbs_left)
+ if (!dwc3_calc_trbs_left(dep))
return;
}
}
@@ -1101,93 +1063,37 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req);
- /*
- * We only add to our list of requests now and
- * start consuming the list once we get XferNotReady
- * IRQ.
- *
- * That way, we avoid doing anything that we don't need
- * to do now and defer it until the point we receive a
- * particular token from the Host side.
- *
- * This will also avoid Host cancelling URBs due to too
- * many NAKs.
- */
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->direction);
if (ret)
return ret;
- list_add_tail(&req->list, &dep->pending_list);
+ req->sg = req->request.sg;
+ req->num_pending_sgs = req->request.num_mapped_sgs;
- /*
- * If there are no pending requests and the endpoint isn't already
- * busy, we will just start the request straight away.
- *
- * This will save one IRQ (XFER_NOT_READY) and possibly make it a
- * little bit faster.
- */
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !usb_endpoint_xfer_int(dep->endpoint.desc)) {
- ret = __dwc3_gadget_kick_transfer(dep, 0);
- goto out;
- }
+ list_add_tail(&req->list, &dep->pending_list);
/*
- * There are a few special cases:
- *
- * 1. XferNotReady with empty list of requests. We need to kick the
- * transfer here in that situation, otherwise we will be NAKing
- * forever. If we get XferNotReady before gadget driver has a
- * chance to queue a request, we will ACK the IRQ but won't be
- * able to receive the data until the next request is queued.
- * The following code is handling exactly that.
+ * NOTICE: Isochronous endpoints should NEVER be prestarted. We must
+ * wait for a XferNotReady event so we will know what's the current
+ * (micro-)frame number.
*
+ * Without this trick, we are very, very likely gonna get Bus Expiry
+ * errors which will force us issue EndTransfer command.
*/
- if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- /*
- * If xfernotready is already elapsed and it is a case
- * of isoc transfer, then issue END TRANSFER, so that
- * you can receive xfernotready again and can have
- * notion of current microframe.
- */
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if (list_empty(&dep->started_list)) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- }
- return 0;
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
+ list_empty(&dep->started_list)) {
+ dwc3_stop_active_transfer(dwc, dep->number, true);
+ dep->flags = DWC3_EP_ENABLED;
}
-
- ret = __dwc3_gadget_kick_transfer(dep, 0);
- if (!ret)
- dep->flags &= ~DWC3_EP_PENDING_REQUEST;
-
- goto out;
- }
-
- /*
- * 2. XferInProgress on Isoc EP with an active transfer. We need to
- * kick the transfer here after queuing a request, otherwise the
- * core may not see the modified TRB(s).
- */
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (dep->flags & DWC3_EP_BUSY) &&
- !(dep->flags & DWC3_EP_MISSED_ISOC)) {
- WARN_ON_ONCE(!dep->resource_index);
- ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index);
- goto out;
+ return 0;
}
- /*
- * 4. Stream Capable Bulk Endpoints. We need to start the transfer
- * right away, otherwise host will not know we have streams to be
- * handled.
- */
- if (dep->stream_capable)
- ret = __dwc3_gadget_kick_transfer(dep, 0);
+ if (!dwc3_calc_trbs_left(dep))
+ return 0;
-out:
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
if (ret && ret != -EBUSY)
dwc3_trace(trace_dwc3_gadget,
"%s: failed to kick transfers",
@@ -1962,7 +1868,11 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int s_pkt = 0;
unsigned int trb_status;
- dep->queued_requests--;
+ dwc3_ep_inc_deq(dep);
+
+ if (req->trb == trb)
+ dep->queued_requests--;
+
trace_dwc3_complete_trb(dep, trb);
/*
@@ -1982,6 +1892,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
count = trb->size & DWC3_TRB_SIZE_MASK;
+ req->request.actual += count;
if (dep->direction) {
if (count) {
@@ -2021,48 +1932,51 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (s_pkt && !chain)
return 1;
- if ((event->status & DEPEVT_STATUS_LST) &&
- (trb->ctrl & (DWC3_TRB_CTRL_LST |
- DWC3_TRB_CTRL_HWO)))
- return 1;
+
if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC))
return 1;
+
return 0;
}
static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct dwc3_event_depevt *event, int status)
{
- struct dwc3_request *req;
+ struct dwc3_request *req, *n;
struct dwc3_trb *trb;
- unsigned int slot;
- unsigned int i;
- int count = 0;
+ bool ioc = false;
int ret;
- do {
+ list_for_each_entry_safe(req, n, &dep->started_list, list) {
+ unsigned length;
+ unsigned actual;
int chain;
- req = next_request(&dep->started_list);
- if (WARN_ON_ONCE(!req))
- return 1;
-
- chain = req->request.num_mapped_sgs > 0;
- i = 0;
- do {
- slot = req->first_trb_index + i;
- if (slot == DWC3_TRB_NUM - 1)
- slot++;
- slot %= DWC3_TRB_NUM;
- trb = &dep->trb_pool[slot];
- count += trb->size & DWC3_TRB_SIZE_MASK;
+ length = req->request.length;
+ chain = req->num_pending_sgs > 0;
+ if (chain) {
+ struct scatterlist *sg = req->sg;
+ struct scatterlist *s;
+ unsigned int pending = req->num_pending_sgs;
+ unsigned int i;
+ for_each_sg(sg, s, pending, i) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
+
+ req->sg = sg_next(s);
+ req->num_pending_sgs--;
+
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status, chain);
+ if (ret)
+ break;
+ }
+ } else {
+ trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, chain);
- if (ret)
- break;
- } while (++i < req->request.num_mapped_sgs);
+ }
/*
* We assume here we will always receive the entire data block
@@ -2071,12 +1985,21 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
* should receive and we simply bounce the request back to the
* gadget driver for further processing.
*/
- req->request.actual += req->request.length - count;
+ actual = length - req->request.actual;
+ req->request.actual = actual;
+
+ if (ret && chain && (actual < length) && req->num_pending_sgs)
+ return __dwc3_gadget_kick_transfer(dep, 0);
+
dwc3_gadget_giveback(dep, req, status);
- if (ret)
+ if (ret) {
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ ioc = true;
break;
- } while (1);
+ }
+ }
/*
* Our endpoint might get disabled by another thread during
@@ -2103,10 +2026,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
}
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- return 0;
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && ioc)
+ return 0;
+
return 1;
}
@@ -2322,6 +2244,18 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
*
* - Issue EndTransfer WITH CMDIOC bit set
* - Wait 100us
+ *
+ * As of IP version 3.10a of the DWC_usb3 IP, the controller
+ * supports a mode to work around the above limitation. The
+ * software can poll the CMDACT bit in the DEPCMD register
+ * after issuing a EndTransfer command. This mode is enabled
+ * by writing GUCTL2[14]. This polling is already done in the
+ * dwc3_send_gadget_ep_cmd() function so if the mode is
+ * enabled, the EndTransfer command will have completed upon
+ * returning from this function and we don't need to delay for
+ * 100us.
+ *
+ * This mode is NOT available on the DWC_usb31 IP.
*/
cmd = DWC3_DEPCMD_ENDTRANSFER;
@@ -2333,7 +2267,9 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
WARN_ON_ONCE(ret);
dep->resource_index = 0;
dep->flags &= ~DWC3_EP_BUSY;
- udelay(100);
+
+ if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
+ udelay(100);
}
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -3054,7 +2990,7 @@ err3:
kfree(dwc->setup_buf);
err2:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
err1:
@@ -3079,7 +3015,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 13/64] usb: dwc3: import from kernel v4.10
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (11 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 12/64] usb: dwc3: import from kernel v4.9 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 14/64] usb: dwc3: import from kernel v4.11 Jens Wiklander
` (54 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.9 to v4.10.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.9..v4.10
Commits imported:
8ae584d1951f usb: dwc3: exynos fix axius clock error path to do cleanup
9418ee15f718 usb: dwc3: gadget: Fix full speed mode
8f8983a56836 usb: dwc3: pci: add Intel Gemini Lake PCI ID
0eae2fde164c usb: dwc3: pci: Add "linux,sysdev_is_parent" property
12a7f17fac5b usb: dwc3: omap: fix race of pm runtime with irq handler in probe
d7fd41c6dbcc usb: dwc3: skip interrupt when ep disabled
51c1685d9562 usb: dwc3: pci: Fix dr_mode misspelling
e71d363d9c61 usb: dwc3: core: avoid Overflow events
d62145929992 usb: dwc3: gadget: always unmap EP0 requests
19ec31230eb3 usb: dwc3: ep0: explicitly call dwc3_ep0_prepare_one_trb()
7931ec86c1b7 usb: dwc3: ep0: add dwc3_ep0_prepare_one_trb()
a9042defa29a Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
9165dabb2500 treewide: Fix printk() message errors
ae4d814bf1f2 Merge tag 'usb-for-v4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
8c9f2de459c7 usb: dwc3: Do not set dma coherent mask
d64ff406e51e usb: dwc3: use bus->sysdev for DMA configuration
1c404b51a05d usb: dwc3: ep0: avoid empty-body warning
36daf3aa399c usb: dwc3: pci: avoid build warning
28632b44d129 usb: dwc3: Workaround for irq mask issue
cf40b86b6ef6 usb: dwc3: Implement interrupt moderation
65aca3205046 usb: dwc3: gadget: clear events in top-half handler
ebbb2d59398f usb: dwc3: gadget: use evt->cache for processing events
caefe6c7be47 usb: dwc3: gadget: use evt->length as we should
d9fa4c63f766 usb: dwc3: core: add a event buffer cache
a22f884b2665 usb: dwc3: core: remove dwc3_soft_reset()
7ac51a12ea11 usb: dwc3: Add a function to check properties
a987a906e95c usb: dwc3: Add a check for the DWC_usb3 core
5fb6fdaf38f7 usb: dwc3: gadget: Fix dead code writing GCTL.RAMCLKSEL
39ebb05ca83d usb: dwc3: gadget: Remove descriptor arguments to ep_enable
f2694a93e0df usb: dwc3: warn on once when no trbs
87aba10639b0 usb: dwc3: isoc clean DWC3_EP_PENDING_REQUEST flag
e3aee48692ac usb: dwc3: fix post-increment
ce3fc8b32994 usb: dwc3: clean TRB if STARTTRANSFER fail
8ab89da4c281 usb: dwc3: decrement queued_requests
8a0a8e1c42fd Merge 4.9-rc5 into usb-next
5eb30cedced6 usb: dwc3: trace: purge dwc3_trace()
2870e5013e19 usb: dwc3: trace: add a tracepoint for ep enable/disable
9cecca75b5a0 usb: dwc3: pci: call _DSM for suspend/resume
0f817ae696b0 usb: dwc3: pci: add a private driver structure
ff377ae47de5 usb: dwc3: gadget: always kick if num_pending_sgs > 0
7282c4ef0b66 usb: dwc3: gadget: stop touching HWO TRBs
d86c5a676e5b usb: dwc3: gadget: always try to prepare on started_list first
e62c5bc57367 usb: dwc3: gadget: tracking per-TRB remaining bytes
6cb2e4e3de10 usb: dwc3: gadget: cope with XferNotReady before usb_ep_queue()
76a638f8ac0d usb: dwc3: gadget: wait for End Transfer to complete
785c91f8ae4d usb: dwc3: cleanup with list_first_entry_or_null()
bb0147364850 usb: dwc3: gadget: don't clear RUN/STOP when it's invalid to do so
cf68923055cb usb: dwc3: gadget: purge dwc3_stop_active_transfers()
c5ac6116db35 usb: dwc3: core: add dwc3_get_properties()
3b2733227f40 usb: dwc3: core: remove unnecessary alignment
941f918ecfa7 usb: dwc3: core: introduce dwc3_core_setup_global_control()
0759956f8ae7 usb: dwc3: core: introduce dwc3_core_is_valid()
0bb39ca1ad87 usb: dwc3: Add support for device L1 exit
f9d2f9228cde usb: dwc3: remove unused struct member dwc3->mem
0a695d4c75ff usb: dwc3: gadget: never ever kill the machine
c9508c8c50f9 usb: dwc3: gadget: only interrupt on short if short_not_ok is set
58f29034b4c4 usb: dwc3: gadget: CSP is only valid for OUT endpoints
b9bd138e06b9 usb: dwc3: gadget: remove unused 'first_trb_index'
39e07ffb1c3d usb: dwc3: ep0: simplify dwc3_ep0_handle_feature()
6d729a55cc0f usb: dwc3: host: extract dwc3_host_get_irq()
6db3812e09db usb: dwc3: gadget: extract dwc3_gadget_get_irq()
4dd5a69e495b usb: dwc3: trace: add a proper tracepoint for reg accessors
ba1773fb7de9 usb: dwc3: Kconfig: allow all glues to build if COMPILE_TEST
57b14da56227 usb: dwc3: don't compile dwc3_trace() unless CONFIG_FTRACE=y
a97ea994605e usb: dwc3: gadget: offset Start Transfer latency for bulk EPs
fa8d965d736b usb: dwc3: trace: pretty print high-bandwidth transfers too
43c96be1c46e usb: dwc3: trace: print out ep0state also from XferComplete
cdd72ac20b4a usb: dwc3: debug: move dwc3_ep0_state_string() to debug.h
8566cd1adf29 usb: dwc3: gadget: remove redundant trace prints
45a2af2f2b5a usb: dwc3: debug: decode control endpoint phase too
799e9dc82968 usb: dwc3: gadget: conditionally disable Link State change events
15b8d9332b92 usb: dwc3: gadget: giveback request if we can't kick it
8897a761c371 usb: dwc3: gadget: make use of No Response Update Transfer
5999914f227b usb: dwc3: gadget: properly check ep cmd
6b9018d4c1e5 usb: dwc3: gadget: set PCM1 field of isochronous-first TRBs
cbfff98a623d Merge 4.9-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 358 ++++++++++++-----------
drivers/usb/dwc3/core.h | 63 +++-
drivers/usb/dwc3/debug.c | 32 --
drivers/usb/dwc3/debug.h | 41 ++-
drivers/usb/dwc3/ep0.c | 391 +++++++++++++------------
drivers/usb/dwc3/gadget.c | 594 ++++++++++++++++++++++----------------
drivers/usb/dwc3/gadget.h | 5 +-
drivers/usb/dwc3/io.h | 6 +-
8 files changed, 830 insertions(+), 660 deletions(-)
delete mode 100644 drivers/usb/dwc3/debug.c
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fea446900cad..369bab16a824 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -169,33 +169,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
return -ETIMEDOUT;
}
-/**
- * dwc3_soft_reset - Issue soft reset
- * @dwc: Pointer to our controller context structure
- */
-static int dwc3_soft_reset(struct dwc3 *dwc)
-{
- unsigned long timeout;
- u32 reg;
-
- timeout = jiffies + msecs_to_jiffies(500);
- dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
- do {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (!(reg & DWC3_DCTL_CSFTRST))
- break;
-
- if (time_after(jiffies, timeout)) {
- dev_err(dwc->dev, "Reset Timed Out\n");
- return -ETIMEDOUT;
- }
-
- cpu_relax();
- } while (true);
-
- return 0;
-}
-
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
@@ -229,7 +202,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
- dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma);
+ dma_free_coherent(dwc->sysdev, evt->length, evt->buf, evt->dma);
}
/**
@@ -251,7 +224,11 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
evt->dwc = dwc;
evt->length = length;
- evt->buf = dma_alloc_coherent(dwc->dev, length,
+ evt->cache = devm_kzalloc(dwc->dev, length, GFP_KERNEL);
+ if (!evt->cache)
+ return ERR_PTR(-ENOMEM);
+
+ evt->buf = dma_alloc_coherent(dwc->sysdev, length,
&evt->dma, GFP_KERNEL);
if (!evt->buf)
return ERR_PTR(-ENOMEM);
@@ -305,13 +282,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
struct dwc3_event_buffer *evt;
evt = dwc->ev_buf;
- dwc3_trace(trace_dwc3_core,
- "Event buf %p dma %08llx length %d\n",
- evt->buf, (unsigned long long) evt->dma,
- evt->length);
-
evt->lpos = 0;
-
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
@@ -370,11 +341,11 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
if (!WARN_ON(dwc->scratchbuf))
return 0;
- scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+ scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf,
dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dwc->dev, scratch_addr)) {
- dev_err(dwc->dev, "failed to map scratch buffer\n");
+ if (dma_mapping_error(dwc->sysdev, scratch_addr)) {
+ dev_err(dwc->sysdev, "failed to map scratch buffer\n");
ret = -EFAULT;
goto err0;
}
@@ -398,7 +369,7 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
return 0;
err1:
- dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
err0:
@@ -417,7 +388,7 @@ static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
if (!WARN_ON(dwc->scratchbuf))
return;
- dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
kfree(dwc->scratchbuf);
}
@@ -428,9 +399,6 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
-
- dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints",
- dwc->num_in_eps, dwc->num_out_eps);
}
static void dwc3_cache_hwparams(struct dwc3 *dwc)
@@ -524,13 +492,6 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
}
/* FALLTHROUGH */
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
- /* Making sure the interface and PHY are operational */
- ret = dwc3_soft_reset(dwc);
- if (ret)
- return ret;
-
- udelay(1);
-
ret = dwc3_ulpi_init(dwc);
if (ret)
return ret;
@@ -594,19 +555,12 @@ static void dwc3_core_exit(struct dwc3 *dwc)
phy_power_off(dwc->usb3_generic_phy);
}
-/**
- * dwc3_core_init - Low-level initialization of DWC3 Core
- * @dwc: Pointer to our controller context structure
- *
- * Returns 0 on success otherwise negative errno.
- */
-static int dwc3_core_init(struct dwc3 *dwc)
+static bool dwc3_core_is_valid(struct dwc3 *dwc)
{
- u32 hwparams4 = dwc->hwparams.hwparams4;
- u32 reg;
- int ret;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+
/* This should read as U3 followed by revision number */
if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
/* Detected DWC_usb3 IP */
@@ -616,36 +570,16 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
dwc->revision |= DWC3_REVISION_IS_DWC31;
} else {
- dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
- ret = -ENODEV;
- goto err0;
+ return false;
}
- /*
- * Write Linux Version Code to our GUID register so it's easy to figure
- * out which kernel version a bug was found.
- */
- dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
-
- /* Handle USB2.0-only core configuration */
- if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
- if (dwc->maximum_speed == USB_SPEED_SUPER)
- dwc->maximum_speed = USB_SPEED_HIGH;
- }
-
- /* issue device SoftReset too */
- ret = dwc3_soft_reset(dwc);
- if (ret)
- goto err0;
-
- ret = dwc3_core_soft_reset(dwc);
- if (ret)
- goto err0;
+ return true;
+}
- ret = dwc3_phy_setup(dwc);
- if (ret)
- goto err0;
+static void dwc3_core_setup_global_control(struct dwc3 *dwc)
+{
+ u32 hwparams4 = dwc->hwparams.hwparams4;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
@@ -683,13 +617,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
- dwc3_trace(trace_dwc3_core, "No power optimization available\n");
+ /* nothing */
+ break;
}
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dwc3_trace(trace_dwc3_core,
- "running on FPGA platform\n");
+ dev_info(dwc->dev, "Running with FPGA optmizations\n");
dwc->is_fpga = true;
}
@@ -714,7 +648,47 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GCTL_U2RSTECN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+/**
+ * dwc3_core_init - Low-level initialization of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_core_init(struct dwc3 *dwc)
+{
+ u32 reg;
+ int ret;
+
+ if (!dwc3_core_is_valid(dwc)) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ ret = -ENODEV;
+ goto err0;
+ }
+
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+
+ /* Handle USB2.0-only core configuration */
+ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ }
+
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret)
+ goto err0;
+ ret = dwc3_phy_setup(dwc);
+ if (ret)
+ goto err0;
+
+ dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
ret = dwc3_setup_scratch_buffers(dwc);
@@ -766,6 +740,16 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
+ /*
+ * Enable hardware control of sending remote wakeup in HS when
+ * the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
return 0;
err4:
@@ -919,57 +903,13 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
}
}
-#define DWC3_ALIGN_MASK (16 - 1)
-
-static int dwc3_probe(struct platform_device *pdev)
+static void dwc3_get_properties(struct dwc3 *dwc)
{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct dwc3 *dwc;
+ struct device *dev = dwc->dev;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
- int ret;
-
- void __iomem *regs;
- void *mem;
-
- mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
- if (!mem)
- return -ENOMEM;
-
- dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
- dwc->mem = mem;
- dwc->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "missing memory resource\n");
- return -ENODEV;
- }
-
- dwc->xhci_resources[0].start = res->start;
- dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
- DWC3_XHCI_REGS_END;
- dwc->xhci_resources[0].flags = res->flags;
- dwc->xhci_resources[0].name = res->name;
-
- res->start += DWC3_GLOBALS_REGS_START;
-
- /*
- * Request memory region but exclude xHCI regs,
- * since it will be requested by the xhci-plat driver.
- */
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs)) {
- ret = PTR_ERR(regs);
- goto err0;
- }
-
- dwc->regs = regs;
- dwc->regs_size = resource_size(res);
-
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -986,6 +926,13 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->dr_mode = usb_get_dr_mode(dev);
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
+ dwc->sysdev_is_parent = device_property_read_bool(dev,
+ "linux,sysdev_is_parent");
+ if (dwc->sysdev_is_parent)
+ dwc->sysdev = dwc->dev->parent;
+ else
+ dwc->sysdev = dwc->dev;
+
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
device_property_read_u8(dev, "snps,lpm-nyet-threshold",
@@ -1041,6 +988,112 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
+ dwc->imod_interval = 0;
+}
+
+/* check whether the core supports IMOD */
+bool dwc3_has_imod(struct dwc3 *dwc)
+{
+ return ((dwc3_is_usb3(dwc) &&
+ dwc->revision >= DWC3_REVISION_300A) ||
+ (dwc3_is_usb31(dwc) &&
+ dwc->revision >= DWC3_USB31_REVISION_120A));
+}
+
+static void dwc3_check_params(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+
+ /* Check for proper value of imod_interval */
+ if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
+ dev_warn(dwc->dev, "Interrupt moderation not supported\n");
+ dwc->imod_interval = 0;
+ }
+
+ /*
+ * Workaround for STAR 9000961433 which affects only version
+ * 3.00a of the DWC_usb3 core. This prevents the controller
+ * interrupt from being masked while handling events. IMOD
+ * allows us to work around this issue. Enable it for the
+ * affected version.
+ */
+ if (!dwc->imod_interval &&
+ (dwc->revision == DWC3_REVISION_300A))
+ dwc->imod_interval = 1;
+
+ /* Check the maximum_speed parameter */
+ switch (dwc->maximum_speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ break;
+ default:
+ dev_err(dev, "invalid maximum_speed parameter %d\n",
+ dwc->maximum_speed);
+ /* fall through */
+ case USB_SPEED_UNKNOWN:
+ /* default to superspeed */
+ dwc->maximum_speed = USB_SPEED_SUPER;
+
+ /*
+ * default to superspeed plus if we are capable.
+ */
+ if (dwc3_is_usb31(dwc) &&
+ (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+
+ break;
+ }
+}
+
+static int dwc3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct dwc3 *dwc;
+
+ int ret;
+
+ void __iomem *regs;
+
+ dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
+ if (!dwc)
+ return -ENOMEM;
+
+ dwc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc->xhci_resources[0].start = res->start;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
+ dwc->xhci_resources[0].flags = res->flags;
+ dwc->xhci_resources[0].name = res->name;
+
+ res->start += DWC3_GLOBALS_REGS_START;
+
+ /*
+ * Request memory region but exclude xHCI regs,
+ * since it will be requested by the xhci-plat driver.
+ */
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto err0;
+ }
+
+ dwc->regs = regs;
+ dwc->regs_size = resource_size(res);
+
+ dwc3_get_properties(dwc);
+
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
@@ -1050,12 +1103,6 @@ static int dwc3_probe(struct platform_device *pdev)
spin_lock_init(&dwc->lock);
- if (!dev->dma_mask) {
- dev->dma_mask = dev->parent->dma_mask;
- dev->dma_parms = dev->parent->dma_parms;
- dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
- }
-
pm_runtime_set_active(dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
@@ -1087,32 +1134,7 @@ static int dwc3_probe(struct platform_device *pdev)
goto err4;
}
- /* Check the maximum_speed parameter */
- switch (dwc->maximum_speed) {
- case USB_SPEED_LOW:
- case USB_SPEED_FULL:
- case USB_SPEED_HIGH:
- case USB_SPEED_SUPER:
- case USB_SPEED_SUPER_PLUS:
- break;
- default:
- dev_err(dev, "invalid maximum_speed parameter %d\n",
- dwc->maximum_speed);
- /* fall through */
- case USB_SPEED_UNKNOWN:
- /* default to superspeed */
- dwc->maximum_speed = USB_SPEED_SUPER;
-
- /*
- * default to superspeed plus if we are capable.
- */
- if (dwc3_is_usb31(dwc) &&
- (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
- dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
-
- break;
- }
+ dwc3_check_params(dwc);
ret = dwc3_core_init_mode(dwc);
if (ret)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6b60e42626a2..14b760209680 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -26,6 +26,7 @@
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/debugfs.h>
+#include <linux/wait.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -37,15 +38,14 @@
#define DWC3_MSG_MAX 500
/* Global constants */
+#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
-#define DWC3_EVENT_SIZE 4 /* bytes */
-#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
-#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
+#define DWC3_EVENT_BUFFERS_SIZE 4096
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -65,6 +65,7 @@
#define DWC3_DEVICE_EVENT_OVERFLOW 11
#define DWC3_GEVNTCOUNT_MASK 0xfffc
+#define DWC3_GEVNTCOUNT_EHB (1 << 31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
@@ -147,6 +148,8 @@
#define DWC3_DEPCMDPAR0 0x08
#define DWC3_DEPCMD 0x0c
+#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
+
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
@@ -198,6 +201,9 @@
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+/* Global User Control 1 Register */
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
@@ -303,9 +309,8 @@
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
-#define DWC3_DCFG_FULLSPEED2 (1 << 0)
+#define DWC3_DCFG_FULLSPEED (1 << 0)
#define DWC3_DCFG_LOWSPEED (2 << 0)
-#define DWC3_DCFG_FULLSPEED1 (3 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
@@ -397,9 +402,8 @@
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
-#define DWC3_DSTS_FULLSPEED2 (1 << 0)
+#define DWC3_DSTS_FULLSPEED (1 << 0)
#define DWC3_DSTS_LOWSPEED (2 << 0)
-#define DWC3_DSTS_FULLSPEED1 (3 << 0)
/* Device Generic Command Register */
#define DWC3_DGCMD_SET_LMP 0x01
@@ -450,6 +454,8 @@
#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
+#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
+
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
#define DWC3_DALEPENA_EP(n) (1 << n)
@@ -458,6 +464,11 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+#define DWC3_DEV_IMOD_COUNT_SHIFT 16
+#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16)
+#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
+#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+
/* Structures */
struct dwc3_trb;
@@ -465,6 +476,7 @@ struct dwc3_trb;
/**
* struct dwc3_event_buffer - Software event buffer representation
* @buf: _THE_ buffer
+ * @cache: The buffer cache used in the threaded interrupt
* @length: size of this buffer
* @lpos: event offset
* @count: cache of last read event count register
@@ -474,6 +486,7 @@ struct dwc3_trb;
*/
struct dwc3_event_buffer {
void *buf;
+ void *cache;
unsigned length;
unsigned int lpos;
unsigned int count;
@@ -499,6 +512,7 @@ struct dwc3_event_buffer {
* @endpoint: usb endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
+ * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
* @lock: spinlock for endpoint request queue traversal
* @regs: pointer to first endpoint register
* @trb_pool: array of transaction buffers
@@ -524,12 +538,13 @@ struct dwc3_ep {
struct list_head pending_list;
struct list_head started_list;
+ wait_queue_head_t wait_end_transfer;
+
spinlock_t lock;
void __iomem *regs;
struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma;
- const struct usb_ss_ep_comp_descriptor *comp_desc;
struct dwc3 *dwc;
u32 saved_state;
@@ -540,6 +555,8 @@ struct dwc3_ep {
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
#define DWC3_EP_MISSED_ISOC (1 << 6)
+#define DWC3_EP_END_TRANSFER_PENDING (1 << 7)
+#define DWC3_EP_TRANSFER_STARTED (1 << 8)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
@@ -703,7 +720,7 @@ struct dwc3_hwparams {
* @dep: struct dwc3_ep owning this request
* @sg: pointer to first incomplete sg
* @num_pending_sgs: counter to pending sgs
- * @first_trb_index: index to first trb used by this request
+ * @remaining: amount of data remaining
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
@@ -718,7 +735,7 @@ struct dwc3_request {
struct scatterlist *sg;
unsigned num_pending_sgs;
- u8 first_trb_index;
+ unsigned remaining;
u8 epnum;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
@@ -748,6 +765,7 @@ struct dwc3_scratchpad_array {
* @ep0_usb_req: dummy req used while handling STD USB requests
* @ep0_bounce_addr: dma address of ep0_bounce
* @scratch_addr: dma address of scratchbuf
+ * @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
* @dev: pointer to our struct device
* @xhci: pointer to our xHCI child
@@ -784,7 +802,6 @@ struct dwc3_scratchpad_array {
* @ep0state: state of endpoint zero
* @link_state: link state
* @speed: device speed (super, high, full, low)
- * @mem: points to start of memory which is used for this struct.
* @hwparams: copy of hwparams registers
* @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
@@ -798,6 +815,7 @@ struct dwc3_scratchpad_array {
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
* @has_hibernation: true when dwc3 was configured with Hibernation
+ * @sysdev_is_parent: true when dwc3 device has a parent driver
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
@@ -833,6 +851,8 @@ struct dwc3_scratchpad_array {
* 1 - -3.5dB de-emphasis
* 2 - No de-emphasis
* 3 - Reserved
+ * @imod_interval: set the interrupt moderation interval in 250ns
+ * increments or 0 to disable.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -846,11 +866,13 @@ struct dwc3 {
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
+ struct completion ep0_in_setup;
/* device lock */
spinlock_t lock;
struct device *dev;
+ struct device *sysdev;
struct platform_device *xhci;
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
@@ -909,6 +931,7 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
@@ -918,6 +941,7 @@ struct dwc3 {
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -934,8 +958,6 @@ struct dwc3 {
u8 num_out_eps;
u8 num_in_eps;
- void *mem;
-
struct dwc3_hwparams hwparams;
struct dentry *root;
struct debugfs_regset32 *regset;
@@ -952,6 +974,7 @@ struct dwc3 {
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
unsigned has_hibernation:1;
+ unsigned sysdev_is_parent:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
@@ -978,6 +1001,8 @@ struct dwc3 {
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
+
+ u16 imod_interval;
};
/* -------------------------------------------------------------------------- */
@@ -1039,12 +1064,16 @@ struct dwc3_event_depevt {
/* Control-only Status */
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
+#define DEPEVT_STATUS_CONTROL_PHASE(n) ((n) & 3)
/* In response to Start Transfer */
#define DEPEVT_TRANSFER_NO_RESOURCE 1
#define DEPEVT_TRANSFER_BUS_EXPIRY 2
u32 parameters:16;
+
+/* For Command Complete Events */
+#define DEPEVT_PARAMETER_CMD(n) (((n) & (0xf << 8)) >> 8)
} __packed;
/**
@@ -1133,12 +1162,20 @@ struct dwc3_gadget_ep_cmd_params {
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
+/* check whether we are on the DWC_usb3 core */
+static inline bool dwc3_is_usb3(struct dwc3 *dwc)
+{
+ return !(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
/* check whether we are on the DWC_usb31 core */
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
{
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
}
+bool dwc3_has_imod(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c
deleted file mode 100644
index 0be6885bc370..000000000000
--- a/drivers/usb/dwc3/debug.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support
- *
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
- *
- * Author: Felipe Balbi <balbi@ti.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include "debug.h"
-
-void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...)
-{
- struct va_format vaf;
- va_list args;
-
- va_start(args, fmt);
- vaf.fmt = fmt;
- vaf.va = &args;
-
- trace(&vaf);
-
- va_end(args);
-}
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 33ab2a203c1b..eeed4ffd8131 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -124,6 +124,22 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
}
+static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+{
+ switch (state) {
+ case EP0_UNCONNECTED:
+ return "Unconnected";
+ case EP0_SETUP_PHASE:
+ return "Setup Phase";
+ case EP0_DATA_PHASE:
+ return "Data Phase";
+ case EP0_STATUS_PHASE:
+ return "Status Phase";
+ default:
+ return "UNKNOWN";
+ }
+}
+
/**
* dwc3_gadget_event_string - returns event name
* @event: the event code
@@ -184,10 +200,11 @@ dwc3_gadget_event_string(const struct dwc3_event_devt *event)
* @event: then event code
*/
static inline const char *
-dwc3_ep_event_string(const struct dwc3_event_depevt *event)
+dwc3_ep_event_string(const struct dwc3_event_depevt *event, u32 ep0state)
{
u8 epnum = event->endpoint_number;
static char str[256];
+ size_t len;
int status;
int ret;
@@ -199,6 +216,10 @@ dwc3_ep_event_string(const struct dwc3_event_depevt *event)
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
strcat(str, "Transfer Complete");
+ len = strlen(str);
+
+ if (epnum <= 1)
+ sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state));
break;
case DWC3_DEPEVT_XFERINPROGRESS:
strcat(str, "Transfer In-Progress");
@@ -207,6 +228,19 @@ dwc3_ep_event_string(const struct dwc3_event_depevt *event)
strcat(str, "Transfer Not Ready");
status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
strcat(str, status ? " (Active)" : " (Not Active)");
+
+ /* Control Endpoints */
+ if (epnum <= 1) {
+ int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status);
+
+ switch (phase) {
+ case DEPEVT_STATUS_CONTROL_DATA:
+ strcat(str, " [Data Phase]");
+ break;
+ case DEPEVT_STATUS_CONTROL_STATUS:
+ strcat(str, " [Status Phase]");
+ }
+ }
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
strcat(str, "FIFO");
@@ -270,14 +304,14 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
}
}
-static inline const char *dwc3_decode_event(u32 event)
+static inline const char *dwc3_decode_event(u32 event, u32 ep0state)
{
const union dwc3_event evt = (union dwc3_event) event;
if (evt.type.is_devspec)
return dwc3_gadget_event_string(&evt.devt);
else
- return dwc3_ep_event_string(&evt.depevt);
+ return dwc3_ep_event_string(&evt.depevt, ep0state);
}
static inline const char *dwc3_ep_cmd_status_string(int status)
@@ -310,7 +344,6 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
}
}
-void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
#ifdef CONFIG_DEBUG_FS
extern void dwc3_debugfs_init(struct dwc3 *);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index fe79d771dee4..9bb1f8526f3e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -39,36 +39,13 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
-static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
+ dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
- switch (state) {
- case EP0_UNCONNECTED:
- return "Unconnected";
- case EP0_SETUP_PHASE:
- return "Setup Phase";
- case EP0_DATA_PHASE:
- return "Data Phase";
- case EP0_STATUS_PHASE:
- return "Status Phase";
- default:
- return "UNKNOWN";
- }
-}
-
-static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type, bool chain)
-{
- struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
struct dwc3_ep *dep;
- int ret;
-
dep = dwc->eps[epnum];
- if (dep->flags & DWC3_EP_BUSY) {
- dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
- return 0;
- }
trb = &dwc->ep0_trb[dep->trb_enqueue];
@@ -89,21 +66,26 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trb->ctrl |= (DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_LST);
- if (chain)
+ trace_dwc3_prepare_trb(dep, trb);
+}
+
+static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
+ int ret;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_BUSY)
return 0;
memset(¶ms, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
- trace_dwc3_prepare_trb(dep, trb);
-
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms);
- if (ret < 0) {
- dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
- dep->name);
+ if (ret < 0)
return ret;
- }
dep->flags |= DWC3_EP_BUSY;
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
@@ -163,9 +145,6 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
- else
- dwc3_trace(trace_dwc3_ep0,
- "too early for delayed status");
return 0;
}
@@ -229,9 +208,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
if (!dep->endpoint.desc) {
- dwc3_trace(trace_dwc3_ep0,
- "trying to queue request %p to disabled %s",
- request, dep->name);
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
ret = -ESHUTDOWN;
goto out;
}
@@ -242,11 +220,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
- dwc3_trace(trace_dwc3_ep0,
- "queueing request %p to %s length %d state '%s'",
- request, dep->name, request->length,
- dwc3_ep0_state_string(dwc->ep0state));
-
ret = __dwc3_gadget_ep0_queue(dep, req);
out:
@@ -308,8 +281,11 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{
int ret;
- ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
+ complete(&dwc->ep0_in_setup);
+
+ dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
+ ret = dwc3_ep0_start_trans(dwc, 0);
WARN_ON(ret < 0);
}
@@ -395,126 +371,198 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
}
-static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
+static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
+ int set)
+{
+ u32 reg;
+
+ if (state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU1ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
+ int set)
+{
+ u32 reg;
+
+
+ if (state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU2ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
+ u32 wIndex, int set)
+{
+ if ((wIndex & 0xff) != 0)
+ return -EINVAL;
+ if (!set)
+ return -EINVAL;
+
+ switch (wIndex >> 8) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ dwc->test_mode_nr = wIndex >> 8;
+ dwc->test_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_device(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
- struct dwc3_ep *dep;
- u32 recip;
+ enum usb_device_state state;
u32 wValue;
u32 wIndex;
- u32 reg;
- int ret;
- enum usb_device_state state;
+ int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
- recip = ctrl->bRequestType & USB_RECIP_MASK;
state = dwc->gadget.state;
- switch (recip) {
- case USB_RECIP_DEVICE:
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
+ /*
+ * 9.4.1 says only only for SS, in AddressState only for
+ * default control pipe
+ */
+ case USB_DEVICE_U1_ENABLE:
+ ret = dwc3_ep0_handle_u1(dwc, state, set);
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ ret = dwc3_ep0_handle_u2(dwc, state, set);
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ ret = -EINVAL;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ ret = dwc3_ep0_handle_test(dwc, state, wIndex, set);
+ break;
+ default:
+ ret = -EINVAL;
+ }
- switch (wValue) {
- case USB_DEVICE_REMOTE_WAKEUP:
- break;
+ return ret;
+}
+
+static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ state = dwc->gadget.state;
+
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
/*
- * 9.4.1 says only only for SS, in AddressState only for
- * default control pipe
+ * REVISIT: Ideally we would enable some low power mode here,
+ * however it's unclear what we should be doing here.
+ *
+ * For now, we're not doing anything, just making sure we return
+ * 0 so USB Command Verifier tests pass without any errors.
*/
- case USB_DEVICE_U1_ENABLE:
- if (state != USB_STATE_CONFIGURED)
- return -EINVAL;
- if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
- (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
- return -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (set)
- reg |= DWC3_DCTL_INITU1ENA;
- else
- reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- break;
+ return ret;
+}
- case USB_DEVICE_U2_ENABLE:
- if (state != USB_STATE_CONFIGURED)
- return -EINVAL;
- if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
- (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
- return -EINVAL;
+static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ struct dwc3_ep *dep;
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (set)
- reg |= DWC3_DCTL_INITU2ENA;
- else
- reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- break;
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ state = dwc->gadget.state;
- case USB_DEVICE_LTM_ENABLE:
+ switch (wValue) {
+ case USB_ENDPOINT_HALT:
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
+ if (!dep)
return -EINVAL;
- case USB_DEVICE_TEST_MODE:
- if ((wIndex & 0xff) != 0)
- return -EINVAL;
- if (!set)
- return -EINVAL;
-
- switch (wIndex >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
- dwc->test_mode_nr = wIndex >> 8;
- dwc->test_mode = true;
- break;
- default:
- return -EINVAL;
- }
+ if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
break;
- default:
+
+ ret = __dwc3_gadget_ep_set_halt(dep, set, true);
+ if (ret)
return -EINVAL;
- }
break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ u32 recip;
+ int ret;
+ enum usb_device_state state;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+ state = dwc->gadget.state;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ ret = dwc3_ep0_handle_device(dwc, ctrl, set);
+ break;
case USB_RECIP_INTERFACE:
- switch (wValue) {
- case USB_INTRF_FUNC_SUSPEND:
- if (wIndex & USB_INTRF_FUNC_SUSPEND_LP)
- /* XXX enable Low power suspend */
- ;
- if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
- /* XXX enable remote wakeup */
- ;
- break;
- default:
- return -EINVAL;
- }
+ ret = dwc3_ep0_handle_intf(dwc, ctrl, set);
break;
-
case USB_RECIP_ENDPOINT:
- switch (wValue) {
- case USB_ENDPOINT_HALT:
- dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
- if (!dep)
- return -EINVAL;
- if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
- break;
- ret = __dwc3_gadget_ep_set_halt(dep, set, true);
- if (ret)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
+ ret = dwc3_ep0_handle_endpoint(dwc, ctrl, set);
break;
-
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ return ret;
}
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
@@ -525,13 +573,12 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
addr = le16_to_cpu(ctrl->wValue);
if (addr > 127) {
- dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr);
+ dev_err(dwc->dev, "invalid device address %d\n", addr);
return -EINVAL;
}
if (state == USB_STATE_CONFIGURED) {
- dwc3_trace(trace_dwc3_ep0,
- "trying to set address when configured");
+ dev_err(dwc->dev, "can't SetAddress() from Configured State\n");
return -EINVAL;
}
@@ -716,35 +763,27 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
case USB_REQ_SET_SEL:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL");
ret = dwc3_ep0_set_sel(dwc, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
default:
- dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
}
@@ -820,9 +859,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
dwc->setup_packet_pending = true;
-
- dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
-
if (r)
dwc3_gadget_giveback(ep0, r, -ECONNRESET);
@@ -880,9 +916,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
- ret = dwc3_ep0_start_trans(dwc, epnum,
- dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, false);
+ dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, epnum);
WARN_ON(ret < 0);
}
}
@@ -912,7 +948,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
if (ret < 0) {
- dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d",
+ dev_err(dwc->dev, "invalid test #%d\n",
dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -920,10 +956,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
}
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ if (status == DWC3_TRBSTS_SETUP_PENDING)
dwc->setup_packet_pending = true;
- dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
- }
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
@@ -940,17 +974,14 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
switch (dwc->ep0state) {
case EP0_SETUP_PHASE:
- dwc3_trace(trace_dwc3_ep0, "Setup Phase");
dwc3_ep0_inspect_setup(dwc, event);
break;
case EP0_DATA_PHASE:
- dwc3_trace(trace_dwc3_ep0, "Data Phase");
dwc3_ep0_complete_data(dwc, event);
break;
case EP0_STATUS_PHASE:
- dwc3_trace(trace_dwc3_ep0, "Status Phase");
dwc3_ep0_complete_status(dwc, event);
break;
default:
@@ -966,27 +997,26 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
u32 transfer_size = 0;
u32 maxpacket;
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- dep->number);
- if (ret) {
- dwc3_trace(trace_dwc3_ep0, "failed to map request");
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
return;
- }
maxpacket = dep->endpoint.maxpacket;
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(req->request.length - maxpacket,
maxpacket);
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
req->request.dma,
transfer_size,
DWC3_TRBCTL_CONTROL_DATA,
@@ -998,20 +1028,20 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounced = true;
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else {
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- dep->number);
- if (ret) {
- dwc3_trace(trace_dwc3_ep0, "failed to map request");
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
return;
- }
- ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
}
WARN_ON(ret < 0);
@@ -1025,8 +1055,9 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
- return dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dwc, dep->number);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
@@ -1065,8 +1096,6 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
{
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
- dwc3_trace(trace_dwc3_ep0, "Control Data");
-
/*
* We already have a DATA transfer in the controller's cache,
* if we receive a XferNotReady(DATA) we will ignore it, unless
@@ -1079,8 +1108,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_expect_in != event->endpoint_number) {
struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
- dwc3_trace(trace_dwc3_ep0,
- "Wrong direction for Data phase");
+ dev_err(dwc->dev, "unexpected direction for Data Phase\n");
dwc3_ep0_end_control_data(dwc, dep);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -1092,13 +1120,10 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
- dwc3_trace(trace_dwc3_ep0, "Control Status");
-
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
WARN_ON_ONCE(event->endpoint_number != 1);
- dwc3_trace(trace_dwc3_ep0, "Delayed Status");
return;
}
@@ -1109,10 +1134,6 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- dwc3_trace(trace_dwc3_ep0, "%s: state '%s'",
- dwc3_ep_event_string(event),
- dwc3_ep0_state_string(dwc->ep0state));
-
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_ep0_xfer_complete(dwc, event);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1dfa56a5f1c5..204c754cc647 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -139,9 +139,6 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
udelay(5);
}
- dwc3_trace(trace_dwc3_gadget,
- "link state change request timed out");
-
return -ETIMEDOUT;
}
@@ -178,15 +175,16 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
req->started = false;
list_del(&req->list);
req->trb = NULL;
+ req->remaining = 0;
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number == 0)
+ if (dwc->ep0_bounced && dep->number <= 1)
dwc->ep0_bounced = false;
- else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
trace_dwc3_gadget_giveback(req);
@@ -216,7 +214,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
ret = -EINVAL;
break;
}
- } while (timeout--);
+ } while (--timeout);
if (!timeout) {
ret = -ETIMEDOUT;
@@ -233,6 +231,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
struct dwc3_gadget_ep_cmd_params *params)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 500;
u32 reg;
@@ -258,7 +257,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
}
}
- if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
int needs_wakeup;
needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
@@ -276,7 +275,28 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
- dwc3_writel(dep->regs, DWC3_DEPCMD, cmd | DWC3_DEPCMD_CMDACT);
+ /*
+ * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're
+ * not relying on XferNotReady, we can make use of a special "No
+ * Response Update Transfer" command where we should clear both CmdAct
+ * and CmdIOC bits.
+ *
+ * With this, we don't need to wait for command completion and can
+ * straight away issue further commands to the endpoint.
+ *
+ * NOTICE: We're making an assumption that control endpoints will never
+ * make use of Update Transfer command. This is a safe assumption
+ * because we can never have more than one request at a time with
+ * Control Endpoints. If anybody changes that assumption, this chunk
+ * needs to be updated accordingly.
+ */
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_UPDATETRANSFER &&
+ !usb_endpoint_xfer_isoc(desc))
+ cmd &= ~(DWC3_DEPCMD_CMDIOC | DWC3_DEPCMD_CMDACT);
+ else
+ cmd |= DWC3_DEPCMD_CMDACT;
+
+ dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
do {
reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
if (!(reg & DWC3_DEPCMD_CMDACT)) {
@@ -318,6 +338,20 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
+ if (ret == 0) {
+ switch (DWC3_DEPCMD_CMD(cmd)) {
+ case DWC3_DEPCMD_STARTTRANSFER:
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ break;
+ case DWC3_DEPCMD_ENDTRANSFER:
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ }
+
if (unlikely(susphy)) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -365,7 +399,7 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
if (dep->trb_pool)
return 0;
- dep->trb_pool = dma_alloc_coherent(dwc->dev,
+ dep->trb_pool = dma_alloc_coherent(dwc->sysdev,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
&dep->trb_pool_dma, GFP_KERNEL);
if (!dep->trb_pool) {
@@ -381,7 +415,7 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
- dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ dma_free_coherent(dwc->sysdev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
dep->trb_pool, dep->trb_pool_dma);
dep->trb_pool = NULL;
@@ -454,16 +488,19 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
}
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
- const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc,
bool modify, bool restore)
{
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ const struct usb_endpoint_descriptor *desc;
struct dwc3_gadget_ep_cmd_params params;
if (dev_WARN_ONCE(dwc->dev, modify && restore,
"Can't modify and restore\n"))
return -EINVAL;
+ comp_desc = dep->endpoint.comp_desc;
+ desc = dep->endpoint.desc;
+
memset(¶ms, 0x00, sizeof(params));
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
@@ -542,24 +579,21 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
* Caller should take care of locking
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
- const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc,
bool modify, bool restore)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
+
u32 reg;
int ret;
- dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
-
if (!(dep->flags & DWC3_EP_ENABLED)) {
ret = dwc3_gadget_start_config(dwc, dep);
if (ret)
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, modify,
- restore);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, modify, restore);
if (ret)
return ret;
@@ -567,17 +601,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
struct dwc3_trb *trb_st_hw;
struct dwc3_trb *trb_link;
- dep->endpoint.desc = desc;
- dep->comp_desc = comp_desc;
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ init_waitqueue_head(&dep->wait_end_transfer);
+
if (usb_endpoint_xfer_control(desc))
- return 0;
+ goto out;
/* Initialize the TRB ring */
dep->trb_dequeue = 0;
@@ -595,6 +630,39 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
}
+ /*
+ * Issue StartTransfer here with no-op TRB so we can always rely on No
+ * Response Update Transfer command.
+ */
+ if (usb_endpoint_xfer_bulk(desc)) {
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_trb *trb;
+ dma_addr_t trb_dma;
+ u32 cmd;
+
+ memset(¶ms, 0, sizeof(params));
+ trb = &dep->trb_pool[0];
+ trb_dma = dwc3_trb_dma_offset(dep, trb);
+
+ params.param0 = upper_32_bits(trb_dma);
+ params.param1 = lower_32_bits(trb_dma);
+
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ if (ret < 0)
+ return ret;
+
+ dep->flags |= DWC3_EP_BUSY;
+
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
+ WARN_ON_ONCE(!dep->resource_index);
+ }
+
+
+out:
+ trace_dwc3_gadget_ep_enable(dep);
+
return 0;
}
@@ -632,7 +700,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
struct dwc3 *dwc = dep->dwc;
u32 reg;
- dwc3_trace(trace_dwc3_gadget, "Disabling %s", dep->name);
+ trace_dwc3_gadget_ep_disable(dep);
dwc3_remove_requests(dwc, dep);
@@ -645,10 +713,14 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
dep->stream_capable = false;
- dep->endpoint.desc = NULL;
- dep->comp_desc = NULL;
dep->type = 0;
- dep->flags = 0;
+ dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
+
+ /* Clear out the ep descriptors for non-ep0 */
+ if (dep->number > 1) {
+ dep->endpoint.comp_desc = NULL;
+ dep->endpoint.desc = NULL;
+ }
return 0;
}
@@ -695,7 +767,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return 0;
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, false, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -771,10 +843,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
unsigned length, unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
-
- dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
- dep->name, req, (unsigned long long) dma,
- length, chain ? " chain" : "");
+ struct dwc3 *dwc = dep->dwc;
+ struct usb_gadget *gadget = &dwc->gadget;
+ enum usb_device_speed speed = gadget->speed;
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -782,7 +853,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
dwc3_gadget_move_started_request(req);
req->trb = trb;
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- req->first_trb_index = dep->trb_enqueue;
dep->queued_requests++;
}
@@ -798,10 +868,16 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
break;
case USB_ENDPOINT_XFER_ISOC:
- if (!node)
+ if (!node) {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
- else
+
+ if (speed == USB_SPEED_HIGH) {
+ struct usb_ep *ep = &dep->endpoint;
+ trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
+ }
+ } else {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+ }
/* always enable Interrupt on Missed ISOC */
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
@@ -816,15 +892,21 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
* This is only possible with faulty memory because we
* checked it already :)
*/
- BUG();
+ dev_WARN(dwc->dev, "Unknown endpoint type %d\n",
+ usb_endpoint_type(dep->endpoint.desc));
}
/* always enable Continue on Short Packet */
- trb->ctrl |= DWC3_TRB_CTRL_CSP;
+ if (usb_endpoint_dir_out(dep->endpoint.desc)) {
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
+
+ if (req->request.short_not_ok)
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ }
if ((!req->request.no_interrupt && !chain) ||
(dwc3_calc_trbs_left(dep) == 0))
- trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
@@ -859,6 +941,7 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{
struct dwc3_trb *tmp;
+ struct dwc3 *dwc = dep->dwc;
u8 trbs_left;
/*
@@ -870,7 +953,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ if (dev_WARN_ONCE(dwc->dev, tmp->ctrl & DWC3_TRB_CTRL_HWO,
+ "%s No TRBS left\n", dep->name))
return 0;
return DWC3_TRB_NUM - 1;
@@ -941,6 +1025,24 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return;
+ /*
+ * We can get in a situation where there's a request in the started list
+ * but there weren't enough TRBs to fully kick it in the first time
+ * around, so it has been waiting for more TRBs to be freed up.
+ *
+ * In that case, we should check if we have a request with pending_sgs
+ * in the started list and prepare TRBs for that request first,
+ * otherwise we will prepare TRBs completely out of order and that will
+ * break things.
+ */
+ list_for_each_entry(req, &dep->started_list, list) {
+ if (req->num_pending_sgs > 0)
+ dwc3_prepare_one_trb_sg(dep, req);
+
+ if (!dwc3_calc_trbs_left(dep))
+ return;
+ }
+
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
if (req->num_pending_sgs > 0)
dwc3_prepare_one_trb_sg(dep, req);
@@ -956,7 +1058,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_request *req;
- struct dwc3 *dwc = dep->dwc;
int starting;
int ret;
u32 cmd;
@@ -989,9 +1090,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
* here and stop, unmap, free and del each of the linked
* requests instead of what we do now.
*/
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
- list_del(&req->list);
+ if (req->trb)
+ memset(req->trb, 0, sizeof(struct dwc3_trb));
+ dep->queued_requests--;
+ dwc3_gadget_giveback(dep, req, ret);
return ret;
}
@@ -1005,14 +1107,21 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
return 0;
}
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ return DWC3_DSTS_SOFFN(reg);
+}
+
static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, u32 cur_uf)
{
u32 uf;
if (list_empty(&dep->pending_list)) {
- dwc3_trace(trace_dwc3_gadget,
- "ISOC ep %s run out for requests",
+ dev_info(dwc->dev, "%s: ran out of requests\n",
dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
@@ -1041,16 +1150,15 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
int ret;
if (!dep->endpoint.desc) {
- dwc3_trace(trace_dwc3_gadget,
- "trying to queue request %p to disabled %s",
- &req->request, dep->endpoint.name);
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
return -ESHUTDOWN;
}
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
&req->request, req->dep->name)) {
- dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'",
- &req->request, req->dep->name);
+ dev_err(dwc->dev, "%s: request %p belongs to '%s'\n",
+ dep->name, &req->request, req->dep->name);
return -EINVAL;
}
@@ -1063,8 +1171,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req);
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- dep->direction);
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
+ dep->direction);
if (ret)
return ret;
@@ -1082,10 +1190,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* errors which will force us issue EndTransfer command.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
- list_empty(&dep->started_list)) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
+ dwc3_stop_active_transfer(dwc, dep->number, true);
+ dep->flags = DWC3_EP_ENABLED;
+ } else {
+ u32 cur_uf;
+
+ cur_uf = __dwc3_gadget_get_frame(dwc);
+ __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+ dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+ }
}
return 0;
}
@@ -1094,10 +1209,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return 0;
ret = __dwc3_gadget_kick_transfer(dep, 0);
- if (ret && ret != -EBUSY)
- dwc3_trace(trace_dwc3_gadget,
- "%s: failed to kick transfers",
- dep->name);
if (ret == -EBUSY)
ret = 0;
@@ -1116,7 +1227,6 @@ static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
struct usb_request *request;
struct usb_ep *ep = &dep->endpoint;
- dwc3_trace(trace_dwc3_gadget, "queueing ZLP");
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
if (!request)
return -ENOMEM;
@@ -1235,9 +1345,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (!protocol && ((dep->direction && transfer_in_flight) ||
(!dep->direction && started))) {
- dwc3_trace(trace_dwc3_gadget,
- "%s: pending request, cannot halt",
- dep->name);
return -EAGAIN;
}
@@ -1331,10 +1438,8 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
- return DWC3_DSTS_SOFFN(reg);
+ return __dwc3_gadget_get_frame(dwc);
}
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
@@ -1357,10 +1462,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
speed = reg & DWC3_DSTS_CONNECTSPD;
if ((speed == DWC3_DSTS_SUPERSPEED) ||
- (speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
- dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed");
+ (speed == DWC3_DSTS_SUPERSPEED_PLUS))
return 0;
- }
link_state = DWC3_DSTS_USBLNKST(reg);
@@ -1369,9 +1472,6 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
break;
default:
- dwc3_trace(trace_dwc3_gadget,
- "can't wakeup from '%s'",
- dwc3_gadget_link_string(link_state));
return -EINVAL;
}
@@ -1476,11 +1576,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
if (!timeout)
return -ETIMEDOUT;
- dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
- dwc->gadget_driver
- ? dwc->gadget_driver->function : "no-function",
- is_on ? "connect" : "disconnect");
-
return 0;
}
@@ -1492,6 +1587,21 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
is_on = !!is_on;
+ /*
+ * Per databook, when we want to stop the gadget, if a control transfer
+ * is still in process, complete it and get the core into setup phase.
+ */
+ if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+ reinit_completion(&dwc->ep0_in_setup);
+
+ ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+ msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+ if (ret == 0) {
+ dev_err(dwc->dev, "timed out waiting for SETUP phase\n");
+ return -ETIMEDOUT;
+ }
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1509,11 +1619,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
DWC3_DEVTEN_CMDCMPLTEN |
DWC3_DEVTEN_ERRTICERREN |
DWC3_DEVTEN_WKUPEVTEN |
- DWC3_DEVTEN_ULSTCNGEN |
DWC3_DEVTEN_CONNECTDONEEN |
DWC3_DEVTEN_USBRSTEN |
DWC3_DEVTEN_DISCONNEVTEN);
+ if (dwc->revision < DWC3_REVISION_250A)
+ reg |= DWC3_DEVTEN_ULSTCNGEN;
+
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}
@@ -1573,6 +1685,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
int ret = 0;
u32 reg;
+ /*
+ * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
+ * the core supports IMOD, disable it.
+ */
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ } else if (dwc3_has_imod(dwc)) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -1597,7 +1720,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
reg |= DWC3_DCFG_LOWSPEED;
break;
case USB_SPEED_FULL:
- reg |= DWC3_DCFG_FULLSPEED1;
+ reg |= DWC3_DCFG_FULLSPEED;
break;
case USB_SPEED_HIGH:
reg |= DWC3_DCFG_HIGHSPEED;
@@ -1633,16 +1756,14 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, false, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err0;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, false, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -1708,9 +1829,6 @@ err0:
static void __dwc3_gadget_stop(struct dwc3 *dwc)
{
- if (pm_runtime_suspended(dwc->dev))
- return;
-
dwc3_gadget_disable_irq(dwc);
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -1720,9 +1838,30 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int epnum;
spin_lock_irqsave(&dwc->lock, flags);
+
+ if (pm_runtime_suspended(dwc->dev))
+ goto out;
+
__dwc3_gadget_stop(dwc);
+
+ for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ struct dwc3_ep *dep = dwc->eps[epnum];
+
+ if (!dep)
+ continue;
+
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ continue;
+
+ wait_event_lock_irq(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock);
+ }
+
+out:
dwc->gadget_driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1765,9 +1904,13 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
(epnum & 1) ? "in" : "out");
dep->endpoint.name = dep->name;
- spin_lock_init(&dep->lock);
- dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
+ if (!(dep->number > 1)) {
+ dep->endpoint.desc = &dwc3_gadget_ep0_desc;
+ dep->endpoint.comp_desc = NULL;
+ }
+
+ spin_lock_init(&dep->lock);
if (epnum == 0 || epnum == 1) {
usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
@@ -1815,15 +1958,13 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
- dwc3_trace(trace_dwc3_gadget,
- "failed to allocate OUT endpoints");
+ dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
return ret;
}
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
if (ret < 0) {
- dwc3_trace(trace_dwc3_gadget,
- "failed to allocate IN endpoints");
+ dev_err(dwc->dev, "failed to initialize IN endpoints\n");
return ret;
}
@@ -1892,15 +2033,12 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
count = trb->size & DWC3_TRB_SIZE_MASK;
- req->request.actual += count;
+ req->remaining += count;
if (dep->direction) {
if (count) {
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
- dwc3_trace(trace_dwc3_gadget,
- "%s: incomplete IN transfer",
- dep->name);
/*
* If missed isoc occurred and there is
* no request queued then issue END
@@ -1946,11 +2084,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_request *req, *n;
struct dwc3_trb *trb;
bool ioc = false;
- int ret;
+ int ret = 0;
list_for_each_entry_safe(req, n, &dep->started_list, list) {
unsigned length;
- unsigned actual;
int chain;
length = req->request.length;
@@ -1964,6 +2101,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
for_each_sg(sg, s, pending, i) {
trb = &dep->trb_pool[dep->trb_dequeue];
+ if (trb->ctrl & DWC3_TRB_CTRL_HWO)
+ break;
+
req->sg = sg_next(s);
req->num_pending_sgs--;
@@ -1978,17 +2118,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}
- /*
- * We assume here we will always receive the entire data block
- * which we should receive. Meaning, if we program RX to
- * receive 4K but we receive only 2K, we assume that's all we
- * should receive and we simply bounce the request back to the
- * gadget driver for further processing.
- */
- actual = length - req->request.actual;
- req->request.actual = actual;
+ req->request.actual = length - req->remaining;
- if (ret && chain && (actual < length) && req->num_pending_sgs)
+ if ((req->request.actual < length) && req->num_pending_sgs)
return __dwc3_gadget_kick_transfer(dep, 0);
dwc3_gadget_giveback(dep, req, status);
@@ -2096,11 +2228,18 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
{
struct dwc3_ep *dep;
u8 epnum = event->endpoint_number;
+ u8 cmd;
dep = dwc->eps[epnum];
- if (!(dep->flags & DWC3_EP_ENABLED))
- return;
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return;
+
+ /* Handle only EPCMDCMPLT when EP disabled */
+ if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
+ return;
+ }
if (epnum == 0 || epnum == 1) {
dwc3_ep0_interrupt(dwc, event);
@@ -2112,9 +2251,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->resource_index = 0;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dwc3_trace(trace_dwc3_gadget,
- "%s is an Isochronous endpoint",
- dep->name);
+ dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
return;
}
@@ -2127,22 +2264,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
- int active;
int ret;
- active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
-
- dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
- dep->name, active ? "Transfer Active"
- : "Transfer Not Active");
-
ret = __dwc3_gadget_kick_transfer(dep, 0);
if (!ret || ret == -EBUSY)
return;
-
- dwc3_trace(trace_dwc3_gadget,
- "%s: failed to kick transfers",
- dep->name);
}
break;
@@ -2152,26 +2278,16 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->name);
return;
}
+ break;
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ cmd = DEPEVT_PARAMETER_CMD(event->parameters);
- switch (event->status) {
- case DEPEVT_STREAMEVT_FOUND:
- dwc3_trace(trace_dwc3_gadget,
- "Stream %d found and started",
- event->parameters);
-
- break;
- case DEPEVT_STREAMEVT_NOTFOUND:
- /* FALLTHROUGH */
- default:
- dwc3_trace(trace_dwc3_gadget,
- "unable to find suitable stream");
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ wake_up(&dep->wait_end_transfer);
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name);
- break;
- case DWC3_DEPEVT_EPCMDCMPLT:
- dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
break;
}
}
@@ -2224,7 +2340,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
dep = dwc->eps[epnum];
- if (!dep->resource_index)
+ if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
+ !dep->resource_index)
return;
/*
@@ -2268,25 +2385,9 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
dep->resource_index = 0;
dep->flags &= ~DWC3_EP_BUSY;
- if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
+ if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) {
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
udelay(100);
-}
-
-static void dwc3_stop_active_transfers(struct dwc3 *dwc)
-{
- u32 epnum;
-
- for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
- struct dwc3_ep *dep;
-
- dep = dwc->eps[epnum];
- if (!dep)
- continue;
-
- if (!(dep->flags & DWC3_EP_ENABLED))
- continue;
-
- dwc3_remove_requests(dwc, dep);
}
}
@@ -2375,8 +2476,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
dwc->test_mode = false;
-
- dwc3_stop_active_transfers(dwc);
dwc3_clear_stall_all_ep(dwc);
/* Reset device address to zero */
@@ -2385,32 +2484,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
}
-static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
-{
- u32 reg;
- u32 usb30_clock = DWC3_GCTL_CLK_BUS;
-
- /*
- * We change the clock only at SS but I dunno why I would want to do
- * this. Maybe it becomes part of the power saving plan.
- */
-
- if ((speed != DWC3_DSTS_SUPERSPEED) &&
- (speed != DWC3_DSTS_SUPERSPEED_PLUS))
- return;
-
- /*
- * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
- * each time on Connect Done.
- */
- if (!usb30_clock)
- return;
-
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock);
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
-}
-
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@@ -2422,7 +2495,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
- dwc3_update_ram_clk_sel(dwc, speed);
+ /*
+ * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
+ * each time on Connect Done.
+ *
+ * Currently we always use the reset value. If any platform
+ * wants to set this to a different value, we need to add a
+ * setting and update GCTL.RAMCLKSEL here.
+ */
switch (speed) {
case DWC3_DSTS_SUPERSPEED_PLUS:
@@ -2456,8 +2536,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_HIGH;
break;
- case DWC3_DSTS_FULLSPEED2:
- case DWC3_DSTS_FULLSPEED1:
+ case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_FULL;
@@ -2491,7 +2570,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
*/
WARN_ONCE(dwc->revision < DWC3_REVISION_240A
&& dwc->has_lpm_erratum,
- "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+ "LPM Erratum not available on dwc3 revisions < 2.40a\n");
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
@@ -2504,16 +2583,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, true, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, true, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
@@ -2570,8 +2647,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
- dwc3_trace(trace_dwc3_gadget,
- "ignoring transition U3 -> Resume");
return;
}
}
@@ -2705,11 +2780,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEVICE_EVENT_EOPF:
/* It changed to be suspend event for version 2.30a and above */
- if (dwc->revision < DWC3_REVISION_230A) {
- dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
- } else {
- dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
-
+ if (dwc->revision >= DWC3_REVISION_230A) {
/*
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
@@ -2720,16 +2791,9 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
}
break;
case DWC3_DEVICE_EVENT_SOF:
- dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
- break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- dwc3_trace(trace_dwc3_gadget, "Erratic Error");
- break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- dwc3_trace(trace_dwc3_gadget, "Command Complete");
- break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- dwc3_trace(trace_dwc3_gadget, "Overflow");
break;
default:
dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
@@ -2739,7 +2803,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
static void dwc3_process_event_entry(struct dwc3 *dwc,
const union dwc3_event *event)
{
- trace_dwc3_event(event->raw);
+ trace_dwc3_event(event->raw, dwc);
/* Endpoint IRQ, handle it and return early */
if (event->type.is_devspec == 0) {
@@ -2772,7 +2836,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
while (left > 0) {
union dwc3_event event;
- event.raw = *(u32 *) (evt->buf + evt->lpos);
+ event.raw = *(u32 *) (evt->cache + evt->lpos);
dwc3_process_event_entry(dwc, &event);
@@ -2785,10 +2849,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
* boundary so I worry about that once we try to handle
* that.
*/
- evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
+ evt->lpos = (evt->lpos + 4) % evt->length;
left -= 4;
-
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4);
}
evt->count = 0;
@@ -2800,6 +2862,11 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
reg &= ~DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ }
+
return ret;
}
@@ -2820,6 +2887,7 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
{
struct dwc3 *dwc = evt->dwc;
+ u32 amount;
u32 count;
u32 reg;
@@ -2843,6 +2911,14 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
reg |= DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+ amount = min(count, evt->length - evt->lpos);
+ memcpy(evt->cache + evt->lpos, evt->buf + evt->lpos, amount);
+
+ if (amount < count)
+ memcpy(evt->cache, evt->buf, count - amount);
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+
return IRQ_WAKE_THREAD;
}
@@ -2853,6 +2929,39 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
return dwc3_check_event_buf(evt);
}
+static int dwc3_gadget_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing peripheral IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
/**
* dwc3_gadget_init - Initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -2861,35 +2970,18 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
*/
int dwc3_gadget_init(struct dwc3 *dwc)
{
- int ret, irq;
- struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int ret;
+ int irq;
- irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
- if (irq == -EPROBE_DEFER)
- return irq;
-
- if (irq <= 0) {
- irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
- if (irq == -EPROBE_DEFER)
- return irq;
-
- if (irq <= 0) {
- irq = platform_get_irq(dwc3_pdev, 0);
- if (irq <= 0) {
- if (irq != -EPROBE_DEFER) {
- dev_err(dwc->dev,
- "missing peripheral IRQ\n");
- }
- if (!irq)
- irq = -EINVAL;
- return irq;
- }
- }
+ irq = dwc3_gadget_get_irq(dwc);
+ if (irq < 0) {
+ ret = irq;
+ goto err0;
}
dwc->irq_gadget = irq;
- dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
if (!dwc->ctrl_req) {
dev_err(dwc->dev, "failed to allocate ctrl request\n");
@@ -2897,8 +2989,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err0;
}
- dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
- &dwc->ep0_trb_addr, GFP_KERNEL);
+ dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
+ sizeof(*dwc->ep0_trb) * 2,
+ &dwc->ep0_trb_addr, GFP_KERNEL);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
@@ -2911,7 +3004,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err2;
}
- dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
+ dwc->ep0_bounce = dma_alloc_coherent(dwc->sysdev,
DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
GFP_KERNEL);
if (!dwc->ep0_bounce) {
@@ -2926,6 +3019,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err4;
}
+ init_completion(&dwc->ep0_in_setup);
+
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
@@ -2949,8 +3044,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* composite.c that we are USB 2.0 + LPM ECN.
*/
if (dwc->revision < DWC3_REVISION_220A)
- dwc3_trace(trace_dwc3_gadget,
- "Changing max_speed on rev %08x",
+ dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
dwc->gadget.max_speed = dwc->maximum_speed;
@@ -2983,18 +3077,18 @@ err5:
err4:
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
err3:
kfree(dwc->setup_buf);
err2:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
err1:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
err0:
@@ -3009,16 +3103,16 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index e4a1d974a5ae..3129bcf74d7d 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -62,10 +62,7 @@ struct dwc3;
static inline struct dwc3_request *next_request(struct list_head *list)
{
- if (list_empty(list))
- return NULL;
-
- return list_first_entry(list, struct dwc3_request, list);
+ return list_first_entry_or_null(list, struct dwc3_request, list);
}
static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index a06f9a8fecc7..c69b06696824 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -40,8 +40,7 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- dwc3_trace(trace_dwc3_readl, "addr %p value %08x",
- base - DWC3_GLOBALS_REGS_START + offset, value);
+ trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value);
return value;
}
@@ -60,8 +59,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- dwc3_trace(trace_dwc3_writel, "addr %p value %08x",
- base - DWC3_GLOBALS_REGS_START + offset, value);
+ trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value);
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 14/64] usb: dwc3: import from kernel v4.11
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (12 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 13/64] usb: dwc3: import from kernel v4.10 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 15/64] usb: dwc3: import from kernel v4.12 Jens Wiklander
` (53 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.10 to v4.11.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.10..v4.11
Commits imported:
de288e36fe33 usb: dwc3: gadget: delay unmap of bounced requests
35b2719e72d3 usb: dwc3: gadget: make to increment req->remaining in all cases
0913750f9fb6 usb: dwc3-omap: Fix missing break in dwc3_omap_set_mailbox()
1551e35ea418 usb: dwc3: gadget: Fix system suspend/resume on TI platforms
73561128eb72 usb: dwc3: Fix incorrect type for utmi mode
cf3113d893d4 usb: dwc3: gadget: properly increment dequeue pointer on ep_dequeue
7369090a9fb5 usb: dwc3: gadget: make Set Endpoint Configuration macros safe
bc46e23c3425 Merge tag 'usb-for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
e42a5dbb8a3d usb: dwc3: host: pass quirk-broken-port-ped property for known broken revisions
28781789051e usb: dwc3: gadget: read IN ep FIFO size from HW
692665c621bb Merge 4.10-rc5 into usb-next
ffb80fc672c3 usb: dwc3: gadget: skip Set/Clear Halt when invalid
538967983b88 usb: dwc3: ep0: Fix the possible missed request for handling delay STATUS phase
c773bb0b9264 usb: dwc3: omap: Replace the extcon API
c6267a51639b usb: dwc3: gadget: align transfers to wMaxPacketSize
905dc04ea796 usb: dwc3: gadget: allocate bounce buffer for unaligned xfers
e49d3cf4926a usb: dwc3: gadget: extract __dwc3_prepare_one_trb()
843053093f47 usb: dwc3: gadget: simplify dwc3_prepare_one_trb()
0e4018ff5d12 usb: dwc3: exynos: Remove MODULE_ALIAS()
3e27b3f66a56 usb: dwc3: exynos remove suspend clock unspecified debug message
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.h | 5 +
drivers/usb/dwc3/ep0.c | 14 ++
drivers/usb/dwc3/gadget.c | 287 +++++++++++++++++++++++++++++++-------
drivers/usb/dwc3/gadget.h | 14 +-
4 files changed, 260 insertions(+), 60 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 14b760209680..2b9e4ca3c932 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -40,6 +40,7 @@
/* Global constants */
#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
+#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -724,6 +725,7 @@ struct dwc3_hwparams {
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
+ * @unaligned: true for OUT endpoints with length not divisible by maxp
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
* @queued: true when request has been queued to HW
@@ -740,6 +742,7 @@ struct dwc3_request {
struct dwc3_trb *trb;
dma_addr_t trb_dma;
+ unsigned unaligned:1;
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
@@ -857,12 +860,14 @@ struct dwc3_scratchpad_array {
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
+ void *bounce;
void *ep0_bounce;
void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
+ dma_addr_t bounce_addr;
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 9bb1f8526f3e..e689cede9b0e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1123,7 +1123,21 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
+ struct dwc3_ep *dep = dwc->eps[0];
+
WARN_ON_ONCE(event->endpoint_number != 1);
+ /*
+ * We should handle the delay STATUS phase here if the
+ * request for handling delay STATUS has been queued
+ * into the list.
+ */
+ if (!list_empty(&dep->pending_list)) {
+ dwc->delayed_status = false;
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
+ dwc3_ep0_do_control_status(dwc, event);
+ }
+
return;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 204c754cc647..79e7a3480d51 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -171,6 +171,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ unsigned int unmap_after_complete = false;
req->started = false;
list_del(&req->list);
@@ -180,11 +181,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number <= 1)
+ /*
+ * NOTICE we don't want to unmap before calling ->complete() if we're
+ * dealing with a bounced ep0 request. If we unmap it here, we would end
+ * up overwritting the contents of req->buf and this could confuse the
+ * gadget driver.
+ */
+ if (dwc->ep0_bounced && dep->number <= 1) {
dwc->ep0_bounced = false;
-
- usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
+ unmap_after_complete = true;
+ } else {
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
+ }
trace_dwc3_gadget_giveback(req);
@@ -192,6 +201,10 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
+ if (unmap_after_complete)
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
+
if (dep->number > 1)
pm_runtime_put(dwc->dev);
}
@@ -833,29 +846,14 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
- struct dwc3_request *req, dma_addr_t dma,
- unsigned length, unsigned chain, unsigned node)
+static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
+ dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
+ unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
{
- struct dwc3_trb *trb;
struct dwc3 *dwc = dep->dwc;
struct usb_gadget *gadget = &dwc->gadget;
enum usb_device_speed speed = gadget->speed;
- trb = &dep->trb_pool[dep->trb_enqueue];
-
- if (!req->trb) {
- dwc3_gadget_move_started_request(req);
- req->trb = trb;
- req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- dep->queued_requests++;
- }
-
dwc3_ep_inc_enq(dep);
trb->size = DWC3_TRB_SIZE_LENGTH(length);
@@ -900,11 +898,11 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
if (usb_endpoint_dir_out(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_CSP;
- if (req->request.short_not_ok)
+ if (short_not_ok)
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
- if ((!req->request.no_interrupt && !chain) ||
+ if ((!no_interrupt && !chain) ||
(dwc3_calc_trbs_left(dep) == 0))
trb->ctrl |= DWC3_TRB_CTRL_IOC;
@@ -912,13 +910,43 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl |= DWC3_TRB_CTRL_CHN;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
- trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
+ trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
trb->ctrl |= DWC3_TRB_CTRL_HWO;
trace_dwc3_prepare_trb(dep, trb);
}
+/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned chain, unsigned node)
+{
+ struct dwc3_trb *trb;
+ unsigned length = req->request.length;
+ unsigned stream_id = req->request.stream_id;
+ unsigned short_not_ok = req->request.short_not_ok;
+ unsigned no_interrupt = req->request.no_interrupt;
+ dma_addr_t dma = req->request.dma;
+
+ trb = &dep->trb_pool[dep->trb_enqueue];
+
+ if (!req->trb) {
+ dwc3_gadget_move_started_request(req);
+ req->trb = trb;
+ req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+ dep->queued_requests++;
+ }
+
+ __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
+ stream_id, short_not_ok, no_interrupt);
+}
+
/**
* dwc3_ep_prev_trb() - Returns the previous TRB in the ring
* @dep: The endpoint with the TRB ring
@@ -974,21 +1002,36 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
{
struct scatterlist *sg = req->sg;
struct scatterlist *s;
- unsigned int length;
- dma_addr_t dma;
int i;
for_each_sg(sg, s, req->num_pending_sgs, i) {
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
unsigned chain = true;
- length = sg_dma_len(s);
- dma = sg_dma_address(s);
-
if (sg_is_last(s))
chain = false;
- dwc3_prepare_one_trb(dep, req, dma, length,
- chain, i);
+ if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->unaligned = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, i);
+
+ /* Now prepare one extra TRB to align transfer size */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
+ maxp - rem, false, 0,
+ req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, chain, i);
+ }
if (!dwc3_calc_trbs_left(dep))
break;
@@ -998,14 +1041,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- unsigned int length;
- dma_addr_t dma;
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
- dma = req->request.dma;
- length = req->request.length;
+ if (rem && usb_endpoint_dir_out(dep->endpoint.desc)) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->unaligned = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, 0);
- dwc3_prepare_one_trb(dep, req, dma, length,
- false, 0);
+ /* Now prepare one extra TRB to align transfer size */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
+ false, 0, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, false, 0);
+ }
}
/*
@@ -1298,6 +1355,68 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
if (r == req) {
/* wait until it is processed */
dwc3_stop_active_transfer(dwc, dep->number, true);
+
+ /*
+ * If request was already started, this means we had to
+ * stop the transfer. With that we also need to ignore
+ * all TRBs used by the request, however TRBs can only
+ * be modified after completion of END_TRANSFER
+ * command. So what we do here is that we wait for
+ * END_TRANSFER completion and only after that, we jump
+ * over TRBs by clearing HWO and incrementing dequeue
+ * pointer.
+ *
+ * Note that we have 2 possible types of transfers here:
+ *
+ * i) Linear buffer request
+ * ii) SG-list based request
+ *
+ * SG-list based requests will have r->num_pending_sgs
+ * set to a valid number (> 0). Linear requests,
+ * normally use a single TRB.
+ *
+ * For each of these two cases, if r->unaligned flag is
+ * set, one extra TRB has been used to align transfer
+ * size to wMaxPacketSize.
+ *
+ * All of these cases need to be taken into
+ * consideration so we don't mess up our TRB ring
+ * pointers.
+ */
+ wait_event_lock_irq(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock);
+
+ if (!r->trb)
+ goto out1;
+
+ if (r->num_pending_sgs) {
+ struct dwc3_trb *trb;
+ int i = 0;
+
+ for (i = 0; i < r->num_pending_sgs; i++) {
+ trb = r->trb + i;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+
+ if (r->unaligned) {
+ trb = r->trb + r->num_pending_sgs + 1;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+ } else {
+ struct dwc3_trb *trb = r->trb;
+
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+
+ if (r->unaligned) {
+ trb = r->trb + 1;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+ }
goto out1;
}
dev_err(dwc->dev, "request %p was not queued to %s\n",
@@ -1308,6 +1427,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
out1:
/* giveback the request */
+ dep->queued_requests--;
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
@@ -1335,6 +1455,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
unsigned transfer_in_flight;
unsigned started;
+ if (dep->flags & DWC3_EP_STALL)
+ return 0;
+
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
else
@@ -1356,6 +1479,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
+ if (!(dep->flags & DWC3_EP_STALL))
+ return 0;
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
@@ -1918,6 +2043,44 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
dwc->gadget.ep0 = &dep->endpoint;
+ } else if (direction) {
+ int mdwidth;
+ int size;
+ int ret;
+ int num;
+
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth /= 8;
+
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(i));
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+
+ /* FIFO Depth is in MDWDITH bytes. Multiply */
+ size *= mdwidth;
+
+ num = size / 1024;
+ if (num == 0)
+ num = 1;
+
+ /*
+ * FIFO sizes account an extra MDWIDTH * (num + 1) bytes for
+ * internal overhead. We don't really know how these are used,
+ * but documentation say it exists.
+ */
+ size -= mdwidth * (num + 1);
+ size /= num;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+
+ ret = dwc3_alloc_trb_pool(dep);
+ if (ret)
+ return ret;
} else {
int ret;
@@ -2029,12 +2192,22 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+ /*
+ * If we're dealing with unaligned size OUT transfer, we will be left
+ * with one TRB pending in the ring. We need to manually clear HWO bit
+ * from that TRB.
+ */
+ if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
+ }
count = trb->size & DWC3_TRB_SIZE_MASK;
req->remaining += count;
+ if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+ return 1;
+
if (dep->direction) {
if (count) {
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
@@ -2118,6 +2291,13 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}
+ if (req->unaligned) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status, false);
+ req->unaligned = false;
+ }
+
req->request.actual = length - req->remaining;
if ((req->request.actual < length) && req->num_pending_sgs)
@@ -3019,6 +3199,13 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err4;
}
+ dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
+ &dwc->bounce_addr, GFP_KERNEL);
+ if (!dwc->bounce) {
+ ret = -ENOMEM;
+ goto err5;
+ }
+
init_completion(&dwc->ep0_in_setup);
dwc->gadget.ops = &dwc3_gadget_ops;
@@ -3049,12 +3236,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.max_speed = dwc->maximum_speed;
- /*
- * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
- * on ep out.
- */
- dwc->gadget.quirk_ep_out_aligned_size = true;
-
/*
* REVISIT: Here we should clear all pending IRQs to be
* sure we're starting from a well known location.
@@ -3062,15 +3243,18 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err5;
+ goto err6;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err5;
+ goto err6;
}
return 0;
+err6:
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
err5:
kfree(dwc->zlp_buf);
@@ -3103,6 +3287,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_gadget_free_endpoints(dwc);
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
@@ -3118,15 +3304,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
- int ret;
-
if (!dwc->gadget_driver)
return 0;
- ret = dwc3_gadget_run_stop(dwc, false, false);
- if (ret < 0)
- return ret;
-
+ dwc3_gadget_run_stop(dwc, false, false);
dwc3_disconnect_gadget(dwc);
__dwc3_gadget_stop(dwc);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 3129bcf74d7d..265e223ab645 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -28,23 +28,23 @@ struct dwc3;
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
/* DEPCFG parameter 1 */
-#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
-#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
-#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
/* DEPCFG parameter 0 */
-#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
-#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
-#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
-#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 15/64] usb: dwc3: import from kernel v4.12
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (13 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 14/64] usb: dwc3: import from kernel v4.11 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 16/64] usb: dwc3: import from kernel v4.13 Jens Wiklander
` (52 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.11 to v4.12.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.11..v4.12
Commits imported:
018047a1dba7 usb: dwc3: keystone: check return value
d325a1de49d6 usb: dwc3: gadget: Prevent losing events in event cache
f1d6826cae30 usb: dwc3: gadget: Fix ISO transfer performance
682179592e48 usb: dwc3: pci: add Intel Cannonlake PCI IDs
8f28472a739e Merge tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
2bd804017435 Merge tag 'gpio-v4.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
ba7756d08212 Merge tag 'usb-for-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
9840354ff429 usb: dwc3: Add dual-role support
41ce1456e1db usb: dwc3: core: make dwc3_set_mode() work properly
b202c42cbf81 usb: dwc3: debugfs: remove unnecessary | operator
6b3261a27428 usb: dwc3: core: add current_dr_role member
d6e5a549cc4d usb: dwc3: simplify ZLP handling
4199c5f8bccd usb: dwc3: ep0: improve handling of unaligned OUT requests
d686a5ff6a2d usb: dwc3: ep0: pass dep as argument to internal functions
7d5e650a5fdb usb: dwc3: ep0: use immediate SETUP on TRB
b5c7ed5cca82 usb: dwc3: expose dwc3_trb_type_string()
90abb425d949 usb: dwc3: debugfs: return strings that match tracepoints
26c9f3e391d1 usb: dwc3: debugfs: make use of dwc3_gadget_link_string()
2df301cd3d71 usb: dwc3: debugfs: downcase OTG on 'mode' file
ff3f0789b3dc usb: dwc3: use BIT() macro where possible
aad7c2596495 usb: dwc3: trace: change format for string to cmd trace
af771d731b8e usb: dwc3: gadget: Fix starting microframe for ISOC
3a932b0f50f4 usb: dwc3: exynos: change goto labels to meaningful names
f3bcfc7e68f0 usb: dwc3: remove dwc3_gadget_init_hw_endpoints
47d3946ea220 usb: dwc3: refactor gadget endpoint count calculation
8261bd4e91e2 usb: dwc3: make macros safe to expression arguments
05428ba983e6 usb: dwc3: remove dwc3_log_msg trace class
1966b8657d05 usb: dwc3: make sure UX_EXIT_PX is cleared
e47ff590cc57 Merge 4.11-rc4 into usb-next
4a56e413b50c usb: dwc3: pci: Switch to devm_acpi_dev_add_driver_gpios()
2c93e790e825 usb: add CONFIG_USB_PCI for system have both PCI HW and non-PCI based USB HW
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 109 +++++++++++-----
drivers/usb/dwc3/core.h | 261 ++++++++++++++++++++------------------
drivers/usb/dwc3/debug.h | 28 ++++
drivers/usb/dwc3/ep0.c | 151 ++++++++++------------
drivers/usb/dwc3/gadget.c | 218 +++++++++++--------------------
drivers/usb/dwc3/gadget.h | 20 +--
6 files changed, 392 insertions(+), 395 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 369bab16a824..455d89a1cd6d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -100,7 +100,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
-void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
+static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+static int dwc3_event_buffers_setup(struct dwc3 *dwc);
+
+static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -110,6 +113,69 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
+static void __dwc3_set_mode(struct work_struct *work)
+{
+ struct dwc3 *dwc = work_to_dwc(work);
+ unsigned long flags;
+ int ret;
+
+ if (!dwc->desired_dr_role)
+ return;
+
+ if (dwc->desired_dr_role == dwc->current_dr_role)
+ return;
+
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_gadget_exit(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc3_set_prtcap(dwc, dwc->desired_dr_role);
+
+ dwc->current_dr_role = dwc->desired_dr_role;
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (dwc->desired_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ ret = dwc3_host_init(dwc);
+ if (ret)
+ dev_err(dwc->dev, "failed to initialize host\n");
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_event_buffers_setup(dwc);
+ ret = dwc3_gadget_init(dwc);
+ if (ret)
+ dev_err(dwc->dev, "failed to initialize peripheral\n");
+ break;
+ default:
+ break;
+ }
+}
+
+void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_dr_role = mode;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ queue_work(system_power_efficient_wq, &dwc->drd_work);
+}
+
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
{
struct dwc3 *dwc = dep->dwc;
@@ -397,8 +463,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
- dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
- dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
+ dwc->num_eps = DWC3_NUM_EPS(parms);
}
static void dwc3_cache_hwparams(struct dwc3 *dwc)
@@ -431,6 +496,12 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ /*
+ * Make sure UX_EXIT_PX is cleared as that causes issues with some
+ * PHYs. Also, this bit is not supposed to be used in normal operation.
+ */
+ reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
+
/*
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
* to '0' during coreConsultant configuration. So default value
@@ -714,21 +785,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err4;
}
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- break;
- case USB_DR_MODE_HOST:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
- break;
- case USB_DR_MODE_OTG:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
- break;
- default:
- dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
- break;
- }
-
/*
* ENDXFER polling is available on version 3.10a and later of
* the DWC_usb3 controller. It is NOT available in the
@@ -846,6 +902,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -854,6 +911,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_HOST:
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -862,17 +920,11 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
- ret = dwc3_host_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
-
- ret = dwc3_gadget_init(dwc);
+ INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
+ ret = dwc3_drd_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize gadget\n");
+ dev_err(dev, "failed to initialize dual-role\n");
return ret;
}
break;
@@ -894,8 +946,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_host_exit(dwc);
break;
case USB_DR_MODE_OTG:
- dwc3_host_exit(dwc);
- dwc3_gadget_exit(dwc);
+ dwc3_drd_exit(dwc);
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 2b9e4ca3c932..981c77f5628e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -23,10 +23,12 @@
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <linux/list.h>
+#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/debugfs.h>
#include <linux/wait.h>
+#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -39,9 +41,8 @@
/* Global constants */
#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
-#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
-#define DWC3_EP0_BOUNCE_SIZE 512
+#define DWC3_EP0_SETUP_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -66,7 +67,7 @@
#define DWC3_DEVICE_EVENT_OVERFLOW 11
#define DWC3_GEVNTCOUNT_MASK 0xfffc
-#define DWC3_GEVNTCOUNT_EHB (1 << 31)
+#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
@@ -116,20 +117,20 @@
#define DWC3_VER_NUMBER 0xc1a0
#define DWC3_VER_TYPE 0xc1a4
-#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
-#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04))
+#define DWC3_GUSB2PHYCFG(n) (0xc200 + ((n) * 0x04))
+#define DWC3_GUSB2I2CCTL(n) (0xc240 + ((n) * 0x04))
-#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04))
+#define DWC3_GUSB2PHYACC(n) (0xc280 + ((n) * 0x04))
-#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04))
+#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + ((n) * 0x04))
-#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04))
-#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04))
+#define DWC3_GTXFIFOSIZ(n) (0xc300 + ((n) * 0x04))
+#define DWC3_GRXFIFOSIZ(n) (0xc380 + ((n) * 0x04))
-#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10))
-#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10))
-#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10))
-#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
+#define DWC3_GEVNTADRLO(n) (0xc400 + ((n) * 0x10))
+#define DWC3_GEVNTADRHI(n) (0xc404 + ((n) * 0x10))
+#define DWC3_GEVNTSIZ(n) (0xc408 + ((n) * 0x10))
+#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
#define DWC3_GHWPARAMS8 0xc600
#define DWC3_GFLADJ 0xc630
@@ -143,13 +144,13 @@
#define DWC3_DGCMD 0xc714
#define DWC3_DALEPENA 0xc720
-#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10))
+#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
#define DWC3_DEPCMDPAR2 0x00
#define DWC3_DEPCMDPAR1 0x04
#define DWC3_DEPCMDPAR0 0x08
#define DWC3_DEPCMD 0x0c
-#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
+#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
/* OTG Registers */
#define DWC3_OCFG 0xcc00
@@ -176,11 +177,11 @@
/* Global RX Threshold Configuration Register */
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
-#define DWC3_GRXTHRCFG_PKTCNTSEL (1 << 29)
+#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
-#define DWC3_GCTL_U2RSTECN (1 << 16)
+#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
#define DWC3_GCTL_CLK_PIPE (1)
@@ -193,24 +194,24 @@
#define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3
-#define DWC3_GCTL_CORESOFTRESET (1 << 11)
-#define DWC3_GCTL_SOFITPSYNC (1 << 10)
+#define DWC3_GCTL_CORESOFTRESET BIT(11)
+#define DWC3_GCTL_SOFITPSYNC BIT(10)
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
-#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
-#define DWC3_GCTL_U2EXIT_LFPS (1 << 2)
-#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
-#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+#define DWC3_GCTL_DISSCRAMBLE BIT(3)
+#define DWC3_GCTL_U2EXIT_LFPS BIT(2)
+#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
+#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
/* Global User Control 1 Register */
-#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
/* Global USB2 PHY Configuration Register */
-#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
-#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
-#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
-#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
+#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
+#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
+#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
+#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
@@ -221,25 +222,26 @@
#define UTMI_PHYIF_8_BIT 0
/* Global USB2 PHY Vendor Control Register */
-#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
-#define DWC3_GUSB2PHYACC_BUSY (1 << 23)
-#define DWC3_GUSB2PHYACC_WRITE (1 << 22)
+#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25)
+#define DWC3_GUSB2PHYACC_BUSY BIT(23)
+#define DWC3_GUSB2PHYACC_WRITE BIT(22)
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
/* Global USB3 PIPE Control Register */
-#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
-#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
-#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST BIT(31)
+#define DWC3_GUSB3PIPECTL_U2SSINP3OK BIT(29)
+#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28)
+#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27)
+#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(24)
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1)
-#define DWC3_GUSB3PIPECTL_DEPOCHANGE (1 << 18)
-#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
-#define DWC3_GUSB3PIPECTL_LFPSFILT (1 << 9)
-#define DWC3_GUSB3PIPECTL_RX_DETOPOLL (1 << 8)
+#define DWC3_GUSB3PIPECTL_DEPOCHANGE BIT(18)
+#define DWC3_GUSB3PIPECTL_SUSPHY BIT(17)
+#define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9)
+#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8)
#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
@@ -248,7 +250,7 @@
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
/* Global Event Size Registers */
-#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
+#define DWC3_GEVNTSIZ_INTMASK BIT(31)
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
/* Global HWPARAMS0 Register */
@@ -289,18 +291,18 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
-#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
+#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* Global HWPARAMS7 Register */
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
/* Global Frame Length Adjustment Register */
-#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
+#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
/* Global User Control Register 2 */
-#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
+#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
@@ -310,23 +312,23 @@
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
-#define DWC3_DCFG_FULLSPEED (1 << 0)
+#define DWC3_DCFG_FULLSPEED BIT(0)
#define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
-#define DWC3_DCFG_LPM_CAP (1 << 22)
+#define DWC3_DCFG_LPM_CAP BIT(22)
/* Device Control Register */
-#define DWC3_DCTL_RUN_STOP (1 << 31)
-#define DWC3_DCTL_CSFTRST (1 << 30)
-#define DWC3_DCTL_LSFTRST (1 << 29)
+#define DWC3_DCTL_RUN_STOP BIT(31)
+#define DWC3_DCTL_CSFTRST BIT(30)
+#define DWC3_DCTL_LSFTRST BIT(29)
#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
-#define DWC3_DCTL_APPL1RES (1 << 23)
+#define DWC3_DCTL_APPL1RES BIT(23)
/* These apply for core versions 1.87a and earlier */
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
@@ -341,15 +343,15 @@
#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
-#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
-#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
-#define DWC3_DCTL_CRS (1 << 17)
-#define DWC3_DCTL_CSS (1 << 16)
+#define DWC3_DCTL_KEEP_CONNECT BIT(19)
+#define DWC3_DCTL_L1_HIBER_EN BIT(18)
+#define DWC3_DCTL_CRS BIT(17)
+#define DWC3_DCTL_CSS BIT(16)
-#define DWC3_DCTL_INITU2ENA (1 << 12)
-#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
-#define DWC3_DCTL_INITU1ENA (1 << 10)
-#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
+#define DWC3_DCTL_INITU2ENA BIT(12)
+#define DWC3_DCTL_ACCEPTU2ENA BIT(11)
+#define DWC3_DCTL_INITU1ENA BIT(10)
+#define DWC3_DCTL_ACCEPTU1ENA BIT(9)
#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
@@ -364,36 +366,36 @@
#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11))
/* Device Event Enable Register */
-#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12)
-#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11)
-#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10)
-#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
-#define DWC3_DEVTEN_SOFEN (1 << 7)
-#define DWC3_DEVTEN_EOPFEN (1 << 6)
-#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
-#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
-#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
-#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
-#define DWC3_DEVTEN_USBRSTEN (1 << 1)
-#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
+#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN BIT(12)
+#define DWC3_DEVTEN_EVNTOVERFLOWEN BIT(11)
+#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
+#define DWC3_DEVTEN_ERRTICERREN BIT(9)
+#define DWC3_DEVTEN_SOFEN BIT(7)
+#define DWC3_DEVTEN_EOPFEN BIT(6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
+#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
+#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
+#define DWC3_DEVTEN_CONNECTDONEEN BIT(2)
+#define DWC3_DEVTEN_USBRSTEN BIT(1)
+#define DWC3_DEVTEN_DISCONNEVTEN BIT(0)
/* Device Status Register */
-#define DWC3_DSTS_DCNRD (1 << 29)
+#define DWC3_DSTS_DCNRD BIT(29)
/* This applies for core versions 1.87a and earlier */
-#define DWC3_DSTS_PWRUPREQ (1 << 24)
+#define DWC3_DSTS_PWRUPREQ BIT(24)
/* These apply for core versions 1.94a and later */
-#define DWC3_DSTS_RSS (1 << 25)
-#define DWC3_DSTS_SSS (1 << 24)
+#define DWC3_DSTS_RSS BIT(25)
+#define DWC3_DSTS_SSS BIT(24)
-#define DWC3_DSTS_COREIDLE (1 << 23)
-#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
+#define DWC3_DSTS_COREIDLE BIT(23)
+#define DWC3_DSTS_DEVCTRLHLT BIT(22)
#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18)
#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18)
-#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
+#define DWC3_DSTS_RXFIFOEMPTY BIT(17)
#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
@@ -403,7 +405,7 @@
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
-#define DWC3_DSTS_FULLSPEED (1 << 0)
+#define DWC3_DSTS_FULLSPEED BIT(0)
#define DWC3_DSTS_LOWSPEED (2 << 0)
/* Device Generic Command Register */
@@ -421,26 +423,26 @@
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
-#define DWC3_DGCMD_CMDACT (1 << 10)
-#define DWC3_DGCMD_CMDIOC (1 << 8)
+#define DWC3_DGCMD_CMDACT BIT(10)
+#define DWC3_DGCMD_CMDIOC BIT(8)
/* Device Generic Command Parameter Register */
-#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT BIT(0)
#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
-#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO BIT(5)
#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
-#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0)
/* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
-#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
-#define DWC3_DEPCMD_CLEARPENDIN (1 << 11)
-#define DWC3_DEPCMD_CMDACT (1 << 10)
-#define DWC3_DEPCMD_CMDIOC (1 << 8)
+#define DWC3_DEPCMD_HIPRI_FORCERM BIT(11)
+#define DWC3_DEPCMD_CLEARPENDIN BIT(11)
+#define DWC3_DEPCMD_CMDACT BIT(10)
+#define DWC3_DEPCMD_CMDIOC BIT(8)
#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)
#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)
@@ -458,7 +460,7 @@
#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
-#define DWC3_DALEPENA_EP(n) (1 << n)
+#define DWC3_DALEPENA_EP(n) BIT(n)
#define DWC3_DEPCMD_TYPE_CONTROL 0
#define DWC3_DEPCMD_TYPE_ISOC 1
@@ -500,8 +502,8 @@ struct dwc3_event_buffer {
struct dwc3 *dwc;
};
-#define DWC3_EP_FLAG_STALLED (1 << 0)
-#define DWC3_EP_FLAG_WEDGED (1 << 1)
+#define DWC3_EP_FLAG_STALLED BIT(0)
+#define DWC3_EP_FLAG_WEDGED BIT(1)
#define DWC3_EP_DIRECTION_TX true
#define DWC3_EP_DIRECTION_RX false
@@ -550,17 +552,17 @@ struct dwc3_ep {
u32 saved_state;
unsigned flags;
-#define DWC3_EP_ENABLED (1 << 0)
-#define DWC3_EP_STALL (1 << 1)
-#define DWC3_EP_WEDGE (1 << 2)
-#define DWC3_EP_BUSY (1 << 4)
-#define DWC3_EP_PENDING_REQUEST (1 << 5)
-#define DWC3_EP_MISSED_ISOC (1 << 6)
-#define DWC3_EP_END_TRANSFER_PENDING (1 << 7)
-#define DWC3_EP_TRANSFER_STARTED (1 << 8)
+#define DWC3_EP_ENABLED BIT(0)
+#define DWC3_EP_STALL BIT(1)
+#define DWC3_EP_WEDGE BIT(2)
+#define DWC3_EP_BUSY BIT(4)
+#define DWC3_EP_PENDING_REQUEST BIT(5)
+#define DWC3_EP_MISSED_ISOC BIT(6)
+#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
+#define DWC3_EP_TRANSFER_STARTED BIT(8)
/* This last one is specific to EP0 */
-#define DWC3_EP0_DIR_IN (1 << 31)
+#define DWC3_EP0_DIR_IN BIT(31)
/*
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
@@ -638,13 +640,13 @@ enum dwc3_link_state {
#define DWC3_TRB_STS_XFER_IN_PROG 4
/* TRB Control */
-#define DWC3_TRB_CTRL_HWO (1 << 0)
-#define DWC3_TRB_CTRL_LST (1 << 1)
-#define DWC3_TRB_CTRL_CHN (1 << 2)
-#define DWC3_TRB_CTRL_CSP (1 << 3)
+#define DWC3_TRB_CTRL_HWO BIT(0)
+#define DWC3_TRB_CTRL_LST BIT(1)
+#define DWC3_TRB_CTRL_CHN BIT(2)
+#define DWC3_TRB_CTRL_CSP BIT(3)
#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4)
-#define DWC3_TRB_CTRL_ISP_IMI (1 << 10)
-#define DWC3_TRB_CTRL_IOC (1 << 11)
+#define DWC3_TRB_CTRL_ISP_IMI BIT(10)
+#define DWC3_TRB_CTRL_IOC BIT(11)
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
@@ -746,6 +748,7 @@ struct dwc3_request {
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
+ unsigned zero:1;
};
/*
@@ -758,15 +761,11 @@ struct dwc3_scratchpad_array {
/**
* struct dwc3 - representation of our controller
- * @ctrl_req: usb control request which is used for ep0
+ * @drd_work - workqueue used for role swapping
* @ep0_trb: trb which is used for the ctrl_req
- * @ep0_bounce: bounce buffer for ep0
- * @zlp_buf: used when request->zero is set
* @setup_buf: used while precessing STD USB requests
- * @ctrl_req_addr: dma address of ctrl_req
* @ep0_trb: dma address of ep0_trb
* @ep0_usb_req: dummy req used while handling STD USB requests
- * @ep0_bounce_addr: dma address of ep0_bounce
* @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
@@ -784,6 +783,10 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @current_dr_role: current role of operation when in dual-role mode
+ * @desired_dr_role: desired role of operation when in dual-role mode
+ * @edev: extcon handle
+ * @edev_nb: extcon notifier
* @hsphy_mode: UTMI phy mode, one of following:
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
@@ -799,8 +802,7 @@ struct dwc3_scratchpad_array {
* @u2pel: parameter from Set SEL request.
* @u1sel: parameter from Set SEL request.
* @u1pel: parameter from Set SEL request.
- * @num_out_eps: number of out endpoints
- * @num_in_eps: number of in endpoints
+ * @num_eps: number of endpoints
* @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero
* @link_state: link state
@@ -858,17 +860,13 @@ struct dwc3_scratchpad_array {
* increments or 0 to disable.
*/
struct dwc3 {
- struct usb_ctrlrequest *ctrl_req;
+ struct work_struct drd_work;
struct dwc3_trb *ep0_trb;
void *bounce;
- void *ep0_bounce;
- void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
- dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
dma_addr_t bounce_addr;
- dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
struct completion ep0_in_setup;
@@ -900,6 +898,10 @@ struct dwc3 {
size_t regs_size;
enum usb_dr_mode dr_mode;
+ u32 current_dr_role;
+ u32 desired_dr_role;
+ struct extcon_dev *edev;
+ struct notifier_block edev_nb;
enum usb_phy_interface hsphy_mode;
u32 fladj;
@@ -960,8 +962,7 @@ struct dwc3 {
u8 speed;
- u8 num_out_eps;
- u8 num_in_eps;
+ u8 num_eps;
struct dwc3_hwparams hwparams;
struct dentry *root;
@@ -1010,7 +1011,7 @@ struct dwc3 {
u16 imod_interval;
};
-/* -------------------------------------------------------------------------- */
+#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
/* -------------------------------------------------------------------------- */
@@ -1054,13 +1055,13 @@ struct dwc3_event_depevt {
u32 status:4;
/* Within XferNotReady */
-#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3)
+#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3)
/* Within XferComplete */
-#define DEPEVT_STATUS_BUSERR (1 << 0)
-#define DEPEVT_STATUS_SHORT (1 << 1)
-#define DEPEVT_STATUS_IOC (1 << 2)
-#define DEPEVT_STATUS_LST (1 << 3)
+#define DEPEVT_STATUS_BUSERR BIT(0)
+#define DEPEVT_STATUS_SHORT BIT(1)
+#define DEPEVT_STATUS_IOC BIT(2)
+#define DEPEVT_STATUS_LST BIT(3)
/* Stream event only */
#define DEPEVT_STREAMEVT_FOUND 1
@@ -1221,6 +1222,16 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
#endif
+#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_drd_init(struct dwc3 *dwc);
+void dwc3_drd_exit(struct dwc3 *dwc);
+#else
+static inline int dwc3_drd_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_drd_exit(struct dwc3 *dwc)
+{ }
+#endif
+
/* power management interface */
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_suspend(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index eeed4ffd8131..cb2d8d3f7f3d 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -124,6 +124,34 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
}
+/**
+ * dwc3_trb_type_string - returns TRB type as a string
+ * @type: the type of the TRB
+ */
+static inline const char *dwc3_trb_type_string(unsigned int type)
+{
+ switch (type) {
+ case DWC3_TRBCTL_NORMAL:
+ return "normal";
+ case DWC3_TRBCTL_CONTROL_SETUP:
+ return "setup";
+ case DWC3_TRBCTL_CONTROL_STATUS2:
+ return "status2";
+ case DWC3_TRBCTL_CONTROL_STATUS3:
+ return "status3";
+ case DWC3_TRBCTL_CONTROL_DATA:
+ return "data";
+ case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
+ return "isoc-first";
+ case DWC3_TRBCTL_ISOCHRONOUS:
+ return "isoc";
+ case DWC3_TRBCTL_LINK_TRB:
+ return "link";
+ default:
+ return "UNKNOWN";
+ }
+}
+
static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
switch (state) {
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index e689cede9b0e..a78c78e7a8c3 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -39,14 +39,13 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
-static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
+static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
struct dwc3_trb *trb;
- struct dwc3_ep *dep;
-
- dep = dwc->eps[epnum];
+ struct dwc3 *dwc;
+ dwc = dep->dwc;
trb = &dwc->ep0_trb[dep->trb_enqueue];
if (chain)
@@ -69,16 +68,17 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
trace_dwc3_prepare_trb(dep, trb);
}
-static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
+static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3_ep *dep;
+ struct dwc3 *dwc;
int ret;
- dep = dwc->eps[epnum];
if (dep->flags & DWC3_EP_BUSY)
return 0;
+ dwc = dep->dwc;
+
memset(¶ms, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
@@ -279,13 +279,15 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
void dwc3_ep0_out_start(struct dwc3 *dwc)
{
+ struct dwc3_ep *dep;
int ret;
complete(&dwc->ep0_in_setup);
- dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
+ dep = dwc->eps[0];
+ dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
- ret = dwc3_ep0_start_trans(dwc, 0);
+ ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0);
}
@@ -794,7 +796,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
+ struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb;
int ret = -EINVAL;
u32 len;
@@ -834,7 +836,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
- unsigned transfer_size = 0;
unsigned maxp;
unsigned remaining_ur_length;
void *buf;
@@ -847,9 +848,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ep0 = dwc->eps[0];
dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
-
trb = dwc->ep0_trb;
-
trace_dwc3_complete_trb(ep0, trb);
r = next_request(&ep0->pending_list);
@@ -870,58 +869,23 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
remaining_ur_length = ur->length;
length = trb->size & DWC3_TRB_SIZE_MASK;
-
maxp = ep0->endpoint.maxpacket;
-
- if (dwc->ep0_bounced) {
- /*
- * Handle the first TRB before handling the bounce buffer if
- * the request length is greater than the bounce buffer size
- */
- if (ur->length > DWC3_EP0_BOUNCE_SIZE) {
- transfer_size = ALIGN(ur->length - maxp, maxp);
- transferred = transfer_size - length;
- buf = (u8 *)buf + transferred;
- ur->actual += transferred;
- remaining_ur_length -= transferred;
-
- trb++;
- length = trb->size & DWC3_TRB_SIZE_MASK;
-
- ep0->trb_enqueue = 0;
- }
-
- transfer_size = roundup((ur->length - transfer_size),
- maxp);
-
- transferred = min_t(u32, remaining_ur_length,
- transfer_size - length);
- memcpy(buf, dwc->ep0_bounce, transferred);
- } else {
- transferred = ur->length - length;
- }
-
+ transferred = ur->length - length;
ur->actual += transferred;
- if ((epnum & 1) && ur->actual < ur->length) {
- /* for some reason we did not get everything out */
+ if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
+ ur->length && ur->zero) || dwc->ep0_bounced) {
+ trb++;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ trace_dwc3_complete_trb(ep0, trb);
+ ep0->trb_enqueue = 0;
+ dwc->ep0_bounced = false;
+ }
+ if ((epnum & 1) && ur->actual < ur->length)
dwc3_ep0_stall_and_restart(dwc);
- } else {
+ else
dwc3_gadget_giveback(ep0, r, 0);
-
- if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
- ur->length && ur->zero) {
- int ret;
-
- dwc->ep0_next_event = DWC3_EP0_COMPLETE;
-
- dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
- 0, DWC3_TRBCTL_CONTROL_DATA, false);
- ret = dwc3_ep0_start_trans(dwc, epnum);
- WARN_ON(ret < 0);
- }
- }
}
static void dwc3_ep0_complete_status(struct dwc3 *dwc,
@@ -997,14 +961,13 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->direction = !!dep->number;
if (req->request.length == 0) {
- dwc3_ep0_prepare_one_trb(dwc, dep->number,
- dwc->ctrl_req_addr, 0,
+ dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
- ret = dwc3_ep0_start_trans(dwc, dep->number);
+ ret = dwc3_ep0_start_trans(dep);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
- u32 transfer_size = 0;
u32 maxpacket;
+ u32 rem;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
@@ -1012,36 +975,55 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
maxpacket = dep->endpoint.maxpacket;
+ rem = req->request.length % maxpacket;
+ dwc->ep0_bounced = true;
- if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
- transfer_size = ALIGN(req->request.length - maxpacket,
- maxpacket);
- dwc3_ep0_prepare_one_trb(dwc, dep->number,
- req->request.dma,
- transfer_size,
- DWC3_TRBCTL_CONTROL_DATA,
- true);
- }
-
- transfer_size = roundup((req->request.length - transfer_size),
- maxpacket);
+ /* prepare normal TRB */
+ dwc3_ep0_prepare_one_trb(dep, req->request.dma,
+ req->request.length,
+ DWC3_TRBCTL_CONTROL_DATA,
+ true);
+
+ /* Now prepare one extra TRB to align transfer size */
+ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
+ maxpacket - rem,
+ DWC3_TRBCTL_CONTROL_DATA,
+ false);
+ ret = dwc3_ep0_start_trans(dep);
+ } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
+ req->request.length && req->request.zero) {
+ u32 maxpacket;
+ u32 rem;
- dwc->ep0_bounced = true;
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
+ return;
- dwc3_ep0_prepare_one_trb(dwc, dep->number,
- dwc->ep0_bounce_addr, transfer_size,
- DWC3_TRBCTL_CONTROL_DATA, false);
- ret = dwc3_ep0_start_trans(dwc, dep->number);
+ maxpacket = dep->endpoint.maxpacket;
+ rem = req->request.length % maxpacket;
+
+ /* prepare normal TRB */
+ dwc3_ep0_prepare_one_trb(dep, req->request.dma,
+ req->request.length,
+ DWC3_TRBCTL_CONTROL_DATA,
+ true);
+
+ /* Now prepare one extra TRB to align transfer size */
+ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA,
+ false);
+ ret = dwc3_ep0_start_trans(dep);
} else {
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
if (ret)
return;
- dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
+ dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
- ret = dwc3_ep0_start_trans(dwc, dep->number);
+ ret = dwc3_ep0_start_trans(dep);
}
WARN_ON(ret < 0);
@@ -1055,9 +1037,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
- dwc3_ep0_prepare_one_trb(dwc, dep->number,
- dwc->ctrl_req_addr, 0, type, false);
- return dwc3_ep0_start_trans(dwc, dep->number);
+ dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dep);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 79e7a3480d51..aea9a5b948b4 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -171,7 +171,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
- unsigned int unmap_after_complete = false;
req->started = false;
list_del(&req->list);
@@ -181,19 +180,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- /*
- * NOTICE we don't want to unmap before calling ->complete() if we're
- * dealing with a bounced ep0 request. If we unmap it here, we would end
- * up overwritting the contents of req->buf and this could confuse the
- * gadget driver.
- */
- if (dwc->ep0_bounced && dep->number <= 1) {
- dwc->ep0_bounced = false;
- unmap_after_complete = true;
- } else {
- usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
- }
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
trace_dwc3_gadget_giveback(req);
@@ -201,10 +189,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
- if (unmap_after_complete)
- usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
-
if (dep->number > 1)
pm_runtime_put(dwc->dev);
}
@@ -1060,6 +1044,22 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
false, 0, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
+ } else if (req->request.zero && req->request.length &&
+ (IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->zero = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, 0);
+
+ /* Now prepare one extra TRB to handle ZLP */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
+ false, 0, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
} else {
dwc3_prepare_one_trb(dep, req, false, 0);
}
@@ -1184,8 +1184,11 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
return;
}
- /* 4 micro frames in the future */
- uf = cur_uf + dep->interval * 4;
+ /*
+ * Schedule the first trb for one interval in the future or at
+ * least 4 microframes.
+ */
+ uf = cur_uf + max_t(u32, 4, dep->interval);
__dwc3_gadget_kick_transfer(dep, uf);
}
@@ -1258,45 +1261,30 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
}
+ return 0;
}
- return 0;
+
+ if ((dep->flags & DWC3_EP_BUSY) &&
+ !(dep->flags & DWC3_EP_MISSED_ISOC)) {
+ WARN_ON_ONCE(!dep->resource_index);
+ ret = __dwc3_gadget_kick_transfer(dep,
+ dep->resource_index);
+ }
+
+ goto out;
}
if (!dwc3_calc_trbs_left(dep))
return 0;
ret = __dwc3_gadget_kick_transfer(dep, 0);
+out:
if (ret == -EBUSY)
ret = 0;
return ret;
}
-static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
- struct usb_request *request)
-{
- dwc3_gadget_ep_free_request(ep, request);
-}
-
-static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
-{
- struct dwc3_request *req;
- struct usb_request *request;
- struct usb_ep *ep = &dep->endpoint;
-
- request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
- if (!request)
- return -ENOMEM;
-
- request->length = 0;
- request->buf = dwc->zlp_buf;
- request->complete = __dwc3_gadget_ep_zlp_complete;
-
- req = to_dwc3_request(request);
-
- return __dwc3_gadget_ep_queue(dep, req);
-}
-
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
@@ -1310,17 +1298,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req);
-
- /*
- * Okay, here's the thing, if gadget driver has requested for a ZLP by
- * setting request->zero, instead of doing magic, we will just queue an
- * extra usb_request ourselves so that it gets handled the same way as
- * any other request.
- */
- if (ret == 0 && request->zero && request->length &&
- (request->length % ep->maxpacket == 0))
- ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
-
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1400,7 +1377,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
dwc3_ep_inc_deq(dep);
}
- if (r->unaligned) {
+ if (r->unaligned || r->zero) {
trb = r->trb + r->num_pending_sgs + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
@@ -1411,7 +1388,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
- if (r->unaligned) {
+ if (r->unaligned || r->zero) {
trb = r->trb + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
@@ -2006,14 +1983,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
/* -------------------------------------------------------------------------- */
-static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
- u8 num, u32 direction)
+static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
{
struct dwc3_ep *dep;
- u8 i;
+ u8 epnum;
+
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
- for (i = 0; i < num; i++) {
- u8 epnum = (i << 1) | (direction ? 1 : 0);
+ for (epnum = 0; epnum < num; epnum++) {
+ bool direction = epnum & 1;
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (!dep)
@@ -2021,12 +1999,12 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->dwc = dwc;
dep->number = epnum;
- dep->direction = !!direction;
+ dep->direction = direction;
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
- (epnum & 1) ? "in" : "out");
+ direction ? "in" : "out");
dep->endpoint.name = dep->name;
@@ -2053,7 +2031,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(i));
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(epnum >> 1));
size = DWC3_GTXFIFOSIZ_TXFDEF(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
@@ -2103,7 +2081,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.caps.type_int = true;
}
- dep->endpoint.caps.dir_in = !!direction;
+ dep->endpoint.caps.dir_in = direction;
dep->endpoint.caps.dir_out = !direction;
INIT_LIST_HEAD(&dep->pending_list);
@@ -2113,27 +2091,6 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
return 0;
}
-static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
-{
- int ret;
-
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
-
- ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
- if (ret < 0) {
- dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
- return ret;
- }
-
- ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
- if (ret < 0) {
- dev_err(dwc->dev, "failed to initialize IN endpoints\n");
- return ret;
- }
-
- return 0;
-}
-
static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@@ -2197,7 +2154,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
- if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+ if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2291,11 +2248,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}
- if (req->unaligned) {
+ if (req->unaligned || req->zero) {
trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, false);
req->unaligned = false;
+ req->zero = false;
}
req->request.actual = length - req->remaining;
@@ -3078,6 +3036,15 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
return IRQ_HANDLED;
}
+ /*
+ * With PCIe legacy interrupt, test shows that top-half irq handler can
+ * be called again after HW interrupt deassertion. Check if bottom-half
+ * irq event handler completes before caching new event to prevent
+ * losing events.
+ */
+ if (evt->flags & DWC3_EVENT_PENDING)
+ return IRQ_HANDLED;
+
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
@@ -3161,49 +3128,26 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->irq_gadget = irq;
- dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
- &dwc->ctrl_req_addr, GFP_KERNEL);
- if (!dwc->ctrl_req) {
- dev_err(dwc->dev, "failed to allocate ctrl request\n");
- ret = -ENOMEM;
- goto err0;
- }
-
dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
sizeof(*dwc->ep0_trb) * 2,
&dwc->ep0_trb_addr, GFP_KERNEL);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
- goto err1;
+ goto err0;
}
- dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
+ dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL);
if (!dwc->setup_buf) {
ret = -ENOMEM;
- goto err2;
- }
-
- dwc->ep0_bounce = dma_alloc_coherent(dwc->sysdev,
- DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
- GFP_KERNEL);
- if (!dwc->ep0_bounce) {
- dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
- ret = -ENOMEM;
- goto err3;
- }
-
- dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
- if (!dwc->zlp_buf) {
- ret = -ENOMEM;
- goto err4;
+ goto err1;
}
dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
&dwc->bounce_addr, GFP_KERNEL);
if (!dwc->bounce) {
ret = -ENOMEM;
- goto err5;
+ goto err2;
}
init_completion(&dwc->ep0_in_setup);
@@ -3241,39 +3185,31 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* sure we're starting from a well known location.
*/
- ret = dwc3_gadget_init_endpoints(dwc);
+ ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
if (ret)
- goto err6;
+ goto err3;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err6;
+ goto err4;
}
return 0;
-err6:
- dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
- dwc->bounce_addr);
-
-err5:
- kfree(dwc->zlp_buf);
err4:
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
- dwc->ep0_bounce, dwc->ep0_bounce_addr);
err3:
- kfree(dwc->setup_buf);
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
err2:
- dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
- dwc->ep0_trb, dwc->ep0_trb_addr);
+ kfree(dwc->setup_buf);
err1:
- dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
- dwc->ctrl_req, dwc->ctrl_req_addr);
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
+ dwc->ep0_trb, dwc->ep0_trb_addr);
err0:
return ret;
@@ -3284,22 +3220,12 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
usb_del_gadget_udc(&dwc->gadget);
-
dwc3_gadget_free_endpoints(dwc);
-
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
- dwc->bounce_addr);
- dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
- dwc->ep0_bounce, dwc->ep0_bounce_addr);
-
+ dwc->bounce_addr);
kfree(dwc->setup_buf);
- kfree(dwc->zlp_buf);
-
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
- dwc->ep0_trb, dwc->ep0_trb_addr);
-
- dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
- dwc->ctrl_req, dwc->ctrl_req_addr);
+ dwc->ep0_trb, dwc->ep0_trb_addr);
}
int dwc3_gadget_suspend(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 265e223ab645..e4602d0e515b 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -29,16 +29,16 @@ struct dwc3;
/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
-#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
-#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
-#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
-#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
-#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
+#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8)
+#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
+#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
+#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
+#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(12)
#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
-#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
+#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
-#define DWC3_DEPCFG_BULK_BASED (1 << 30)
-#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
+#define DWC3_DEPCFG_BULK_BASED BIT(30)
+#define DWC3_DEPCFG_FIFO_BASED BIT(31)
/* DEPCFG parameter 0 */
#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
@@ -47,10 +47,10 @@ struct dwc3;
#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
-#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
+#define DWC3_DEPCFG_IGN_SEQ_NUM BIT(31)
/* These apply for core versions 1.94a and later */
#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
-#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE BIT(30)
#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
/* DEPXFERCFG parameter 0 */
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 16/64] usb: dwc3: import from kernel v4.13
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (14 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 15/64] usb: dwc3: import from kernel v4.12 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 17/64] usb: dwc3: import from kernel v4.14 Jens Wiklander
` (51 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.12 to v4.13.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.12..v4.13
Commits imported:
40d829fb2ec6 usb: dwc3: gadget: Correct ISOC DATA PIDs for short packets
4a71fcb8ac5f usb: dwc3: gadget: only unmap requests from DMA if mapped
ee249b455494 usb: dwc3: omap: remove IRQ_NOAUTOEN used with shared irq
541768b08a40 usb: dwc3: core: Call dwc3_core_get_phy() before initializing phys
408c9861c697 Merge tag 'pm-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
362f6729cbb1 Merge tag 'usb-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
de3ef1eb1cd0 PM / core: Drop run_wake flag from struct dev_pm_info
24040a58379e Merge tag 'usb-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
ece7af5f787e usb: dwc3: exynos: Handle return value of clk_prepare_enable
46b780d46bcc usb: dwc3: gadget: increase readability of dwc3_gadget_init_endpoints()
958d1a4c40dd usb: dwc3: core: program PHY for proper DRD modes
f54edb539c11 usb: dwc3: core: initialize ULPI before trying to get the PHY
e0082698b689 usb: dwc3: ulpi: conditionally resume ULPI PHY
9b0a1f95c4b4 usb: dwc3: ep0: make sure wValue is 0 on GetStatus()
7d8d0639565f usb: dwc3: gadget: implement ->udc_set_speed()
94116f8126de ACPI: Switch to use generic guid_t in acpi_evaluate_dsm()
af32423a2d86 usb: dwc3: trace: decode ctrl request
3587f36a125f usb: dwc3: debug: remove static char buffer from dwc3_decode_event()
e42f09b85f20 usb: dwc3: trace: rely on __string() and __assign_str()
dfc5e80578f2 usb: dwc3: gadget: slight cleanup to dwc3_process_event_entry()
436841d53dd7 usb: dwc3: debugfs: slightly improve output of trb_ring
bfad65ee9be2 usb: dwc3: update documentation
04fb365c453e usb: dwc3: replace %p with %pK
6acf116c9558 Merge 4.12-rc2 into usb-next
65db7a0c9816 usb: dwc3: add disable u2mac linestate check quirk
cdb55b39fab8 usb: dwc3: gadget: lazily map requests for DMA
202adafe5a6e usb: dwc3: gadget: don't WARN about lack of TRBs
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 57 +++++--
drivers/usb/dwc3/core.h | 48 +++---
drivers/usb/dwc3/debug.h | 247 ++++++++++++++++++++++++++-
drivers/usb/dwc3/ep0.c | 8 +-
drivers/usb/dwc3/gadget.c | 340 +++++++++++++++++++++++---------------
drivers/usb/dwc3/gadget.h | 22 ++-
6 files changed, 549 insertions(+), 173 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 455d89a1cd6d..03474d3575ab 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -151,11 +151,24 @@ static void __dwc3_set_mode(struct work_struct *work)
switch (dwc->desired_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
ret = dwc3_host_init(dwc);
- if (ret)
+ if (ret) {
dev_err(dwc->dev, "failed to initialize host\n");
+ } else {
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, true);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+
+ }
break;
case DWC3_GCTL_PRTCAP_DEVICE:
dwc3_event_buffers_setup(dwc);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, false);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+
ret = dwc3_gadget_init(dwc);
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
@@ -721,6 +734,8 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
+static int dwc3_core_get_phy(struct dwc3 *dwc);
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -751,6 +766,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->maximum_speed = USB_SPEED_HIGH;
}
+ ret = dwc3_core_get_phy(dwc);
+ if (ret)
+ goto err0;
+
ret = dwc3_core_soft_reset(dwc);
if (ret)
goto err0;
@@ -796,13 +815,19 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
- /*
- * Enable hardware control of sending remote wakeup in HS when
- * the device is in the L1 state.
- */
- if (dwc->revision >= DWC3_REVISION_290A) {
+ if (dwc->revision >= DWC3_REVISION_250A) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
- reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+
+ /*
+ * Enable hardware control of sending remote wakeup
+ * in HS when the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A)
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+
+ if (dwc->dis_tx_ipgap_linecheck_quirk)
+ reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@@ -903,6 +928,12 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, false);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -912,6 +943,12 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
break;
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, true);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -1023,6 +1060,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,dis-u2-freeclk-exists-quirk");
dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
"snps,dis-del-phy-power-chg-quirk");
+ dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
+ "snps,dis-tx-ipgap-linecheck-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
@@ -1148,10 +1187,6 @@ static int dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
- ret = dwc3_core_get_phy(dwc);
- if (ret)
- goto err0;
-
spin_lock_init(&dwc->lock);
pm_runtime_set_active(dev);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 981c77f5628e..ea910acb4bb0 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1,4 +1,4 @@
-/**
+/*
* core.h - DesignWare USB3 DRD Core Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
@@ -204,6 +204,7 @@
#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
/* Global User Control 1 Register */
+#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
/* Global USB2 PHY Configuration Register */
@@ -522,7 +523,6 @@ struct dwc3_event_buffer {
* @trb_pool_dma: dma address of @trb_pool
* @trb_enqueue: enqueue 'pointer' into TRB array
* @trb_dequeue: dequeue 'pointer' into TRB array
- * @desc: usb_endpoint_descriptor pointer
* @dwc: pointer to DWC controller
* @saved_state: ep state saved during hibernation
* @flags: endpoint flags (wedged, stalled, ...)
@@ -664,7 +664,7 @@ enum dwc3_link_state {
* @bpl: DW0-3
* @bph: DW4-7
* @size: DW8-B
- * @trl: DWC-F
+ * @ctrl: DWC-F
*/
struct dwc3_trb {
u32 bpl;
@@ -674,16 +674,16 @@ struct dwc3_trb {
} __packed;
/**
- * dwc3_hwparams - copy of HWPARAMS registers
- * @hwparams0 - GHWPARAMS0
- * @hwparams1 - GHWPARAMS1
- * @hwparams2 - GHWPARAMS2
- * @hwparams3 - GHWPARAMS3
- * @hwparams4 - GHWPARAMS4
- * @hwparams5 - GHWPARAMS5
- * @hwparams6 - GHWPARAMS6
- * @hwparams7 - GHWPARAMS7
- * @hwparams8 - GHWPARAMS8
+ * struct dwc3_hwparams - copy of HWPARAMS registers
+ * @hwparams0: GHWPARAMS0
+ * @hwparams1: GHWPARAMS1
+ * @hwparams2: GHWPARAMS2
+ * @hwparams3: GHWPARAMS3
+ * @hwparams4: GHWPARAMS4
+ * @hwparams5: GHWPARAMS5
+ * @hwparams6: GHWPARAMS6
+ * @hwparams7: GHWPARAMS7
+ * @hwparams8: GHWPARAMS8
*/
struct dwc3_hwparams {
u32 hwparams0;
@@ -730,7 +730,8 @@ struct dwc3_hwparams {
* @unaligned: true for OUT endpoints with length not divisible by maxp
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
- * @queued: true when request has been queued to HW
+ * @started: request is started
+ * @zero: wants a ZLP
*/
struct dwc3_request {
struct usb_request request;
@@ -761,17 +762,23 @@ struct dwc3_scratchpad_array {
/**
* struct dwc3 - representation of our controller
- * @drd_work - workqueue used for role swapping
+ * @drd_work: workqueue used for role swapping
* @ep0_trb: trb which is used for the ctrl_req
+ * @bounce: address of bounce buffer
+ * @scratchbuf: address of scratch buffer
* @setup_buf: used while precessing STD USB requests
- * @ep0_trb: dma address of ep0_trb
+ * @ep0_trb_addr: dma address of @ep0_trb
+ * @bounce_addr: dma address of @bounce
* @ep0_usb_req: dummy req used while handling STD USB requests
* @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
* @dev: pointer to our struct device
+ * @sysdev: pointer to the DMA-capable device
* @xhci: pointer to our xHCI child
- * @event_buffer_list: a list of event buffers
+ * @xhci_resources: struct resources for our @xhci child
+ * @ev_buf: struct dwc3_event_buffer pointer
+ * @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
* @regs: base address for our registers
@@ -795,8 +802,6 @@ struct dwc3_scratchpad_array {
* @usb2_generic_phy: pointer to USB2 PHY
* @usb3_generic_phy: pointer to USB3 PHY
* @ulpi: pointer to ulpi interface
- * @dcfg: saved contents of DCFG register
- * @gctl: saved contents of GCTL register
* @isoch_delay: wValue from Set Isochronous Delay request;
* @u2sel: parameter from Set SEL request.
* @u2pel: parameter from Set SEL request.
@@ -830,7 +835,6 @@ struct dwc3_scratchpad_array {
* @pending_events: true when we have pending IRQs to be handled
* @pullups_connected: true when Run/Stop bit is set
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
- * @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
* @usb3_lpm_capable: set if hadrware supports Link Power Management
* @disable_scramble_quirk: set if we enable the disable scramble quirk
@@ -845,11 +849,14 @@ struct dwc3_scratchpad_array {
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
* disabling the suspend signal to the PHY.
+ * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3
* @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
* in GUSB2PHYCFG, specify that USB2 PHY doesn't
* provide a free-running PHY clock.
* @dis_del_phy_power_chg_quirk: set if we disable delay phy power
* change quirk.
+ * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
+ * check during HS transmit.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -1004,6 +1011,7 @@ struct dwc3 {
unsigned dis_rxdet_inp3_quirk:1;
unsigned dis_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1;
+ unsigned dis_tx_ipgap_linecheck_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index cb2d8d3f7f3d..5e9c070ec874 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -173,9 +173,8 @@ static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
* @event: the event code
*/
static inline const char *
-dwc3_gadget_event_string(const struct dwc3_event_devt *event)
+dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
{
- static char str[256];
enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
switch (event->type) {
@@ -223,15 +222,249 @@ dwc3_gadget_event_string(const struct dwc3_event_devt *event)
return str;
}
+static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
+{
+ switch (t & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
+ i, l);
+ break;
+ case USB_RECIP_ENDPOINT:
+ sprintf(str, "Get Endpoint Status(ep%d%s)",
+ i & ~USB_DIR_IN,
+ i & USB_DIR_IN ? "in" : "out");
+ break;
+ }
+}
+
+static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
+ __u16 i, char *str)
+{
+ switch (t & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ sprintf(str, "%s Device Feature(%s%s)",
+ b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ ({char *s;
+ switch (v) {
+ case USB_DEVICE_SELF_POWERED:
+ s = "Self Powered";
+ break;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ s = "Remote Wakeup";
+ break;
+ case USB_DEVICE_TEST_MODE:
+ s = "Test Mode";
+ break;
+ default:
+ s = "UNKNOWN";
+ } s; }),
+ v == USB_DEVICE_TEST_MODE ?
+ ({ char *s;
+ switch (i) {
+ case TEST_J:
+ s = ": TEST_J";
+ break;
+ case TEST_K:
+ s = ": TEST_K";
+ break;
+ case TEST_SE0_NAK:
+ s = ": TEST_SE0_NAK";
+ break;
+ case TEST_PACKET:
+ s = ": TEST_PACKET";
+ break;
+ case TEST_FORCE_EN:
+ s = ": TEST_FORCE_EN";
+ break;
+ default:
+ s = ": UNKNOWN";
+ } s; }) : "");
+ break;
+ case USB_RECIP_INTERFACE:
+ sprintf(str, "%s Interface Feature(%s)",
+ b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ v == USB_INTRF_FUNC_SUSPEND ?
+ "Function Suspend" : "UNKNOWN");
+ break;
+ case USB_RECIP_ENDPOINT:
+ sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
+ b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
+ i & ~USB_DIR_IN,
+ i & USB_DIR_IN ? "in" : "out");
+ break;
+ }
+}
+
+static inline void dwc3_decode_set_address(__u16 v, char *str)
+{
+ sprintf(str, "Set Address(Addr = %02x)", v);
+}
+
+static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
+ __u16 i, __u16 l, char *str)
+{
+ sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
+ b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
+ ({ char *s;
+ switch (v >> 8) {
+ case USB_DT_DEVICE:
+ s = "Device";
+ break;
+ case USB_DT_CONFIG:
+ s = "Configuration";
+ break;
+ case USB_DT_STRING:
+ s = "String";
+ break;
+ case USB_DT_INTERFACE:
+ s = "Interface";
+ break;
+ case USB_DT_ENDPOINT:
+ s = "Endpoint";
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ s = "Device Qualifier";
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ s = "Other Speed Config";
+ break;
+ case USB_DT_INTERFACE_POWER:
+ s = "Interface Power";
+ break;
+ case USB_DT_OTG:
+ s = "OTG";
+ break;
+ case USB_DT_DEBUG:
+ s = "Debug";
+ break;
+ case USB_DT_INTERFACE_ASSOCIATION:
+ s = "Interface Association";
+ break;
+ case USB_DT_BOS:
+ s = "BOS";
+ break;
+ case USB_DT_DEVICE_CAPABILITY:
+ s = "Device Capability";
+ break;
+ case USB_DT_PIPE_USAGE:
+ s = "Pipe Usage";
+ break;
+ case USB_DT_SS_ENDPOINT_COMP:
+ s = "SS Endpoint Companion";
+ break;
+ case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+ s = "SSP Isochronous Endpoint Companion";
+ break;
+ default:
+ s = "UNKNOWN";
+ break;
+ } s; }), v & 0xff, l);
+}
+
+
+static inline void dwc3_decode_get_configuration(__u16 l, char *str)
+{
+ sprintf(str, "Get Configuration(Length = %d)", l);
+}
+
+static inline void dwc3_decode_set_configuration(__u8 v, char *str)
+{
+ sprintf(str, "Set Configuration(Config = %d)", v);
+}
+
+static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
+{
+ sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
+}
+
+static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
+{
+ sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
+}
+
+static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
+{
+ sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
+}
+
+static inline void dwc3_decode_set_sel(__u16 l, char *str)
+{
+ sprintf(str, "Set SEL(Length = %d)", l);
+}
+
+static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
+{
+ sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
+}
+
+/**
+ * dwc3_decode_ctrl - returns a string represetion of ctrl request
+ */
+static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
+{
+ switch (bRequest) {
+ case USB_REQ_GET_STATUS:
+ dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
+ wIndex, str);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ dwc3_decode_set_address(wValue, str);
+ break;
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_SET_DESCRIPTOR:
+ dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
+ wIndex, wLength, str);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ dwc3_decode_get_configuration(wLength, str);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ dwc3_decode_set_configuration(wValue, str);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ dwc3_decode_get_intf(wIndex, wLength, str);
+ break;
+ case USB_REQ_SET_INTERFACE:
+ dwc3_decode_set_intf(wValue, wIndex, str);
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ dwc3_decode_synch_frame(wIndex, wLength, str);
+ break;
+ case USB_REQ_SET_SEL:
+ dwc3_decode_set_sel(wLength, str);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ dwc3_decode_set_isoch_delay(wValue, str);
+ break;
+ default:
+ sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bRequestType, bRequest,
+ cpu_to_le16(wValue) & 0xff,
+ cpu_to_le16(wValue) >> 8,
+ cpu_to_le16(wIndex) & 0xff,
+ cpu_to_le16(wIndex) >> 8,
+ cpu_to_le16(wLength) & 0xff,
+ cpu_to_le16(wLength) >> 8);
+ }
+
+ return str;
+}
+
/**
* dwc3_ep_event_string - returns event name
* @event: then event code
*/
static inline const char *
-dwc3_ep_event_string(const struct dwc3_event_depevt *event, u32 ep0state)
+dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
+ u32 ep0state)
{
u8 epnum = event->endpoint_number;
- static char str[256];
size_t len;
int status;
int ret;
@@ -332,14 +565,14 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
}
}
-static inline const char *dwc3_decode_event(u32 event, u32 ep0state)
+static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state)
{
const union dwc3_event evt = (union dwc3_event) event;
if (evt.type.is_devspec)
- return dwc3_gadget_event_string(&evt.devt);
+ return dwc3_gadget_event_string(str, &evt.devt);
else
- return dwc3_ep_event_string(&evt.depevt, ep0state);
+ return dwc3_ep_event_string(str, &evt.depevt, ep0state);
}
static inline const char *dwc3_ep_cmd_status_string(int status)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index a78c78e7a8c3..827e376bfa97 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1,4 +1,4 @@
-/**
+/*
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
@@ -319,10 +319,16 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
{
struct dwc3_ep *dep;
u32 recip;
+ u32 value;
u32 reg;
u16 usb_status = 0;
__le16 *response_pkt;
+ /* We don't support PTM_STATUS */
+ value = le16_to_cpu(ctrl->wValue);
+ if (value != 0)
+ return -EINVAL;
+
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index aea9a5b948b4..f064f1549333 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1,4 +1,4 @@
-/**
+/*
* gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
@@ -36,13 +36,12 @@
#include "io.h"
/**
- * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
+ * dwc3_gadget_set_test_mode - enables usb2 test modes
* @dwc: pointer to our context structure
* @mode: the mode to set (J, K SE0 NAK, Force Enable)
*
- * Caller should take care of locking. This function will
- * return 0 on success or -EINVAL if wrong Test Selector
- * is passed
+ * Caller should take care of locking. This function will return 0 on
+ * success or -EINVAL if wrong Test Selector is passed.
*/
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{
@@ -69,7 +68,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
}
/**
- * dwc3_gadget_get_link_state - Gets current state of USB Link
+ * dwc3_gadget_get_link_state - gets current state of usb link
* @dwc: pointer to our context structure
*
* Caller should take care of locking. This function will
@@ -85,7 +84,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
}
/**
- * dwc3_gadget_set_link_state - Sets USB Link to a particular State
+ * dwc3_gadget_set_link_state - sets usb link to a particular state
* @dwc: pointer to our context structure
* @state: the state to put link into
*
@@ -143,8 +142,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
}
/**
- * dwc3_ep_inc_trb() - Increment a TRB index.
- * @index - Pointer to the TRB index to increment.
+ * dwc3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
*
* The index should never point to the link TRB. After incrementing,
* if it is point to the link TRB, wrap around to the beginning. The
@@ -157,16 +156,34 @@ static void dwc3_ep_inc_trb(u8 *index)
*index = 0;
}
+/**
+ * dwc3_ep_inc_enq - increment endpoint's enqueue pointer
+ * @dep: The endpoint whose enqueue pointer we're incrementing
+ */
static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
{
dwc3_ep_inc_trb(&dep->trb_enqueue);
}
+/**
+ * dwc3_ep_inc_deq - increment endpoint's dequeue pointer
+ * @dep: The endpoint whose enqueue pointer we're incrementing
+ */
static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
{
dwc3_ep_inc_trb(&dep->trb_dequeue);
}
+/**
+ * dwc3_gadget_giveback - call struct usb_request's ->complete callback
+ * @dep: The endpoint to whom the request belongs to
+ * @req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
@@ -174,14 +191,16 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
req->started = false;
list_del(&req->list);
- req->trb = NULL;
req->remaining = 0;
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
+ if (req->trb)
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
+
+ req->trb = NULL;
trace_dwc3_gadget_giveback(req);
@@ -193,6 +212,15 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
pm_runtime_put(dwc->dev);
}
+/**
+ * dwc3_send_gadget_generic_command - issue a generic command for the controller
+ * @dwc: pointer to the controller context
+ * @cmd: the command to be issued
+ * @param: command parameter
+ *
+ * Caller should take care of locking. Issue @cmd with a given @param to @dwc
+ * and wait for its completion.
+ */
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
{
u32 timeout = 500;
@@ -225,6 +253,15 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
+/**
+ * dwc3_send_gadget_ep_cmd - issue an endpoint command
+ * @dep: the endpoint to which the command is going to be issued
+ * @cmd: the command to be issued
+ * @params: parameters to the command
+ *
+ * Caller should handle locking. This function will issue @cmd with given
+ * @params to @dep and wait for its completion.
+ */
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
struct dwc3_gadget_ep_cmd_params *params)
{
@@ -422,36 +459,38 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
/**
- * dwc3_gadget_start_config - Configure EP resources
+ * dwc3_gadget_start_config - configure ep resources
* @dwc: pointer to our controller context structure
* @dep: endpoint that is being enabled
*
- * The assignment of transfer resources cannot perfectly follow the
- * data book due to the fact that the controller driver does not have
- * all knowledge of the configuration in advance. It is given this
- * information piecemeal by the composite gadget framework after every
- * SET_CONFIGURATION and SET_INTERFACE. Trying to follow the databook
- * programming model in this scenario can cause errors. For two
- * reasons:
+ * Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's
+ * completion, it will set Transfer Resource for all available endpoints.
+ *
+ * The assignment of transfer resources cannot perfectly follow the data book
+ * due to the fact that the controller driver does not have all knowledge of the
+ * configuration in advance. It is given this information piecemeal by the
+ * composite gadget framework after every SET_CONFIGURATION and
+ * SET_INTERFACE. Trying to follow the databook programming model in this
+ * scenario can cause errors. For two reasons:
*
- * 1) The databook says to do DEPSTARTCFG for every SET_CONFIGURATION
- * and SET_INTERFACE (8.1.5). This is incorrect in the scenario of
- * multiple interfaces.
+ * 1) The databook says to do %DWC3_DEPCMD_DEPSTARTCFG for every
+ * %USB_REQ_SET_CONFIGURATION and %USB_REQ_SET_INTERFACE (8.1.5). This is
+ * incorrect in the scenario of multiple interfaces.
*
- * 2) The databook does not mention doing more DEPXFERCFG for new
+ * 2) The databook does not mention doing more %DWC3_DEPCMD_DEPXFERCFG for new
* endpoint on alt setting (8.1.6).
*
* The following simplified method is used instead:
*
- * All hardware endpoints can be assigned a transfer resource and this
- * setting will stay persistent until either a core reset or
- * hibernation. So whenever we do a DEPSTARTCFG(0) we can go ahead and
- * do DEPXFERCFG for every hardware endpoint as well. We are
+ * All hardware endpoints can be assigned a transfer resource and this setting
+ * will stay persistent until either a core reset or hibernation. So whenever we
+ * do a %DWC3_DEPCMD_DEPSTARTCFG(0) we can go ahead and do
+ * %DWC3_DEPCMD_DEPXFERCFG for every hardware endpoint as well. We are
* guaranteed that there are as many transfer resources as endpoints.
*
- * This function is called for each endpoint when it is being enabled
- * but is triggered only when called for EP0-out, which always happens
- * first, and which should only happen in one of the above conditions.
+ * This function is called for each endpoint when it is being enabled but is
+ * triggered only when called for EP0-out, which always happens first, and which
+ * should only happen in one of the above conditions.
*/
static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
{
@@ -569,11 +608,13 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
}
/**
- * __dwc3_gadget_ep_enable - Initializes a HW endpoint
+ * __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
- * @desc: USB Endpoint Descriptor
+ * @modify: if true, modify existing endpoint configuration
+ * @restore: if true, restore endpoint configuration from scratch buffer
*
- * Caller should take care of locking
+ * Caller should take care of locking. Execute all necessary commands to
+ * initialize a HW endpoint so it can be used by a gadget driver.
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
bool modify, bool restore)
@@ -685,11 +726,13 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
}
/**
- * __dwc3_gadget_ep_disable - Disables a HW endpoint
+ * __dwc3_gadget_ep_disable - disables a hw endpoint
* @dep: the endpoint to disable
*
- * This function also removes requests which are currently processed ny the
- * hardware and those which are not yet scheduled.
+ * This function undoes what __dwc3_gadget_ep_enable did and also removes
+ * requests which are currently being processed by the hardware and those which
+ * are not yet scheduled.
+ *
* Caller should take care of locking.
*/
static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
@@ -853,9 +896,40 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
if (!node) {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+ /*
+ * USB Specification 2.0 Section 5.9.2 states that: "If
+ * there is only a single transaction in the microframe,
+ * only a DATA0 data packet PID is used. If there are
+ * two transactions per microframe, DATA1 is used for
+ * the first transaction data packet and DATA0 is used
+ * for the second transaction data packet. If there are
+ * three transactions per microframe, DATA2 is used for
+ * the first transaction data packet, DATA1 is used for
+ * the second, and DATA0 is used for the third."
+ *
+ * IOW, we should satisfy the following cases:
+ *
+ * 1) length <= maxpacket
+ * - DATA0
+ *
+ * 2) maxpacket < length <= (2 * maxpacket)
+ * - DATA1, DATA0
+ *
+ * 3) (2 * maxpacket) < length <= (3 * maxpacket)
+ * - DATA2, DATA1, DATA0
+ */
if (speed == USB_SPEED_HIGH) {
struct usb_ep *ep = &dep->endpoint;
- trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
+ unsigned int mult = ep->mult - 1;
+ unsigned int maxp = usb_endpoint_maxp(ep->desc);
+
+ if (length <= (2 * maxp))
+ mult--;
+
+ if (length <= maxp)
+ mult--;
+
+ trb->size |= DWC3_TRB_SIZE_PCM1(mult);
}
} else {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
@@ -932,7 +1006,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
}
/**
- * dwc3_ep_prev_trb() - Returns the previous TRB in the ring
+ * dwc3_ep_prev_trb - returns the previous TRB in the ring
* @dep: The endpoint with the TRB ring
* @index: The index of the current TRB in the ring
*
@@ -953,7 +1027,6 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{
struct dwc3_trb *tmp;
- struct dwc3 *dwc = dep->dwc;
u8 trbs_left;
/*
@@ -965,8 +1038,7 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (dev_WARN_ONCE(dwc->dev, tmp->ctrl & DWC3_TRB_CTRL_HWO,
- "%s No TRBS left\n", dep->name))
+ if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
return 0;
return DWC3_TRB_NUM - 1;
@@ -1101,6 +1173,17 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
}
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
+ struct dwc3 *dwc = dep->dwc;
+ int ret;
+
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
+ dep->direction);
+ if (ret)
+ return;
+
+ req->sg = req->request.sg;
+ req->num_pending_sgs = req->request.num_mapped_sgs;
+
if (req->num_pending_sgs > 0)
dwc3_prepare_one_trb_sg(dep, req);
else
@@ -1207,7 +1290,7 @@ static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
- int ret;
+ int ret = 0;
if (!dep->endpoint.desc) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
@@ -1215,12 +1298,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return -ESHUTDOWN;
}
- if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
- &req->request, req->dep->name)) {
- dev_err(dwc->dev, "%s: request %p belongs to '%s'\n",
- dep->name, &req->request, req->dep->name);
+ if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
+ &req->request, req->dep->name))
return -EINVAL;
- }
pm_runtime_get(dwc->dev);
@@ -1231,14 +1311,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req);
- ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
- dep->direction);
- if (ret)
- return ret;
-
- req->sg = req->request.sg;
- req->num_pending_sgs = req->request.num_mapped_sgs;
-
list_add_tail(&req->list, &dep->pending_list);
/*
@@ -1396,7 +1468,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
goto out1;
}
- dev_err(dwc->dev, "request %p was not queued to %s\n",
+ dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name);
ret = -EINVAL;
goto out0;
@@ -1741,8 +1813,8 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
/**
- * dwc3_gadget_setup_nump - Calculate and initialize NUMP field of DCFG
- * dwc: pointer to our context structure
+ * dwc3_gadget_setup_nump - calculate and initialize NUMP field of %DWC3_DCFG
+ * @dwc: pointer to our context structure
*
* The following looks like complex but it's actually very simple. In order to
* calculate the number of packets we can burst at once on OUT transfers, we're
@@ -1798,49 +1870,6 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
}
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
- reg &= ~(DWC3_DCFG_SPEED_MASK);
-
- /**
- * WORKAROUND: DWC3 revision < 2.20a have an issue
- * which would cause metastability state on Run/Stop
- * bit if we try to force the IP to USB2-only mode.
- *
- * Because of that, we cannot configure the IP to any
- * speed other than the SuperSpeed
- *
- * Refers to:
- *
- * STAR#9000525659: Clock Domain Crossing on DCTL in
- * USB 2.0 Mode
- */
- if (dwc->revision < DWC3_REVISION_220A) {
- reg |= DWC3_DCFG_SUPERSPEED;
- } else {
- switch (dwc->maximum_speed) {
- case USB_SPEED_LOW:
- reg |= DWC3_DCFG_LOWSPEED;
- break;
- case USB_SPEED_FULL:
- reg |= DWC3_DCFG_FULLSPEED;
- break;
- case USB_SPEED_HIGH:
- reg |= DWC3_DCFG_HIGHSPEED;
- break;
- case USB_SPEED_SUPER_PLUS:
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- break;
- default:
- dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
- dwc->maximum_speed);
- /* fall through */
- case USB_SPEED_SUPER:
- reg |= DWC3_DCFG_SUPERSPEED;
- break;
- }
- }
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
-
/*
* We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP
* field instead of letting dwc3 itself calculate that automatically.
@@ -1972,6 +2001,63 @@ out:
return 0;
}
+static void dwc3_gadget_set_speed(struct usb_gadget *g,
+ enum usb_device_speed speed)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+ /*
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (dwc->revision < DWC3_REVISION_220A) {
+ reg |= DWC3_DCFG_SUPERSPEED;
+ } else {
+ switch (speed) {
+ case USB_SPEED_LOW:
+ reg |= DWC3_DCFG_LOWSPEED;
+ break;
+ case USB_SPEED_FULL:
+ reg |= DWC3_DCFG_FULLSPEED;
+ break;
+ case USB_SPEED_HIGH:
+ reg |= DWC3_DCFG_HIGHSPEED;
+ break;
+ case USB_SPEED_SUPER:
+ reg |= DWC3_DCFG_SUPERSPEED;
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ break;
+ default:
+ dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+
+ if (dwc->revision & DWC3_REVISION_IS_DWC31)
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED;
+ }
+ }
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
@@ -1979,19 +2065,21 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
+ .udc_set_speed = dwc3_gadget_set_speed,
};
/* -------------------------------------------------------------------------- */
-static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
+static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
{
struct dwc3_ep *dep;
u8 epnum;
INIT_LIST_HEAD(&dwc->gadget.ep_list);
- for (epnum = 0; epnum < num; epnum++) {
+ for (epnum = 0; epnum < total; epnum++) {
bool direction = epnum & 1;
+ u8 num = epnum >> 1;
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (!dep)
@@ -2003,7 +2091,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
- snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
+ snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
direction ? "in" : "out");
dep->endpoint.name = dep->name;
@@ -2015,39 +2103,39 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
spin_lock_init(&dep->lock);
- if (epnum == 0 || epnum == 1) {
+ if (num == 0) {
usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
- if (!epnum)
+ if (!direction)
dwc->gadget.ep0 = &dep->endpoint;
} else if (direction) {
int mdwidth;
+ int kbytes;
int size;
int ret;
- int num;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(epnum >> 1));
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num));
size = DWC3_GTXFIFOSIZ_TXFDEF(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
- num = size / 1024;
- if (num == 0)
- num = 1;
+ kbytes = size / 1024;
+ if (kbytes == 0)
+ kbytes = 1;
/*
- * FIFO sizes account an extra MDWIDTH * (num + 1) bytes for
+ * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
* internal overhead. We don't really know how these are used,
* but documentation say it exists.
*/
- size -= mdwidth * (num + 1);
- size /= num;
+ size -= mdwidth * (kbytes + 1);
+ size /= kbytes;
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
@@ -2073,7 +2161,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
return ret;
}
- if (epnum == 0 || epnum == 1) {
+ if (num == 0) {
dep->endpoint.caps.type_control = true;
} else {
dep->endpoint.caps.type_iso = true;
@@ -2871,7 +2959,7 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
{
unsigned int is_ss = evtinfo & BIT(4);
- /**
+ /*
* WORKAROUND: DWC3 revison 2.20a with hibernation support
* have a known issue which can cause USB CV TD.9.23 to fail
* randomly.
@@ -2943,20 +3031,12 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
{
trace_dwc3_event(event->raw, dwc);
- /* Endpoint IRQ, handle it and return early */
- if (event->type.is_devspec == 0) {
- /* depevt */
- return dwc3_endpoint_interrupt(dwc, &event->depevt);
- }
-
- switch (event->type.type) {
- case DWC3_EVENT_TYPE_DEV:
+ if (!event->type.is_devspec)
+ dwc3_endpoint_interrupt(dwc, &event->depevt);
+ else if (event->type.type == DWC3_EVENT_TYPE_DEV)
dwc3_gadget_interrupt(dwc, &event->devt);
- break;
- /* REVISIT what to do with Carkit and I2C events ? */
- default:
+ else
dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw);
- }
}
static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
@@ -3110,7 +3190,7 @@ out:
}
/**
- * dwc3_gadget_init - Initializes gadget related registers
+ * dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
*
* Returns 0 on success otherwise negative errno.
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index e4602d0e515b..4a3227543255 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -1,4 +1,4 @@
-/**
+/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
@@ -60,11 +60,25 @@ struct dwc3;
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
+/**
+ * next_request - gets the next request on the given list
+ * @list: the request list to operate on
+ *
+ * Caller should take care of locking. This function return %NULL or the first
+ * request available on @list.
+ */
static inline struct dwc3_request *next_request(struct list_head *list)
{
return list_first_entry_or_null(list, struct dwc3_request, list);
}
+/**
+ * dwc3_gadget_move_started_request - move @req to the started_list
+ * @req: the request to be moved
+ *
+ * Caller should take care of locking. This function will move @req from its
+ * current list to the endpoint's started_list.
+ */
static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
{
struct dwc3_ep *dep = req->dep;
@@ -87,10 +101,10 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
- * @dwc: DesignWare USB3 Pointer
- * @number: DWC endpoint number
+ * @dep: dwc3 endpoint
*
- * Caller should take care of locking
+ * Caller should take care of locking. Returns the transfer resource
+ * index for a given endpoint.
*/
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 17/64] usb: dwc3: import from kernel v4.14
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (15 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 16/64] usb: dwc3: import from kernel v4.13 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 18/64] usb: dwc3: import from kernel v4.15 Jens Wiklander
` (50 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.13 to v4.14.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.13..v4.14
Commits imported:
b24413180f56 License cleanup: add SPDX GPL-2.0 license identifier to files with no license
c3cdce45f8d3 usb: dwc3: of-simple: Add compatible for Spreadtrum SC9860 platform
551684708356 usb: dwc3: ep0: fix DMA starvation by assigning req->trb on ep0
34a003674825 Merge tag 'usb-for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
e7059ef82a22 usb: dwc3: of-simple: remove include of clk-provider.h
feea468014f0 Merge 4.13-rc5 into usb-next
95aa932c93d2 usb/dwc3:constify dev_pm_ops
c14af973ed86 usb: dwc3: keystone: Add PM_RUNTIME Support to DWC3 Keystone USB driver
0ae99ecba792 usb: dwc3: omap: fix error return code in dwc3_omap_probe()
8bcd9889721f usb: dwc3: pci: constify dev_pm_ops
141769851cb7 Merge 4.13-rc2 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/ep0.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 827e376bfa97..75e6cb044eb2 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -990,6 +990,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
DWC3_TRBCTL_CONTROL_DATA,
true);
+ req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
+
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
maxpacket - rem,
@@ -1015,6 +1017,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
DWC3_TRBCTL_CONTROL_DATA,
true);
+ req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
+
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
0, DWC3_TRBCTL_CONTROL_DATA,
@@ -1029,6 +1033,9 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
+
+ req->trb = &dwc->ep0_trb[dep->trb_enqueue];
+
ret = dwc3_ep0_start_trans(dep);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 18/64] usb: dwc3: import from kernel v4.15
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (16 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 17/64] usb: dwc3: import from kernel v4.14 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:27 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 19/64] usb: dwc3: import from kernel v4.16 Jens Wiklander
` (49 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.14 to v4.15.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.14..v4.15
Commits imported:
ec5bb87e4e2a usb: dwc3: gadget: Fix PCM1 for ISOC EP with ep->mult less than 3
a0d8c4cfdf31 usb: dwc3: of-simple: set dev_pm_ops
ded600ea9fb5 usb: dwc3: of-simple: fix missing clk_disable_unprepare
8722e095f5a4 usb: dwc3: gadget: Wait longer for controller to end command processing
894025f24bd0 Merge tag 'usb-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
b5ea47570b5e USB: dwc3: Remove redundant license text
5fd54ace4721 USB: add SPDX identifiers to all remaining files in drivers/usb/
644cbbc3ab4d usb: dwc3: core: Notify current USB mode to USB3 PHY as well
8eed00b237a2 usb: dwc3: pci: Runtime resume child device from wq
689bf72c6e0d usb: dwc3: Don't reinitialize core during host bus-suspend/resume
06c47e6286d5 usb: dwc3: of-simple: Add support to get resets for the device
d6d9c2a3eac2 usb: dwc3: of-simple: Re-order resource handling in remove
86763723eee8 usb: dwc3: ep0: Clean up unused variables
7fdca766499b usb: dwc3: gadget: simplify __dwc3_gadget_kick_transfer() prototype
502a37b98a7b usb: dwc3: gadget: cache frame number in struct dwc3_ep
64e010802997 usb: dwc3: gadget: simplify __dwc3_gadget_ep_queue()
ccb94ebf9e65 usb: dwc3: gadget: check for lack of TRBs a bit earlier
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 74 +++++++++++++++-----------------------
drivers/usb/dwc3/core.h | 12 ++-----
drivers/usb/dwc3/debug.h | 10 +-----
drivers/usb/dwc3/ep0.c | 30 +---------------
drivers/usb/dwc3/gadget.c | 76 ++++++++++++---------------------------
drivers/usb/dwc3/gadget.h | 10 +-----
drivers/usb/dwc3/io.h | 10 +-----
7 files changed, 59 insertions(+), 163 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 03474d3575ab..07832509584f 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/**
* core.c - DesignWare USB3 DRD Controller Core file
*
@@ -5,18 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/version.h>
@@ -156,9 +145,8 @@ static void __dwc3_set_mode(struct work_struct *work)
} else {
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- if (dwc->usb2_generic_phy)
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
-
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+ phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -166,8 +154,8 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- if (dwc->usb2_generic_phy)
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
@@ -927,12 +915,13 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
+ dwc->current_dr_role = DWC3_GCTL_PRTCAP_DEVICE;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- if (dwc->usb2_generic_phy)
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
@@ -942,12 +931,13 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_HOST:
+ dwc->current_dr_role = DWC3_GCTL_PRTCAP_HOST;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- if (dwc->usb2_generic_phy)
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+ phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+ phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
@@ -1293,21 +1283,19 @@ static int dwc3_suspend_common(struct dwc3 *dwc)
{
unsigned long flags;
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_DEVICE:
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_core_exit(dwc);
break;
- case USB_DR_MODE_HOST:
+ case DWC3_GCTL_PRTCAP_HOST:
default:
/* do nothing */
break;
}
- dwc3_core_exit(dwc);
-
return 0;
}
@@ -1316,18 +1304,17 @@ static int dwc3_resume_common(struct dwc3 *dwc)
unsigned long flags;
int ret;
- ret = dwc3_core_init(dwc);
- if (ret)
- return ret;
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
- /* FALLTHROUGH */
- case USB_DR_MODE_HOST:
+ break;
+ case DWC3_GCTL_PRTCAP_HOST:
default:
/* do nothing */
break;
@@ -1338,7 +1325,7 @@ static int dwc3_resume_common(struct dwc3 *dwc)
static int dwc3_runtime_checks(struct dwc3 *dwc)
{
- switch (dwc->dr_mode) {
+ switch (dwc->current_dr_role) {
case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
if (dwc->connected)
@@ -1381,19 +1368,17 @@ static int dwc3_runtime_resume(struct device *dev)
if (ret)
return ret;
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_DEVICE:
dwc3_gadget_process_pending_events(dwc);
break;
- case USB_DR_MODE_HOST:
+ case DWC3_GCTL_PRTCAP_HOST:
default:
/* do nothing */
break;
}
pm_runtime_mark_last_busy(dev);
- pm_runtime_put(dev);
return 0;
}
@@ -1402,13 +1387,12 @@ static int dwc3_runtime_idle(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_DEVICE:
if (dwc3_runtime_checks(dwc))
return -EBUSY;
break;
- case USB_DR_MODE_HOST:
+ case DWC3_GCTL_PRTCAP_HOST:
default:
/* do nothing */
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index ea910acb4bb0..4a4a4c98508c 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* core.h - DesignWare USB3 DRD Core Header
*
@@ -5,15 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef __DRIVERS_USB_DWC3_CORE_H
@@ -529,6 +521,7 @@ struct dwc3_event_buffer {
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
+ * @frame_number: set to the frame number we want this transfer to start (ISOC)
* @interval: the interval on which the ISOC transfer is started
* @allocated_requests: number of requests allocated
* @queued_requests: number of requests queued for transfer
@@ -581,6 +574,7 @@ struct dwc3_ep {
u8 resource_index;
u32 allocated_requests;
u32 queued_requests;
+ u32 frame_number;
u32 interval;
char name[20];
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 5e9c070ec874..368f8e59219a 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/**
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
@@ -5,15 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef __DWC3_DEBUG_H
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 75e6cb044eb2..fd3e7ad2eb0e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
@@ -5,15 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -487,14 +479,10 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
- enum usb_device_state state;
u32 wValue;
- u32 wIndex;
int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
- wIndex = le16_to_cpu(ctrl->wIndex);
- state = dwc->gadget.state;
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
@@ -517,14 +505,10 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
struct dwc3_ep *dep;
- enum usb_device_state state;
u32 wValue;
- u32 wIndex;
int ret;
wValue = le16_to_cpu(ctrl->wValue);
- wIndex = le16_to_cpu(ctrl->wIndex);
- state = dwc->gadget.state;
switch (wValue) {
case USB_ENDPOINT_HALT:
@@ -551,10 +535,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
{
u32 recip;
int ret;
- enum usb_device_state state;
recip = ctrl->bRequestType & USB_RECIP_MASK;
- state = dwc->gadget.state;
switch (recip) {
case USB_RECIP_DEVICE:
@@ -712,12 +694,10 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
struct dwc3_ep *dep;
enum usb_device_state state = dwc->gadget.state;
u16 wLength;
- u16 wValue;
if (state == USB_STATE_DEFAULT)
return -EINVAL;
- wValue = le16_to_cpu(ctrl->wValue);
wLength = le16_to_cpu(ctrl->wLength);
if (wLength != 6) {
@@ -842,9 +822,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
- unsigned maxp;
- unsigned remaining_ur_length;
- void *buf;
u32 transferred = 0;
u32 status;
u32 length;
@@ -871,11 +848,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
}
ur = &r->request;
- buf = ur->buf;
- remaining_ur_length = ur->length;
length = trb->size & DWC3_TRB_SIZE_MASK;
- maxp = ep0->endpoint.maxpacket;
transferred = ur->length - length;
ur->actual += transferred;
@@ -1001,7 +975,6 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
u32 maxpacket;
- u32 rem;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
@@ -1009,7 +982,6 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
maxpacket = dep->endpoint.maxpacket;
- rem = req->request.length % maxpacket;
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f064f1549333..639dd1b163a0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
*
@@ -5,15 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -267,7 +259,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
- u32 timeout = 500;
+ u32 timeout = 1000;
u32 reg;
int cmd_status = 0;
@@ -920,7 +912,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
*/
if (speed == USB_SPEED_HIGH) {
struct usb_ep *ep = &dep->endpoint;
- unsigned int mult = ep->mult - 1;
+ unsigned int mult = 2;
unsigned int maxp = usb_endpoint_maxp(ep->desc);
if (length <= (2 * maxp))
@@ -1151,9 +1143,6 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
- if (!dwc3_calc_trbs_left(dep))
- return;
-
/*
* We can get in a situation where there's a request in the started list
* but there weren't enough TRBs to fully kick it in the first time
@@ -1194,7 +1183,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
}
}
-static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
+static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_request *req;
@@ -1202,6 +1191,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
int ret;
u32 cmd;
+ if (!dwc3_calc_trbs_left(dep))
+ return 0;
+
starting = !(dep->flags & DWC3_EP_BUSY);
dwc3_prepare_trbs(dep);
@@ -1216,8 +1208,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
if (starting) {
params.param0 = upper_32_bits(req->trb_dma);
params.param1 = lower_32_bits(req->trb_dma);
- cmd = DWC3_DEPCMD_STARTTRANSFER |
- DWC3_DEPCMD_PARAM(cmd_param);
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
} else {
cmd = DWC3_DEPCMD_UPDATETRANSFER |
DWC3_DEPCMD_PARAM(dep->resource_index);
@@ -1258,8 +1252,6 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, u32 cur_uf)
{
- u32 uf;
-
if (list_empty(&dep->pending_list)) {
dev_info(dwc->dev, "%s: ran out of requests\n",
dep->name);
@@ -1271,9 +1263,8 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
* Schedule the first trb for one interval in the future or at
* least 4 microframes.
*/
- uf = cur_uf + max_t(u32, 4, dep->interval);
-
- __dwc3_gadget_kick_transfer(dep, uf);
+ dep->frame_number = cur_uf + max_t(u32, 4, dep->interval);
+ __dwc3_gadget_kick_transfer(dep);
}
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
@@ -1290,7 +1281,6 @@ static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
- int ret = 0;
if (!dep->endpoint.desc) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
@@ -1337,24 +1327,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
}
if ((dep->flags & DWC3_EP_BUSY) &&
- !(dep->flags & DWC3_EP_MISSED_ISOC)) {
- WARN_ON_ONCE(!dep->resource_index);
- ret = __dwc3_gadget_kick_transfer(dep,
- dep->resource_index);
- }
-
- goto out;
- }
+ !(dep->flags & DWC3_EP_MISSED_ISOC))
+ goto out;
- if (!dwc3_calc_trbs_left(dep))
return 0;
+ }
- ret = __dwc3_gadget_kick_transfer(dep, 0);
out:
- if (ret == -EBUSY)
- ret = 0;
-
- return ret;
+ return __dwc3_gadget_kick_transfer(dep);
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
@@ -2347,7 +2327,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
req->request.actual = length - req->remaining;
if ((req->request.actual < length) && req->num_pending_sgs)
- return __dwc3_gadget_kick_transfer(dep, 0);
+ return __dwc3_gadget_kick_transfer(dep);
dwc3_gadget_giveback(dep, req, status);
@@ -2440,13 +2420,8 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
if (!dep->endpoint.desc)
return;
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- int ret;
-
- ret = __dwc3_gadget_kick_transfer(dep, 0);
- if (!ret || ret == -EBUSY)
- return;
- }
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -2487,15 +2462,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
dwc3_gadget_start_isoc(dwc, dep, event);
- } else {
- int ret;
-
- ret = __dwc3_gadget_kick_transfer(dep, 0);
- if (!ret || ret == -EBUSY)
- return;
- }
+ else
+ __dwc3_gadget_kick_transfer(dep);
break;
case DWC3_DEPEVT_STREAMEVT:
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 4a3227543255..578aa856f986 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*
@@ -5,15 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef __DRIVERS_USB_DWC3_GADGET_H
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index c69b06696824..70acdf94a0bf 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/**
* io.h - DesignWare USB3 DRD IO Header
*
@@ -5,15 +6,6 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef __DRIVERS_USB_DWC3_IO_H
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 19/64] usb: dwc3: import from kernel v4.16
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (17 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 18/64] usb: dwc3: import from kernel v4.15 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 20/64] usb: dwc3: import from kernel v4.17 Jens Wiklander
` (48 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.15 to v4.16.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.15..v4.16
Commits imported:
084a804e0120 usb: dwc3: Fix lock-up on ID change during system suspend/resume
98112041bcca usb: dwc3: core: Fix ULPI PHYs and prevent phy_get/ulpi_init during suspend/resume
b16ea8b9492e usb: dwc3: Fix GDBGFIFOSPACE_TYPE values
c49f63055e25 usb: dwc3: omap: don't miss events during suspend/resume
c4a5153e87fd usb: dwc3: core: Power-off core/PHYs on system_suspend in host mode
00b42170c86f usb: dwc3: Undo PHY init if soft reset fails
f035d139ffec usb: dwc3: ep0: Reset TRB counter for ep0 IN
6180026341e8 usb: dwc3: gadget: Set maxpacket size for ep0 IN
bff52352e0cc usb: dwc3: of-simple: fix oops by unbalanced clk disable call
f1faf88c137b Merge tag 'phy-for-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next
958e052c5448 Merge tag 'usb-for-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
d8c80bb3b55b phy: exynos5-usbdrd: Calibrate LOS levels for exynos5420/5800
d9e3d899bc7a Merge 4.15-rc4 into usb-next
42bf02ec6e42 usb: dwc3: Allow disabling of metastability workaround
204ec1af6257 usb: dwc3: Make sparse a bit happier
19e0b203bfbd usb: dwc3: ep0: use gadget->isoch_delay for isoch_delay value
0c5d2954f690 usb: dwc3: debug: decode a few more features
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 93 ++++++++++++++++++++++++++++-----------
drivers/usb/dwc3/core.h | 26 +++++++----
drivers/usb/dwc3/debug.h | 9 ++++
drivers/usb/dwc3/ep0.c | 13 +++---
drivers/usb/dwc3/gadget.c | 8 +++-
5 files changed, 106 insertions(+), 43 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 07832509584f..e94bf91cc58a 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -100,6 +100,8 @@ static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ dwc->current_dr_role = mode;
}
static void __dwc3_set_mode(struct work_struct *work)
@@ -133,8 +135,6 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_set_prtcap(dwc, dwc->desired_dr_role);
- dwc->current_dr_role = dwc->desired_dr_role;
-
spin_unlock_irqrestore(&dwc->lock, flags);
switch (dwc->desired_dr_role) {
@@ -147,6 +147,7 @@ static void __dwc3_set_mode(struct work_struct *work)
otg_set_vbus(dwc->usb2_phy->otg, true);
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+ phy_calibrate(dwc->usb2_generic_phy);
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -174,7 +175,7 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
dwc->desired_dr_role = mode;
spin_unlock_irqrestore(&dwc->lock, flags);
- queue_work(system_power_efficient_wq, &dwc->drd_work);
+ queue_work(system_freezable_wq, &dwc->drd_work);
}
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
@@ -218,7 +219,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
* XHCI driver will reset the host block. If dwc3 was configured for
* host-only mode, then we can return early.
*/
- if (dwc->dr_mode == USB_DR_MODE_HOST)
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -233,6 +234,9 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
udelay(1);
} while (--retries);
+ phy_exit(dwc->usb3_generic_phy);
+ phy_exit(dwc->usb2_generic_phy);
+
return -ETIMEDOUT;
}
@@ -482,6 +486,22 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
}
+static int dwc3_core_ulpi_init(struct dwc3 *dwc)
+{
+ int intf;
+ int ret = 0;
+
+ intf = DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3);
+
+ if (intf == DWC3_GHWPARAMS3_HSPHY_IFC_ULPI ||
+ (intf == DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI &&
+ dwc->hsphy_interface &&
+ !strncmp(dwc->hsphy_interface, "ulpi", 4)))
+ ret = dwc3_ulpi_init(dwc);
+
+ return ret;
+}
+
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -493,7 +513,6 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
static int dwc3_phy_setup(struct dwc3 *dwc)
{
u32 reg;
- int ret;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
@@ -564,9 +583,6 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
}
/* FALLTHROUGH */
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
- ret = dwc3_ulpi_init(dwc);
- if (ret)
- return ret;
/* FALLTHROUGH */
default:
break;
@@ -723,6 +739,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
}
static int dwc3_core_get_phy(struct dwc3 *dwc);
+static int dwc3_core_ulpi_init(struct dwc3 *dwc);
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
@@ -754,17 +771,27 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->maximum_speed = USB_SPEED_HIGH;
}
- ret = dwc3_core_get_phy(dwc);
+ ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
- ret = dwc3_core_soft_reset(dwc);
- if (ret)
- goto err0;
+ if (!dwc->ulpi_ready) {
+ ret = dwc3_core_ulpi_init(dwc);
+ if (ret)
+ goto err0;
+ dwc->ulpi_ready = true;
+ }
- ret = dwc3_phy_setup(dwc);
+ if (!dwc->phys_ready) {
+ ret = dwc3_core_get_phy(dwc);
+ if (ret)
+ goto err0a;
+ dwc->phys_ready = true;
+ }
+
+ ret = dwc3_core_soft_reset(dwc);
if (ret)
- goto err0;
+ goto err0a;
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
@@ -837,6 +864,9 @@ err1:
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
+err0a:
+ dwc3_ulpi_exit(dwc);
+
err0:
return ret;
}
@@ -915,7 +945,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
- dwc->current_dr_role = DWC3_GCTL_PRTCAP_DEVICE;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
if (dwc->usb2_phy)
@@ -931,7 +960,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_HOST:
- dwc->current_dr_role = DWC3_GCTL_PRTCAP_HOST;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
if (dwc->usb2_phy)
@@ -945,6 +973,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dev_err(dev, "failed to initialize host\n");
return ret;
}
+ phy_calibrate(dwc->usb2_generic_phy);
break;
case USB_DR_MODE_OTG:
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
@@ -1062,6 +1091,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
&dwc->fladj);
+ dwc->dis_metastability_quirk = device_property_read_bool(dev,
+ "snps,dis_metastability_quirk");
+
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -1229,7 +1261,6 @@ err4:
err3:
dwc3_free_event_buffers(dwc);
- dwc3_ulpi_exit(dwc);
err2:
pm_runtime_allow(&pdev->dev);
@@ -1279,7 +1310,7 @@ static int dwc3_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int dwc3_suspend_common(struct dwc3 *dwc)
+static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
unsigned long flags;
@@ -1291,6 +1322,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc)
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
+ /* do nothing during host runtime_suspend */
+ if (!PMSG_IS_AUTO(msg))
+ dwc3_core_exit(dwc);
+ break;
default:
/* do nothing */
break;
@@ -1299,7 +1334,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc)
return 0;
}
-static int dwc3_resume_common(struct dwc3 *dwc)
+static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
{
unsigned long flags;
int ret;
@@ -1315,6 +1350,13 @@ static int dwc3_resume_common(struct dwc3 *dwc)
spin_unlock_irqrestore(&dwc->lock, flags);
break;
case DWC3_GCTL_PRTCAP_HOST:
+ /* nothing to do on host runtime_resume */
+ if (!PMSG_IS_AUTO(msg)) {
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+ }
+ break;
default:
/* do nothing */
break;
@@ -1326,12 +1368,11 @@ static int dwc3_resume_common(struct dwc3 *dwc)
static int dwc3_runtime_checks(struct dwc3 *dwc)
{
switch (dwc->current_dr_role) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
+ case DWC3_GCTL_PRTCAP_DEVICE:
if (dwc->connected)
return -EBUSY;
break;
- case USB_DR_MODE_HOST:
+ case DWC3_GCTL_PRTCAP_HOST:
default:
/* do nothing */
break;
@@ -1348,7 +1389,7 @@ static int dwc3_runtime_suspend(struct device *dev)
if (dwc3_runtime_checks(dwc))
return -EBUSY;
- ret = dwc3_suspend_common(dwc);
+ ret = dwc3_suspend_common(dwc, PMSG_AUTO_SUSPEND);
if (ret)
return ret;
@@ -1364,7 +1405,7 @@ static int dwc3_runtime_resume(struct device *dev)
device_init_wakeup(dev, false);
- ret = dwc3_resume_common(dwc);
+ ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
if (ret)
return ret;
@@ -1411,7 +1452,7 @@ static int dwc3_suspend(struct device *dev)
struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
- ret = dwc3_suspend_common(dwc);
+ ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
if (ret)
return ret;
@@ -1427,7 +1468,7 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
- ret = dwc3_resume_common(dwc);
+ ret = dwc3_resume_common(dwc, PMSG_RESUME);
if (ret)
return ret;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4a4a4c98508c..860d2bc184d1 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -158,13 +158,15 @@
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
-#define DWC3_TXFIFOQ 1
-#define DWC3_RXFIFOQ 3
-#define DWC3_TXREQQ 5
-#define DWC3_RXREQQ 7
-#define DWC3_RXINFOQ 9
-#define DWC3_DESCFETCHQ 13
-#define DWC3_EVENTQ 15
+#define DWC3_TXFIFOQ 0
+#define DWC3_RXFIFOQ 1
+#define DWC3_TXREQQ 2
+#define DWC3_RXREQQ 3
+#define DWC3_RXINFOQ 4
+#define DWC3_PSTATQ 5
+#define DWC3_DESCFETCHQ 6
+#define DWC3_EVENTQ 7
+#define DWC3_AUXEVENTQ 8
/* Global RX Threshold Configuration Register */
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
@@ -795,8 +797,9 @@ struct dwc3_scratchpad_array {
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
* @usb3_generic_phy: pointer to USB3 PHY
+ * @phys_ready: flag to indicate that PHYs are ready
* @ulpi: pointer to ulpi interface
- * @isoch_delay: wValue from Set Isochronous Delay request;
+ * @ulpi_ready: flag to indicate that ULPI is initialized
* @u2sel: parameter from Set SEL request.
* @u2pel: parameter from Set SEL request.
* @u1sel: parameter from Set SEL request.
@@ -857,6 +860,7 @@ struct dwc3_scratchpad_array {
* 1 - -3.5dB de-emphasis
* 2 - No de-emphasis
* 3 - Reserved
+ * @dis_metastability_quirk: set to disable metastability quirk.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
@@ -893,7 +897,10 @@ struct dwc3 {
struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy;
+ bool phys_ready;
+
struct ulpi *ulpi;
+ bool ulpi_ready;
void __iomem *regs;
size_t regs_size;
@@ -955,7 +962,6 @@ struct dwc3 {
enum dwc3_ep0_state ep0state;
enum dwc3_link_state link_state;
- u16 isoch_delay;
u16 u2sel;
u16 u2pel;
u8 u1sel;
@@ -1010,6 +1016,8 @@ struct dwc3 {
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
+ unsigned dis_metastability_quirk:1;
+
u16 imod_interval;
};
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 368f8e59219a..bfb90c52d8fc 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -247,6 +247,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
case USB_DEVICE_TEST_MODE:
s = "Test Mode";
break;
+ case USB_DEVICE_U1_ENABLE:
+ s = "U1 Enable";
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ s = "U2 Enable";
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ s = "LTM Enable";
+ break;
default:
s = "UNKNOWN";
} s; }),
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index fd3e7ad2eb0e..18be31d5743a 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -736,11 +736,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct
if (wIndex || wLength)
return -EINVAL;
- /*
- * REVISIT It's unclear from Databook what to do with this
- * value. For now, just cache it.
- */
- dwc->isoch_delay = wValue;
+ dwc->gadget.isoch_delay = wValue;
return 0;
}
@@ -858,7 +854,12 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
trb++;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
trace_dwc3_complete_trb(ep0, trb);
- ep0->trb_enqueue = 0;
+
+ if (r->direction)
+ dwc->eps[1]->trb_enqueue = 0;
+ else
+ dwc->eps[0]->trb_enqueue = 0;
+
dwc->ep0_bounced = false;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 639dd1b163a0..2bda4eb1e9ac 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2005,7 +2005,8 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
* STAR#9000525659: Clock Domain Crossing on DCTL in
* USB 2.0 Mode
*/
- if (dwc->revision < DWC3_REVISION_220A) {
+ if (dwc->revision < DWC3_REVISION_220A &&
+ !dwc->dis_metastability_quirk) {
reg |= DWC3_DCFG_SUPERSPEED;
} else {
switch (speed) {
@@ -2744,6 +2745,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
break;
}
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+
/* Enable USB2 LPM Capability */
if ((dwc->revision > DWC3_REVISION_194A) &&
@@ -3224,7 +3227,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* is less than super speed because we don't have means, yet, to tell
* composite.c that we are USB 2.0 + LPM ECN.
*/
- if (dwc->revision < DWC3_REVISION_220A)
+ if (dwc->revision < DWC3_REVISION_220A &&
+ !dwc->dis_metastability_quirk)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 20/64] usb: dwc3: import from kernel v4.17
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (18 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 19/64] usb: dwc3: import from kernel v4.16 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 21/64] usb: dwc3: import from kernel v4.18 Jens Wiklander
` (47 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.16 to v4.17.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.16..v4.17
Commits imported:
96bd39df29c2 usb: dwc3: gadget: Fix list_del corruption in dwc3_ep_dequeue
9cffd15de0c3 usb: dwc3: gadget: dwc3_gadget_del_and_unmap_request() can be static
c91815b59624 usb: dwc3: gadget: never call ->complete() from ->ep_queue()
6d23ee9caa67 Merge tag 'usb-for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
7d11c3ac6669 usb: dwc3: core: Fix broken system suspend/resume on AM437x
2f3090c6a8f2 usb: dwc3: Check controller type before setting speed
80b776340c78 usb: dwc3: Dump LSP and BMU debug info
938a5ad1d305 usb: dwc3: Check for ESS TX/RX threshold config
6743e817a4de usb: dwc3: Add DWC_usb31 GTXTHRCFG reg fields
01b0e2cc7d89 usb: dwc3: gadget: Check IP revision for GRXTHRCFG
2fbc5bdc8fd1 usb: dwc3: Add DWC_usb31 GRXTHRCFG bit fields
d548a61767fa usb: dwc3: Check IP revision for GTXFIFOSIZ
0cab8d26d6e5 usb: dwc3: Update DWC_usb31 GTXFIFOSIZ reg fields
fab383333877 usb: dwc3: Add SoftReset PHY synchonization delay
cabdf83dadfb usb: dwc3: pci: Properly cleanup resource
de948a74ad6f usb: dwc3: Makefile: fix link error on randconfig
a8f25c36f732 Merge branch 4.16-rc6 into usb-next
7642d8386ac7 usb: dwc3: ep0: remove redundant assignment
498f0478aba4 usb: dwc3: Prevent indefinite sleep in _dwc3_set_mode during suspend/resume
f09cc79b4b33 usb: dwc3: add dual role support using OTG block
daaecc6541d0 usb: dwc3: prevent setting PRTCAP to OTG from debugfs
4cff75c7fe3d usb: dwc3: core.h: add some register definitions
e8284db48f1f usb: dwc3: of-simple: add support for the Amlogic Meson GXL and AXG SoCs
ff0a632f0875 usb: dwc3: of-simple: add support for shared and pulsed reset lines
6d5b53c1fd5c usb: dwc3: debugfs: Re-use DEFINE_SHOW_ATTRIBUTE() macro
134d1fd44221 Merge 4.16-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 134 +++++++++++++++++++++++++++++++---
drivers/usb/dwc3/core.h | 146 ++++++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/ep0.c | 2 +-
drivers/usb/dwc3/gadget.c | 84 +++++++++++++++-------
4 files changed, 332 insertions(+), 34 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index e94bf91cc58a..a15648d25e30 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
-static int dwc3_event_buffers_setup(struct dwc3 *dwc);
-
-static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -110,13 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work)
unsigned long flags;
int ret;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ dwc3_otg_update(dwc, 0);
+
if (!dwc->desired_dr_role)
return;
if (dwc->desired_dr_role == dwc->current_dr_role)
return;
- if (dwc->dr_mode != USB_DR_MODE_OTG)
+ if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
return;
switch (dwc->current_dr_role) {
@@ -127,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
default:
break;
}
@@ -162,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work)
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_init(dwc);
+ dwc3_otg_update(dwc, 0);
+ break;
default:
break;
}
+
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -229,7 +244,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
- return 0;
+ goto done;
udelay(1);
} while (--retries);
@@ -238,6 +253,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
phy_exit(dwc->usb2_generic_phy);
return -ETIMEDOUT;
+
+done:
+ /*
+ * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
+ * we must wait at least 50ms before accessing the PHY domain
+ * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ */
+ if (dwc3_is_usb31(dwc))
+ msleep(50);
+
+ return 0;
}
/*
@@ -348,7 +374,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
*
* Returns 0 on success otherwise negative errno.
*/
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -365,7 +391,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -846,6 +872,43 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ /*
+ * Must config both number of packets and max burst settings to enable
+ * RX and/or TX threshold.
+ */
+ if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
+ u8 rx_maxburst = dwc->rx_max_burst_prd;
+ u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
+ u8 tx_maxburst = dwc->tx_max_burst_prd;
+
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC31_RXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num);
+
+ reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC31_TXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num);
+
+ reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ }
+
return 0;
err4:
@@ -1016,6 +1079,10 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -1050,6 +1117,14 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&hird_threshold);
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
+ device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
+ &rx_thr_num_pkt_prd);
+ device_property_read_u8(dev, "snps,rx-max-burst-prd",
+ &rx_max_burst_prd);
+ device_property_read_u8(dev, "snps,tx-thr-num-pkt-prd",
+ &tx_thr_num_pkt_prd);
+ device_property_read_u8(dev, "snps,tx-max-burst-prd",
+ &tx_max_burst_prd);
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
@@ -1100,6 +1175,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
+ dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
+ dwc->rx_max_burst_prd = rx_max_burst_prd;
+
+ dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
+ dwc->tx_max_burst_prd = tx_max_burst_prd;
+
dwc->imod_interval = 0;
}
@@ -1326,6 +1407,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (!PMSG_IS_AUTO(msg))
dwc3_core_exit(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* do nothing during runtime_suspend */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_suspend(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ dwc3_otg_exit(dwc);
+ dwc3_core_exit(dwc);
+ break;
default:
/* do nothing */
break;
@@ -1345,6 +1440,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1355,7 +1451,29 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
ret = dwc3_core_init(dwc);
if (ret)
return ret;
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ }
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* nothing to do on runtime_resume */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_set_prtcap(dwc, dwc->current_dr_role);
+
+ dwc3_otg_init(dwc);
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
+ dwc3_otg_host_init(dwc);
+ } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_resume(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
}
+
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 860d2bc184d1..4f3b43809917 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -58,6 +58,11 @@
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+/* Controller's role while using the OTG block */
+#define DWC3_OTG_ROLE_IDLE 0
+#define DWC3_OTG_ROLE_HOST 1
+#define DWC3_OTG_ROLE_DEVICE 2
+
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
@@ -100,6 +105,11 @@
#define DWC3_GHWPARAMS7 0xc15c
#define DWC3_GDBGFIFOSPACE 0xc160
#define DWC3_GDBGLTSSM 0xc164
+#define DWC3_GDBGBMU 0xc16c
+#define DWC3_GDBGLSPMUX 0xc170
+#define DWC3_GDBGLSP 0xc174
+#define DWC3_GDBGEPINFO0 0xc178
+#define DWC3_GDBGEPINFO1 0xc17c
#define DWC3_GPRTBIMAP_HS0 0xc180
#define DWC3_GPRTBIMAP_HS1 0xc184
#define DWC3_GPRTBIMAP_FS0 0xc188
@@ -173,6 +183,26 @@
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
+/* Global RX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
+/* Global TX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN BIT(16)
@@ -201,6 +231,15 @@
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+/* Global Status Register */
+#define DWC3_GSTS_OTG_IP BIT(10)
+#define DWC3_GSTS_BC_IP BIT(9)
+#define DWC3_GSTS_ADP_IP BIT(8)
+#define DWC3_GSTS_HOST_IP BIT(7)
+#define DWC3_GSTS_DEVICE_IP BIT(6)
+#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
+#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
@@ -241,6 +280,8 @@
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
/* Global TX Fifo Size Register */
+#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
+#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
@@ -286,6 +327,11 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14)
+#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13)
+#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12)
+#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11)
+#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* Global HWPARAMS7 Register */
@@ -467,6 +513,74 @@
#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF BIT(5)
+#define DWC3_OCFG_HIBDISMASK BIT(4)
+#define DWC3_OCFG_SFTRSTMASK BIT(3)
+#define DWC3_OCFG_OTGVERSION BIT(2)
+#define DWC3_OCFG_HNPCAP BIT(1)
+#define DWC3_OCFG_SRPCAP BIT(0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR BIT(7)
+#define DWC3_OCTL_PERIMODE BIT(6)
+#define DWC3_OCTL_PRTPWRCTL BIT(5)
+#define DWC3_OCTL_HNPREQ BIT(4)
+#define DWC3_OCTL_SESREQ BIT(3)
+#define DWC3_OCTL_TERMSELIDPULSE BIT(2)
+#define DWC3_OCTL_DEVSETHNPEN BIT(1)
+#define DWC3_OCTL_HSTSETHNPEN BIT(0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE BIT(31)
+#define DWC3_OEVT_XHCIRUNSTPSET BIT(27)
+#define DWC3_OEVT_DEVRUNSTPSET BIT(26)
+#define DWC3_OEVT_HIBENTRY BIT(25)
+#define DWC3_OEVT_CONIDSTSCHNG BIT(24)
+#define DWC3_OEVT_HRRCONFNOTIF BIT(23)
+#define DWC3_OEVT_HRRINITNOTIF BIT(22)
+#define DWC3_OEVT_ADEVIDLE BIT(21)
+#define DWC3_OEVT_ADEVBHOSTEND BIT(20)
+#define DWC3_OEVT_ADEVHOST BIT(19)
+#define DWC3_OEVT_ADEVHNPCHNG BIT(18)
+#define DWC3_OEVT_ADEVSRPDET BIT(17)
+#define DWC3_OEVT_ADEVSESSENDDET BIT(16)
+#define DWC3_OEVT_BDEVBHOSTEND BIT(11)
+#define DWC3_OEVT_BDEVHNPCHNG BIT(10)
+#define DWC3_OEVT_BDEVSESSVLDDET BIT(9)
+#define DWC3_OEVT_BDEVVBUSCHNG BIT(8)
+#define DWC3_OEVT_BSESSVLD BIT(3)
+#define DWC3_OEVT_HSTNEGSTS BIT(2)
+#define DWC3_OEVT_SESREQSTS BIT(1)
+#define DWC3_OEVT_ERROR BIT(0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26)
+#define DWC3_OEVTEN_HIBENTRYEN BIT(25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22)
+#define DWC3_OEVTEN_ADEVIDLEEN BIT(21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20)
+#define DWC3_OEVTEN_ADEVHOSTEN BIT(19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18)
+#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16)
+#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP BIT(13)
+#define DWC3_OSTS_XHCIRUNSTP BIT(12)
+#define DWC3_OSTS_PERIPHERALSTATE BIT(4)
+#define DWC3_OSTS_XHCIPRTPOWER BIT(3)
+#define DWC3_OSTS_BSESVLD BIT(2)
+#define DWC3_OSTS_VBUSVLD BIT(1)
+#define DWC3_OSTS_CONIDSTS BIT(0)
+
/* Structures */
struct dwc3_trb;
@@ -781,6 +895,10 @@ struct dwc3_scratchpad_array {
* @regs_size: address space size
* @fladj: frame length adjustment
* @irq_gadget: peripheral controller's IRQ number
+ * @otg_irq: IRQ number for OTG IRQs
+ * @current_otg_role: current role of operation while using the OTG block
+ * @desired_otg_role: desired role of operation while using the OTG block
+ * @otg_restart_host: flag that OTG controller needs to restart host
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -816,6 +934,10 @@ struct dwc3_scratchpad_array {
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
+ * @rx_thr_num_pkt_prd: periodic ESS receive packet count
+ * @rx_max_burst_prd: max periodic ESS receive burst size
+ * @tx_thr_num_pkt_prd: periodic ESS transmit packet count
+ * @tx_max_burst_prd: max periodic ESS transmit burst size
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @delayed_status: true when gadget driver asks for delayed status
@@ -914,6 +1036,10 @@ struct dwc3 {
u32 fladj;
u32 irq_gadget;
+ u32 otg_irq;
+ u32 current_otg_role;
+ u32 desired_otg_role;
+ bool otg_restart_host;
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
@@ -979,6 +1105,10 @@ struct dwc3 {
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
const char *hsphy_interface;
@@ -1175,6 +1305,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
@@ -1192,6 +1323,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc)
bool dwc3_has_imod(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
@@ -1235,11 +1369,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_drd_init(struct dwc3 *dwc);
void dwc3_drd_exit(struct dwc3 *dwc);
+void dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus);
+void dwc3_otg_host_init(struct dwc3 *dwc);
#else
static inline int dwc3_drd_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_drd_exit(struct dwc3 *dwc)
{ }
+static inline void dwc3_otg_init(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{ }
+static inline void dwc3_otg_host_init(struct dwc3 *dwc)
+{ }
#endif
/* power management interface */
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 18be31d5743a..5a991bca8ed7 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -814,7 +814,7 @@ out:
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- struct dwc3_request *r = NULL;
+ struct dwc3_request *r;
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2bda4eb1e9ac..0dedf8a799f4 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -166,18 +166,8 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
dwc3_ep_inc_trb(&dep->trb_dequeue);
}
-/**
- * dwc3_gadget_giveback - call struct usb_request's ->complete callback
- * @dep: The endpoint to whom the request belongs to
- * @req: The request we're giving back
- * @status: completion code for the request
- *
- * Must be called with controller's lock held and interrupts disabled. This
- * function will unmap @req and call its ->complete() callback to notify upper
- * layers that it has completed.
- */
-void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
- int status)
+static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
+ struct dwc3_request *req, int status)
{
struct dwc3 *dwc = dep->dwc;
@@ -190,18 +180,35 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->trb)
usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
+ &req->request, req->direction);
req->trb = NULL;
-
trace_dwc3_gadget_giveback(req);
+ if (dep->number > 1)
+ pm_runtime_put(dwc->dev);
+}
+
+/**
+ * dwc3_gadget_giveback - call struct usb_request's ->complete callback
+ * @dep: The endpoint to whom the request belongs to
+ * @req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ int status)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ dwc3_gadget_del_and_unmap_request(dep, req, status);
+
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
-
- if (dep->number > 1)
- pm_runtime_put(dwc->dev);
}
/**
@@ -1227,7 +1234,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
if (req->trb)
memset(req->trb, 0, sizeof(struct dwc3_trb));
dep->queued_requests--;
- dwc3_gadget_giveback(dep, req, ret);
+ dwc3_gadget_del_and_unmap_request(dep, req, ret);
return ret;
}
@@ -1417,7 +1424,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
dwc->lock);
if (!r->trb)
- goto out1;
+ goto out0;
if (r->num_pending_sgs) {
struct dwc3_trb *trb;
@@ -1858,7 +1865,11 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ if (dwc3_is_usb31(dwc))
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc);
@@ -1950,6 +1961,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int epnum;
+ u32 tmo_eps = 0;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1960,6 +1972,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
struct dwc3_ep *dep = dwc->eps[epnum];
+ int ret;
if (!dep)
continue;
@@ -1967,9 +1980,24 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
continue;
- wait_event_lock_irq(dep->wait_end_transfer,
- !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
- dwc->lock);
+ ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock, msecs_to_jiffies(5));
+
+ if (ret <= 0) {
+ /* Timed out or interrupted! There's nothing much
+ * we can do so we just log here and print which
+ * endpoints timed out at the end.
+ */
+ tmo_eps |= 1 << epnum;
+ dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
+ }
+ }
+
+ if (tmo_eps) {
+ dev_err(dwc->dev,
+ "end transfer timed out on endpoints 0x%x [bitmap]\n",
+ tmo_eps);
}
out:
@@ -2023,7 +2051,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
reg |= DWC3_DCFG_SUPERSPEED;
break;
case USB_SPEED_SUPER_PLUS:
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ if (dwc3_is_usb31(dwc))
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED;
break;
default:
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
@@ -2101,7 +2132,10 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num));
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ else
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 21/64] usb: dwc3: import from kernel v4.18
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (19 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 20/64] usb: dwc3: import from kernel v4.17 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 22/64] usb: dwc3: import from kernel v4.19 Jens Wiklander
` (46 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.17 to v4.18.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.17..v4.18
Commits imported:
9566a7c72f4f usb: dwc3: gadget: remove redundant variable maxpacket
9925e6ebe5c2 usb: dwc3: qcom: mark PM functions as __maybe_unused
93ef2dc0c4f7 usb: dwc3: Fix error return code in dwc3_qcom_probe()
615277779f41 usb: dwc3: Only call clk_bulk_get() on devicetree instantiated devices
896e518883f1 usb: dwc3: of-simple: fix use-after-free on remove
00908693c481 usb: dwc3: pci: add support for Intel IceLake
535c8dc5c480 USB: dwc3: no need to check return value of debugfs_create functions
3bea1cfcff11 usb: dwc3: Remove DEBUG define from Qualcomm DWC3 glue driver
05645366f389 usb: dwc3: gadget: Fix list_del corruption in dwc3_ep_dequeue
691025107eb7 usb: dwc3: gadget: dwc3_gadget_del_and_unmap_request() can be static
edbbfe19444e usb: dwc3: dwc3_get_extcon() can be static
31a2f5a7e136 usb: dwc3: gadget: init req->{direction,epnum} from alloc_request()
d7ca7e1896b2 usb: dwc3: gadget: initialize transfer index from send_gadget_ep_cmd()
4439661d64f5 usb: dwc3: gadget: assign resource_index inside get_transfer_index()
f62afb4929a8 usb: dwc3: gadget: always start isochronous aligned to dep->interval
d513320f1fd0 usb: dwc3: gadget: don't issue End Transfer if we have started reqs
e1d542f712e1 usb: dwc3: debug: decode uFrame from event too
42626919efab usb: dwc3: gadget: move dwc3_calc_trbs_left() in place of prototype
20d1d43fa09a usb: dwc3: gadget: move set_xfer_resource() in place of prototype
b07c2db83f92 usb: dwc3: gadget: remove a few more dwc arguments
a2d23f08038e usb: dwc3: gadget: combine modify & restore into single argument
8f1c99cd24b0 usb: dwc3: gadget: refactor dwc3_gadget_init_endpoints()
f38e35dd84e2 usb: dwc3: gadget: split dwc3_gadget_ep_cleanup_completed_requests()
6d9d22dac114 usb: dwc3: gadget: get rid of the length variable
fee73e6144d8 usb: dwc3: gadget: reduce scope of ret variable
6afbdb57732b usb: dwc3: gadget: one declaration per line
e0c42ce590fe usb: dwc3: gadget: simplify IOC handling
d80fe1b6e34d usb: dwc3: gadget: simplify short packet event
b27972b508d1 usb: dwc3: trace: print out event status too
8b3b7b66c0e6 usb: dwc3: gadget: simplify unaligned and zlp handling
cf35fc336e66 usb: dwc3: gadget: remove unnecessary 'chain' variable
021595963a3c usb: dwc3: gadget: remove PENDING handling from cleanup_completed
d36929538f8b usb: dwc3: gadget: split scatterlist and linear handlers
d5044a04b529 usb: dwc3: gadget: simplify isoc case on cleanup_completed_requests
fe990cea8ed5 usb: dwc3: gadget: simplify queueing of isoc transfers
4d588a145b3e usb: dwc3: gadget: remove duplicated missed isoc handling
6d8a019614f3 usb: dwc3: gadget: check for Missed Isoc from event status
58f0218a271d usb: dwc3: gadget: remove unnecessary 'ioc' variable
12a3a4ada816 usb: dwc3: gadget: make cleanup_completed_requests() return nothing
5f2e7975f0df usb: dwc3: gadget: remove DWC3_EP_BUSY flag
1912cbc60f44 usb: dwc3: gadget: start removing BUSY flag
ee3638b897b9 usb: dwc3: gadget: update dep->frame_number from XferInprogress too
5828cada9908 usb: dwc3: gadget: always use frame number from XferNotReady
8f608e8ab628 usb: dwc3: gadget: remove unnecessary 'dwc' parameter
320338651d33 usb: dwc3: gadget: move handler closer to calling site
ed27442e5093 usb: dwc3: gadget: rename dwc3_gadget_start_isoc()
a24a6ab1493a usb: dwc3: gadget: remove some pointless checks
0bd0f6d201eb usb: dwc3: gadget: remove allocated/queued request tracking
66f5dd5a0379 usb: dwc3: gadget: rename done_trbs and done_reqs
a861282f5037 usb: dwc3: gadget: don't kick transfer all the time
fbea935accf4 usb: dwc3: gadget: rename dwc3_endpoint_transfer_complete()
742a4fff5f29 usb: dwc3: gadget: XferComplete only for EP0
38408464aa76 usb: dwc3: gadget: XferNotReady is Isoc-only
52fcc0bead0f usb: dwc3: gadget: pre-issue Start Transfer for Interrupt EPs too
c96e6725db9d usb: dwc3: gadget: Correct the logic for queuing sgs
a31e63b608ff usb: dwc3: gadget: Correct handling of scattergather lists
5f0b74e54890 USB: dwc3: get extcon device by OF graph bindings
fe8abf332b8f usb: dwc3: support clocks and resets for DWC3 core
44feb8e6ea2d usb: dwc3: use local copy of resource to fix-up register offset
bcb128777af5 usb: dwc3: core: Suspend PHYs on runtime suspend in host mode
a4333c3a6ba9 usb: dwc3: Add Qualcomm DWC3 glue driver
e362098f0e12 usb: dwc3: of-simple: Add compatible for Allwinner H6 platform
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 157 ++++++--
drivers/usb/dwc3/core.h | 25 +-
drivers/usb/dwc3/debug.h | 26 +-
drivers/usb/dwc3/ep0.c | 9 +-
drivers/usb/dwc3/gadget.c | 773 ++++++++++++++++++--------------------
drivers/usb/dwc3/gadget.h | 5 +-
6 files changed, 531 insertions(+), 464 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a15648d25e30..103807587dc6 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -8,6 +8,7 @@
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
+#include <linux/clk.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -24,6 +25,7 @@
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/reset.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -266,6 +268,12 @@ done:
return 0;
}
+static const struct clk_bulk_data dwc3_core_clks[] = {
+ { .id = "ref" },
+ { .id = "bus_early" },
+ { .id = "suspend" },
+};
+
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
@@ -667,6 +675,9 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+ clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ reset_control_assert(dwc->reset);
}
static bool dwc3_core_is_valid(struct dwc3 *dwc)
@@ -1245,7 +1256,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
+ struct resource *res, dwc_res;
struct dwc3 *dwc;
int ret;
@@ -1256,6 +1267,11 @@ static int dwc3_probe(struct platform_device *pdev)
if (!dwc)
return -ENOMEM;
+ dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks),
+ GFP_KERNEL);
+ if (!dwc->clks)
+ return -ENOMEM;
+
dwc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1270,23 +1286,52 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->xhci_resources[0].flags = res->flags;
dwc->xhci_resources[0].name = res->name;
- res->start += DWC3_GLOBALS_REGS_START;
-
/*
* Request memory region but exclude xHCI regs,
* since it will be requested by the xhci-plat driver.
*/
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs)) {
- ret = PTR_ERR(regs);
- goto err0;
- }
+ dwc_res = *res;
+ dwc_res.start += DWC3_GLOBALS_REGS_START;
+
+ regs = devm_ioremap_resource(dev, &dwc_res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
dwc->regs = regs;
- dwc->regs_size = resource_size(res);
+ dwc->regs_size = resource_size(&dwc_res);
dwc3_get_properties(dwc);
+ dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(dwc->reset))
+ return PTR_ERR(dwc->reset);
+
+ if (dev->of_node) {
+ dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
+
+ ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ /*
+ * Clocks are optional, but new DT platforms should support all
+ * clocks as required by the DT-binding.
+ */
+ if (ret)
+ dwc->num_clks = 0;
+ }
+
+ ret = reset_control_deassert(dwc->reset);
+ if (ret)
+ goto put_clks;
+
+ ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto assert_reset;
+
+ ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto unprepare_clks;
+
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
@@ -1350,13 +1395,13 @@ err1:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-err0:
- /*
- * restore res->start back to its original value so that, in case the
- * probe is deferred, we don't end up getting error in request the
- * memory region the next time probe is called.
- */
- res->start -= DWC3_GLOBALS_REGS_START;
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+unprepare_clks:
+ clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+assert_reset:
+ reset_control_assert(dwc->reset);
+put_clks:
+ clk_bulk_put(dwc->num_clks, dwc->clks);
return ret;
}
@@ -1364,15 +1409,8 @@ err0:
static int dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pm_runtime_get_sync(&pdev->dev);
- /*
- * restore res->start back to its original value so that, in case the
- * probe is deferred, we don't end up getting error in request the
- * memory region the next time probe is called.
- */
- res->start -= DWC3_GLOBALS_REGS_START;
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
@@ -1386,14 +1424,48 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
+ clk_bulk_put(dwc->num_clks, dwc->clks);
return 0;
}
#ifdef CONFIG_PM
+static int dwc3_core_init_for_resume(struct dwc3 *dwc)
+{
+ int ret;
+
+ ret = reset_control_deassert(dwc->reset);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto assert_reset;
+
+ ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto unprepare_clks;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ goto disable_clks;
+
+ return 0;
+
+disable_clks:
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+unprepare_clks:
+ clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+assert_reset:
+ reset_control_assert(dwc->reset);
+
+ return ret;
+}
+
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
unsigned long flags;
+ u32 reg;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -1403,9 +1475,25 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
- /* do nothing during host runtime_suspend */
- if (!PMSG_IS_AUTO(msg))
+ if (!PMSG_IS_AUTO(msg)) {
dwc3_core_exit(dwc);
+ break;
+ }
+
+ /* Let controller to suspend HSPHY before PHY driver suspends */
+ if (dwc->dis_u2_susphy_quirk ||
+ dwc->dis_enblslpm_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
+ DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ /* Give some time for USB2 PHY to suspend */
+ usleep_range(5000, 6000);
+ }
+
+ phy_pm_runtime_put_sync(dwc->usb2_generic_phy);
+ phy_pm_runtime_put_sync(dwc->usb3_generic_phy);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* do nothing during runtime_suspend */
@@ -1433,10 +1521,11 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
{
unsigned long flags;
int ret;
+ u32 reg;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
- ret = dwc3_core_init(dwc);
+ ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
@@ -1446,13 +1535,25 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
spin_unlock_irqrestore(&dwc->lock, flags);
break;
case DWC3_GCTL_PRTCAP_HOST:
- /* nothing to do on host runtime_resume */
if (!PMSG_IS_AUTO(msg)) {
- ret = dwc3_core_init(dwc);
+ ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ break;
}
+ /* Restore GUSB2PHYCFG bits that were modified in suspend */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (dwc->dis_u2_susphy_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
+ if (dwc->dis_enblslpm_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ phy_pm_runtime_get_sync(dwc->usb2_generic_phy);
+ phy_pm_runtime_get_sync(dwc->usb3_generic_phy);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* nothing to do on runtime_resume */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4f3b43809917..285ce0ef3b91 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -639,8 +639,6 @@ struct dwc3_event_buffer {
* @resource_index: Resource transfer index
* @frame_number: set to the frame number we want this transfer to start (ISOC)
* @interval: the interval on which the ISOC transfer is started
- * @allocated_requests: number of requests allocated
- * @queued_requests: number of requests queued for transfer
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
* @stream_capable: true when streams are enabled
@@ -664,11 +662,9 @@ struct dwc3_ep {
#define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2)
-#define DWC3_EP_BUSY BIT(4)
+#define DWC3_EP_TRANSFER_STARTED BIT(3)
#define DWC3_EP_PENDING_REQUEST BIT(5)
-#define DWC3_EP_MISSED_ISOC BIT(6)
#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
-#define DWC3_EP_TRANSFER_STARTED BIT(8)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -688,8 +684,6 @@ struct dwc3_ep {
u8 number;
u8 type;
u8 resource_index;
- u32 allocated_requests;
- u32 queued_requests;
u32 frame_number;
u32 interval;
@@ -832,7 +826,9 @@ struct dwc3_hwparams {
* @list: a list_head used for request queueing
* @dep: struct dwc3_ep owning this request
* @sg: pointer to first incomplete sg
+ * @start_sg: pointer to the sg which should be queued next
* @num_pending_sgs: counter to pending sgs
+ * @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
@@ -848,8 +844,10 @@ struct dwc3_request {
struct list_head list;
struct dwc3_ep *dep;
struct scatterlist *sg;
+ struct scatterlist *start_sg;
unsigned num_pending_sgs;
+ unsigned int num_queued_sgs;
unsigned remaining;
u8 epnum;
struct dwc3_trb *trb;
@@ -891,6 +889,9 @@ struct dwc3_scratchpad_array {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
+ * @clks: array of clocks
+ * @num_clks: number of clocks
+ * @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
* @fladj: frame length adjustment
@@ -1013,6 +1014,11 @@ struct dwc3 {
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ struct reset_control *reset;
+
struct usb_phy *usb2_phy;
struct usb_phy *usb3_phy;
@@ -1197,11 +1203,12 @@ struct dwc3_event_depevt {
/* Within XferNotReady */
#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3)
-/* Within XferComplete */
+/* Within XferComplete or XferInProgress */
#define DEPEVT_STATUS_BUSERR BIT(0)
#define DEPEVT_STATUS_SHORT BIT(1)
#define DEPEVT_STATUS_IOC BIT(2)
-#define DEPEVT_STATUS_LST BIT(3)
+#define DEPEVT_STATUS_LST BIT(3) /* XferComplete */
+#define DEPEVT_STATUS_MISSED_ISOC BIT(3) /* XferInProgress */
/* Stream event only */
#define DEPEVT_STREAMEVT_FOUND 1
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index bfb90c52d8fc..c66d216dcc30 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -475,21 +475,37 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
if (ret < 0)
return "UNKNOWN";
+ status = event->status;
+
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- strcat(str, "Transfer Complete");
+ len = strlen(str);
+ sprintf(str + len, "Transfer Complete (%c%c%c)",
+ status & DEPEVT_STATUS_SHORT ? 'S' : 's',
+ status & DEPEVT_STATUS_IOC ? 'I' : 'i',
+ status & DEPEVT_STATUS_LST ? 'L' : 'l');
+
len = strlen(str);
if (epnum <= 1)
sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state));
break;
case DWC3_DEPEVT_XFERINPROGRESS:
- strcat(str, "Transfer In-Progress");
+ len = strlen(str);
+
+ sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)",
+ event->parameters,
+ status & DEPEVT_STATUS_SHORT ? 'S' : 's',
+ status & DEPEVT_STATUS_IOC ? 'I' : 'i',
+ status & DEPEVT_STATUS_LST ? 'M' : 'm');
break;
case DWC3_DEPEVT_XFERNOTREADY:
- strcat(str, "Transfer Not Ready");
- status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
- strcat(str, status ? " (Active)" : " (Not Active)");
+ len = strlen(str);
+
+ sprintf(str + len, "Transfer Not Ready [%d]%s",
+ event->parameters,
+ status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
+ " (Active)" : " (Not Active)");
/* Control Endpoints */
if (epnum <= 1) {
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 5a991bca8ed7..8efde178eef4 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -66,7 +66,7 @@ static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
struct dwc3 *dwc;
int ret;
- if (dep->flags & DWC3_EP_BUSY)
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
return 0;
dwc = dep->dwc;
@@ -79,8 +79,6 @@ static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
if (ret < 0)
return ret;
- dep->flags |= DWC3_EP_BUSY;
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
return 0;
@@ -913,7 +911,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
{
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
- dep->flags &= ~DWC3_EP_BUSY;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dep->resource_index = 0;
dwc->setup_packet_pending = false;
@@ -975,15 +973,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dep);
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
- u32 maxpacket;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
if (ret)
return;
- maxpacket = dep->endpoint.maxpacket;
-
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length,
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 0dedf8a799f4..69bf137aab37 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -27,6 +27,9 @@
#include "gadget.h"
#include "io.h"
+#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
+ & ~((d)->interval - 1))
+
/**
* dwc3_gadget_set_test_mode - enables usb2 test modes
* @dwc: pointer to our context structure
@@ -375,6 +378,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
switch (DWC3_DEPCMD_CMD(cmd)) {
case DWC3_DEPCMD_STARTTRANSFER:
dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_get_transfer_index(dep);
break;
case DWC3_DEPCMD_ENDTRANSFER:
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
@@ -455,7 +459,17 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
dep->trb_pool_dma = 0;
}
-static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
+static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+
+ memset(¶ms, 0x00, sizeof(params));
+
+ params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
+
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
+ ¶ms);
+}
/**
* dwc3_gadget_start_config - configure ep resources
@@ -491,9 +505,10 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
* triggered only when called for EP0-out, which always happens first, and which
* should only happen in one of the above conditions.
*/
-static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
+static int dwc3_gadget_start_config(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc;
u32 cmd;
int i;
int ret;
@@ -503,6 +518,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
memset(¶ms, 0x00, sizeof(params));
cmd = DWC3_DEPCMD_DEPSTARTCFG;
+ dwc = dep->dwc;
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret)
@@ -514,7 +530,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
if (!dep)
continue;
- ret = dwc3_gadget_set_xfer_resource(dwc, dep);
+ ret = dwc3_gadget_set_xfer_resource(dep);
if (ret)
return ret;
}
@@ -522,16 +538,12 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
return 0;
}
-static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
- bool modify, bool restore)
+static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
{
const struct usb_ss_ep_comp_descriptor *comp_desc;
const struct usb_endpoint_descriptor *desc;
struct dwc3_gadget_ep_cmd_params params;
-
- if (dev_WARN_ONCE(dwc->dev, modify && restore,
- "Can't modify and restore\n"))
- return -EINVAL;
+ struct dwc3 *dwc = dep->dwc;
comp_desc = dep->endpoint.comp_desc;
desc = dep->endpoint.desc;
@@ -547,14 +559,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
- if (modify) {
- params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;
- } else if (restore) {
- params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
+ params.param0 |= action;
+ if (action == DWC3_DEPCFG_ACTION_RESTORE)
params.param2 |= dep->saved_state;
- } else {
- params.param0 |= DWC3_DEPCFG_ACTION_INIT;
- }
if (usb_endpoint_xfer_control(desc))
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
@@ -594,29 +601,15 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
}
-static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
-{
- struct dwc3_gadget_ep_cmd_params params;
-
- memset(¶ms, 0x00, sizeof(params));
-
- params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
-
- return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
- ¶ms);
-}
-
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
- * @modify: if true, modify existing endpoint configuration
- * @restore: if true, restore endpoint configuration from scratch buffer
+ * @action: one of INIT, MODIFY or RESTORE
*
* Caller should take care of locking. Execute all necessary commands to
* initialize a HW endpoint so it can be used by a gadget driver.
*/
-static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
- bool modify, bool restore)
+static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
@@ -625,12 +618,12 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
int ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
- ret = dwc3_gadget_start_config(dwc, dep);
+ ret = dwc3_gadget_start_config(dep);
if (ret)
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, modify, restore);
+ ret = dwc3_gadget_set_ep_config(dep, action);
if (ret)
return ret;
@@ -671,7 +664,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if (usb_endpoint_xfer_bulk(desc)) {
+ if (usb_endpoint_xfer_bulk(desc) ||
+ usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
@@ -689,26 +683,20 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret < 0)
return ret;
-
- dep->flags |= DWC3_EP_BUSY;
-
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
- WARN_ON_ONCE(!dep->resource_index);
}
-
out:
trace_dwc3_gadget_ep_enable(dep);
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
- dwc3_stop_active_transfer(dwc, dep->number, true);
+ dwc3_stop_active_transfer(dep, true);
/* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) {
@@ -806,7 +794,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return 0;
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -840,7 +828,7 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
}
static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
- gfp_t gfp_flags)
+ gfp_t gfp_flags)
{
struct dwc3_request *req;
struct dwc3_ep *dep = to_dwc3_ep(ep);
@@ -849,11 +837,10 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
if (!req)
return NULL;
+ req->direction = dep->direction;
req->epnum = dep->number;
req->dep = dep;
- dep->allocated_requests++;
-
trace_dwc3_alloc_request(req);
return &req->request;
@@ -863,14 +850,58 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request)
{
struct dwc3_request *req = to_dwc3_request(request);
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- dep->allocated_requests--;
trace_dwc3_free_request(req);
kfree(req);
}
-static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
+/**
+ * dwc3_ep_prev_trb - returns the previous TRB in the ring
+ * @dep: The endpoint with the TRB ring
+ * @index: The index of the current TRB in the ring
+ *
+ * Returns the TRB prior to the one pointed to by the index. If the
+ * index is 0, we will wrap backwards, skip the link TRB, and return
+ * the one just before that.
+ */
+static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
+{
+ u8 tmp = index;
+
+ if (!tmp)
+ tmp = DWC3_TRB_NUM - 1;
+
+ return &dep->trb_pool[tmp - 1];
+}
+
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
+{
+ struct dwc3_trb *tmp;
+ u8 trbs_left;
+
+ /*
+ * If enqueue & dequeue are equal than it is either full or empty.
+ *
+ * One way to know for sure is if the TRB right before us has HWO bit
+ * set or not. If it has, then we're definitely full and can't fit any
+ * more transfers in our ring.
+ */
+ if (dep->trb_enqueue == dep->trb_dequeue) {
+ tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+ if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ return 0;
+
+ return DWC3_TRB_NUM - 1;
+ }
+
+ trbs_left = dep->trb_dequeue - dep->trb_enqueue;
+ trbs_left &= (DWC3_TRB_NUM - 1);
+
+ if (dep->trb_dequeue < dep->trb_enqueue)
+ trbs_left--;
+
+ return trbs_left;
+}
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
@@ -985,11 +1016,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
- unsigned length = req->request.length;
+ unsigned int length;
+ dma_addr_t dma;
unsigned stream_id = req->request.stream_id;
unsigned short_not_ok = req->request.short_not_ok;
unsigned no_interrupt = req->request.no_interrupt;
- dma_addr_t dma = req->request.dma;
+
+ if (req->request.num_sgs > 0) {
+ length = sg_dma_len(req->start_sg);
+ dma = sg_dma_address(req->start_sg);
+ } else {
+ length = req->request.length;
+ dma = req->request.dma;
+ }
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -997,69 +1036,23 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
dwc3_gadget_move_started_request(req);
req->trb = trb;
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- dep->queued_requests++;
}
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
stream_id, short_not_ok, no_interrupt);
}
-/**
- * dwc3_ep_prev_trb - returns the previous TRB in the ring
- * @dep: The endpoint with the TRB ring
- * @index: The index of the current TRB in the ring
- *
- * Returns the TRB prior to the one pointed to by the index. If the
- * index is 0, we will wrap backwards, skip the link TRB, and return
- * the one just before that.
- */
-static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
-{
- u8 tmp = index;
-
- if (!tmp)
- tmp = DWC3_TRB_NUM - 1;
-
- return &dep->trb_pool[tmp - 1];
-}
-
-static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
-{
- struct dwc3_trb *tmp;
- u8 trbs_left;
-
- /*
- * If enqueue & dequeue are equal than it is either full or empty.
- *
- * One way to know for sure is if the TRB right before us has HWO bit
- * set or not. If it has, then we're definitely full and can't fit any
- * more transfers in our ring.
- */
- if (dep->trb_enqueue == dep->trb_dequeue) {
- tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
- return 0;
-
- return DWC3_TRB_NUM - 1;
- }
-
- trbs_left = dep->trb_dequeue - dep->trb_enqueue;
- trbs_left &= (DWC3_TRB_NUM - 1);
-
- if (dep->trb_dequeue < dep->trb_enqueue)
- trbs_left--;
-
- return trbs_left;
-}
-
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- struct scatterlist *sg = req->sg;
+ struct scatterlist *sg = req->start_sg;
struct scatterlist *s;
int i;
- for_each_sg(sg, s, req->num_pending_sgs, i) {
+ unsigned int remaining = req->request.num_mapped_sgs
+ - req->num_queued_sgs;
+
+ for_each_sg(sg, s, remaining, i) {
unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
@@ -1088,6 +1081,18 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
dwc3_prepare_one_trb(dep, req, chain, i);
}
+ /*
+ * There can be a situation where all sgs in sglist are not
+ * queued because of insufficient trb number. To handle this
+ * case, update start_sg to next sg to be queued, so that
+ * we have free trbs we can continue queuing from where we
+ * previously stopped
+ */
+ if (chain)
+ req->start_sg = sg_next(s);
+
+ req->num_queued_sgs++;
+
if (!dwc3_calc_trbs_left(dep))
break;
}
@@ -1178,6 +1183,8 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
return;
req->sg = req->request.sg;
+ req->start_sg = req->sg;
+ req->num_queued_sgs = 0;
req->num_pending_sgs = req->request.num_mapped_sgs;
if (req->num_pending_sgs > 0)
@@ -1201,7 +1208,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return 0;
- starting = !(dep->flags & DWC3_EP_BUSY);
+ starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
dwc3_prepare_trbs(dep);
req = next_request(&dep->started_list);
@@ -1233,18 +1240,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
*/
if (req->trb)
memset(req->trb, 0, sizeof(struct dwc3_trb));
- dep->queued_requests--;
dwc3_gadget_del_and_unmap_request(dep, req, ret);
return ret;
}
- dep->flags |= DWC3_EP_BUSY;
-
- if (starting) {
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
- WARN_ON_ONCE(!dep->resource_index);
- }
-
return 0;
}
@@ -1256,35 +1255,19 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
return DWC3_DSTS_SOFFN(reg);
}
-static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
- struct dwc3_ep *dep, u32 cur_uf)
+static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
if (list_empty(&dep->pending_list)) {
- dev_info(dwc->dev, "%s: ran out of requests\n",
+ dev_info(dep->dwc->dev, "%s: ran out of requests\n",
dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
}
- /*
- * Schedule the first trb for one interval in the future or at
- * least 4 microframes.
- */
- dep->frame_number = cur_uf + max_t(u32, 4, dep->interval);
+ dep->frame_number = DWC3_ALIGN_FRAME(dep);
__dwc3_gadget_kick_transfer(dep);
}
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
- u32 cur_uf, mask;
-
- mask = ~(dep->interval - 1);
- cur_uf = event->parameters & mask;
-
- __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
-}
-
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
@@ -1303,8 +1286,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
req->request.actual = 0;
req->request.status = -EINPROGRESS;
- req->direction = dep->direction;
- req->epnum = dep->number;
trace_dwc3_ep_queue(req);
@@ -1319,28 +1300,18 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* errors which will force us issue EndTransfer command.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
- if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- } else {
- u32 cur_uf;
+ if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
+ !(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ return 0;
- cur_uf = __dwc3_gadget_get_frame(dwc);
- __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
- dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ __dwc3_gadget_start_isoc(dep);
+ return 0;
}
- return 0;
}
-
- if ((dep->flags & DWC3_EP_BUSY) &&
- !(dep->flags & DWC3_EP_MISSED_ISOC))
- goto out;
-
- return 0;
}
-out:
return __dwc3_gadget_kick_transfer(dep);
}
@@ -1390,7 +1361,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
if (r == req) {
/* wait until it is processed */
- dwc3_stop_active_transfer(dwc, dep->number, true);
+ dwc3_stop_active_transfer(dep, true);
/*
* If request was already started, this means we had to
@@ -1463,7 +1434,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
out1:
/* giveback the request */
- dep->queued_requests--;
+
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
@@ -1878,14 +1849,14 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err0;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -2082,113 +2053,142 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
/* -------------------------------------------------------------------------- */
-static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
+static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
{
- struct dwc3_ep *dep;
- u8 epnum;
+ struct dwc3 *dwc = dep->dwc;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
+ dep->endpoint.maxburst = 1;
+ dep->endpoint.ops = &dwc3_gadget_ep0_ops;
+ if (!dep->direction)
+ dwc->gadget.ep0 = &dep->endpoint;
- for (epnum = 0; epnum < total; epnum++) {
- bool direction = epnum & 1;
- u8 num = epnum >> 1;
+ dep->endpoint.caps.type_control = true;
- dep = kzalloc(sizeof(*dep), GFP_KERNEL);
- if (!dep)
- return -ENOMEM;
+ return 0;
+}
- dep->dwc = dwc;
- dep->number = epnum;
- dep->direction = direction;
- dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
- dwc->eps[epnum] = dep;
+static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int mdwidth;
+ int kbytes;
+ int size;
- snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
- direction ? "in" : "out");
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth /= 8;
- dep->endpoint.name = dep->name;
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ else
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
- if (!(dep->number > 1)) {
- dep->endpoint.desc = &dwc3_gadget_ep0_desc;
- dep->endpoint.comp_desc = NULL;
- }
+ /* FIFO Depth is in MDWDITH bytes. Multiply */
+ size *= mdwidth;
- spin_lock_init(&dep->lock);
-
- if (num == 0) {
- usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
- dep->endpoint.maxburst = 1;
- dep->endpoint.ops = &dwc3_gadget_ep0_ops;
- if (!direction)
- dwc->gadget.ep0 = &dep->endpoint;
- } else if (direction) {
- int mdwidth;
- int kbytes;
- int size;
- int ret;
-
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
- /* MDWIDTH is represented in bits, we need it in bytes */
- mdwidth /= 8;
-
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GTXFIFOSIZ_TXFDEF(size);
- else
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ kbytes = size / 1024;
+ if (kbytes == 0)
+ kbytes = 1;
- /* FIFO Depth is in MDWDITH bytes. Multiply */
- size *= mdwidth;
+ /*
+ * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
+ * internal overhead. We don't really know how these are used,
+ * but documentation say it exists.
+ */
+ size -= mdwidth * (kbytes + 1);
+ size /= kbytes;
- kbytes = size / 1024;
- if (kbytes == 0)
- kbytes = 1;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- /*
- * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
- * internal overhead. We don't really know how these are used,
- * but documentation say it exists.
- */
- size -= mdwidth * (kbytes + 1);
- size /= kbytes;
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
- usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+ return dwc3_alloc_trb_pool(dep);
+}
- dep->endpoint.max_streams = 15;
- dep->endpoint.ops = &dwc3_gadget_ep_ops;
- list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
- ret = dwc3_alloc_trb_pool(dep);
- if (ret)
- return ret;
- } else {
- int ret;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
- usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
- dep->endpoint.max_streams = 15;
- dep->endpoint.ops = &dwc3_gadget_ep_ops;
- list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ return dwc3_alloc_trb_pool(dep);
+}
- ret = dwc3_alloc_trb_pool(dep);
- if (ret)
- return ret;
- }
+static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_ep *dep;
+ bool direction = epnum & 1;
+ int ret;
+ u8 num = epnum >> 1;
- if (num == 0) {
- dep->endpoint.caps.type_control = true;
- } else {
- dep->endpoint.caps.type_iso = true;
- dep->endpoint.caps.type_bulk = true;
- dep->endpoint.caps.type_int = true;
- }
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ if (!dep)
+ return -ENOMEM;
- dep->endpoint.caps.dir_in = direction;
- dep->endpoint.caps.dir_out = !direction;
+ dep->dwc = dwc;
+ dep->number = epnum;
+ dep->direction = direction;
+ dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
+ dwc->eps[epnum] = dep;
- INIT_LIST_HEAD(&dep->pending_list);
- INIT_LIST_HEAD(&dep->started_list);
+ snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
+ direction ? "in" : "out");
+
+ dep->endpoint.name = dep->name;
+
+ if (!(dep->number > 1)) {
+ dep->endpoint.desc = &dwc3_gadget_ep0_desc;
+ dep->endpoint.comp_desc = NULL;
+ }
+
+ spin_lock_init(&dep->lock);
+
+ if (num == 0)
+ ret = dwc3_gadget_init_control_endpoint(dep);
+ else if (direction)
+ ret = dwc3_gadget_init_in_endpoint(dep);
+ else
+ ret = dwc3_gadget_init_out_endpoint(dep);
+
+ if (ret)
+ return ret;
+
+ dep->endpoint.caps.dir_in = direction;
+ dep->endpoint.caps.dir_out = !direction;
+
+ INIT_LIST_HEAD(&dep->pending_list);
+ INIT_LIST_HEAD(&dep->started_list);
+
+ return 0;
+}
+
+static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
+{
+ u8 epnum;
+
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
+
+ for (epnum = 0; epnum < total; epnum++) {
+ int ret;
+
+ ret = dwc3_gadget_init_endpoint(dwc, epnum);
+ if (ret)
+ return ret;
}
return 0;
@@ -2223,20 +2223,14 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
/* -------------------------------------------------------------------------- */
-static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
+static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
struct dwc3_request *req, struct dwc3_trb *trb,
- const struct dwc3_event_depevt *event, int status,
- int chain)
+ const struct dwc3_event_depevt *event, int status, int chain)
{
unsigned int count;
- unsigned int s_pkt = 0;
- unsigned int trb_status;
dwc3_ep_inc_deq(dep);
- if (req->trb == trb)
- dep->queued_requests--;
-
trace_dwc3_complete_trb(dep, trb);
/*
@@ -2268,159 +2262,140 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
return 1;
- if (dep->direction) {
- if (count) {
- trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
- /*
- * If missed isoc occurred and there is
- * no request queued then issue END
- * TRANSFER, so that core generates
- * next xfernotready and we will issue
- * a fresh START TRANSFER.
- * If there are still queued request
- * then wait, do not issue either END
- * or UPDATE TRANSFER, just attach next
- * request in pending_list during
- * giveback.If any future queued request
- * is successfully transferred then we
- * will issue UPDATE TRANSFER for all
- * request in the pending_list.
- */
- dep->flags |= DWC3_EP_MISSED_ISOC;
- } else {
- dev_err(dwc->dev, "incomplete IN transfer %s\n",
- dep->name);
- status = -ECONNRESET;
- }
- } else {
- dep->flags &= ~DWC3_EP_MISSED_ISOC;
- }
- } else {
- if (count && (event->status & DEPEVT_STATUS_SHORT))
- s_pkt = 1;
- }
-
- if (s_pkt && !chain)
+ if (event->status & DEPEVT_STATUS_SHORT && !chain)
return 1;
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ if (event->status & DEPEVT_STATUS_IOC)
return 1;
return 0;
}
-static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event, int status)
+static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req, const struct dwc3_event_depevt *event,
+ int status)
{
- struct dwc3_request *req, *n;
- struct dwc3_trb *trb;
- bool ioc = false;
- int ret = 0;
+ struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
+ struct scatterlist *sg = req->sg;
+ struct scatterlist *s;
+ unsigned int pending = req->num_pending_sgs;
+ unsigned int i;
+ int ret = 0;
- list_for_each_entry_safe(req, n, &dep->started_list, list) {
- unsigned length;
- int chain;
+ for_each_sg(sg, s, pending, i) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
- length = req->request.length;
- chain = req->num_pending_sgs > 0;
- if (chain) {
- struct scatterlist *sg = req->sg;
- struct scatterlist *s;
- unsigned int pending = req->num_pending_sgs;
- unsigned int i;
-
- for_each_sg(sg, s, pending, i) {
- trb = &dep->trb_pool[dep->trb_dequeue];
-
- if (trb->ctrl & DWC3_TRB_CTRL_HWO)
- break;
-
- req->sg = sg_next(s);
- req->num_pending_sgs--;
-
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status, chain);
- if (ret)
- break;
- }
- } else {
- trb = &dep->trb_pool[dep->trb_dequeue];
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status, chain);
- }
+ if (trb->ctrl & DWC3_TRB_CTRL_HWO)
+ break;
- if (req->unaligned || req->zero) {
- trb = &dep->trb_pool[dep->trb_dequeue];
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status, false);
- req->unaligned = false;
- req->zero = false;
- }
+ req->sg = sg_next(s);
+ req->num_pending_sgs--;
- req->request.actual = length - req->remaining;
+ ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req,
+ trb, event, status, true);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
- if ((req->request.actual < length) && req->num_pending_sgs)
- return __dwc3_gadget_kick_transfer(dep);
+static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
+ struct dwc3_request *req, const struct dwc3_event_depevt *event,
+ int status)
+{
+ struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
- dwc3_gadget_giveback(dep, req, status);
+ return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb,
+ event, status, false);
+}
- if (ret) {
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- ioc = true;
- break;
- }
+static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
+{
+ return req->request.actual == req->request.length;
+}
+
+static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event,
+ struct dwc3_request *req, int status)
+{
+ int ret;
+
+ if (req->num_pending_sgs)
+ ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event,
+ status);
+ else
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
+ status);
+
+ if (req->unaligned || req->zero) {
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
+ status);
+ req->unaligned = false;
+ req->zero = false;
}
- /*
- * Our endpoint might get disabled by another thread during
- * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
- * early on so DWC3_EP_BUSY flag gets cleared
- */
- if (!dep->endpoint.desc)
- return 1;
+ req->request.actual = req->request.length - req->remaining;
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- list_empty(&dep->started_list)) {
- if (list_empty(&dep->pending_list)) {
- /*
- * If there is no entry in request list then do
- * not issue END TRANSFER now. Just set PENDING
- * flag, so that END TRANSFER is issued when an
- * entry is added into request list.
- */
- dep->flags = DWC3_EP_PENDING_REQUEST;
- } else {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- }
- return 1;
+ if (!dwc3_gadget_ep_request_completed(req) &&
+ req->num_pending_sgs) {
+ __dwc3_gadget_kick_transfer(dep);
+ goto out;
}
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && ioc)
- return 0;
+ dwc3_gadget_giveback(dep, req, status);
+
+out:
+ return ret;
+}
+
+static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
+{
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+ int ret;
+
+ ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
+ req, status);
+ if (ret)
+ break;
+ }
+}
- return 1;
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ dep->frame_number = event->parameters;
}
-static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
{
+ struct dwc3 *dwc = dep->dwc;
unsigned status = 0;
- int clean_busy;
- u32 is_xfer_complete;
+ bool stop = false;
- is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
- clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
- if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
- usb_endpoint_xfer_isoc(dep->endpoint.desc)))
- dep->flags &= ~DWC3_EP_BUSY;
+ if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
+ status = -EXDEV;
+
+ if (list_empty(&dep->started_list))
+ stop = true;
+ }
+
+ dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
+
+ if (stop) {
+ dwc3_stop_active_transfer(dep, true);
+ dep->flags = DWC3_EP_ENABLED;
+ }
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
@@ -2446,17 +2421,13 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dwc->u1u2 = 0;
}
+}
- /*
- * Our endpoint might get disabled by another thread during
- * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
- * early on so DWC3_EP_BUSY flag gets cleared
- */
- if (!dep->endpoint.desc)
- return;
-
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc))
- __dwc3_gadget_kick_transfer(dep);
+static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+ __dwc3_gadget_start_isoc(dep);
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -2483,32 +2454,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
}
switch (event->endpoint_event) {
- case DWC3_DEPEVT_XFERCOMPLETE:
- dep->resource_index = 0;
-
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
- return;
- }
-
- dwc3_endpoint_transfer_complete(dwc, dep, event);
- break;
case DWC3_DEPEVT_XFERINPROGRESS:
- dwc3_endpoint_transfer_complete(dwc, dep, event);
+ dwc3_gadget_endpoint_transfer_in_progress(dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dwc3_gadget_start_isoc(dwc, dep, event);
- else
- __dwc3_gadget_kick_transfer(dep);
-
- break;
- case DWC3_DEPEVT_STREAMEVT:
- if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
- dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
- dep->name);
- return;
- }
+ dwc3_gadget_endpoint_transfer_not_ready(dep, event);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
@@ -2518,6 +2468,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
wake_up(&dep->wait_end_transfer);
}
break;
+ case DWC3_DEPEVT_STREAMEVT:
+ case DWC3_DEPEVT_XFERCOMPLETE:
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
}
@@ -2562,15 +2514,13 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
{
- struct dwc3_ep *dep;
+ struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
- dep = dwc->eps[epnum];
-
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
!dep->resource_index)
return;
@@ -2614,7 +2564,6 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
- dep->flags &= ~DWC3_EP_BUSY;
if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) {
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
@@ -2816,14 +2765,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, true, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, true, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 578aa856f986..db610c56f1d6 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -98,13 +98,12 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
* Caller should take care of locking. Returns the transfer resource
* index for a given endpoint.
*/
-static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
+static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
u32 res_id;
res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
-
- return DWC3_DEPCMD_GET_RSC_IDX(res_id);
+ dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 22/64] usb: dwc3: import from kernel v4.19
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (20 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 21/64] usb: dwc3: import from kernel v4.18 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 23/64] usb: dwc3: import from kernel v4.20 Jens Wiklander
` (45 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.18 to v4.19.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.18..v4.19
Commits imported:
bfa150f37f80 Merge tag 'fixes-for-v4.19-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus
4e3121abcf53 usb/dwc3/gadget: fix kernel-doc parameter warning
b497fff6f59e usb: dwc3: pci: Fix return value check in dwc3_byt_enable_ulpi_refclock()
66174b6998a6 usb: dwc3: of-simple: avoid unused function warnings
45dd7af410b7 Merge tag 'usb-for-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
4ea438da76f4 usb: dwc3: gadget: Check MaxPacketSize from descriptor
b138e23d3dff usb: dwc3: core: Enable AutoRetry feature in the controller
a77004681148 usb: dwc3: Set default mode for dwc_usb31
c31d983beaf0 usb: dwc3: pci: Intel Merrifield can be host
1a7b12f69a94 usb: dwc3: pci: Supply device properties via driver data
d9612c2f0449 usb: dwc3: Enable undefined length INCR burst type
d635db5508b0 usb: dwc3: add global soc bus configuration reg0
87d852de94d6 usb: dwc3: Describe 'wakeup_work' field of struct dwc3_pci
9a7faac36502 usb: dwc3: change stream event enable bit back to 13
76251db86561 usb: dwc3: of-simple: reset host controller at suspend/resume
211f658b7b40 usb: dwc3: pci: Use devm functions to get the phy GPIOs
7740d04d901d usb: dwc3: pci: Enable ULPI Refclk on platforms where the firmware does not
5741022cbdf3 usb: dwc3: pci: Add GPIO lookup table on platforms without ACPI GPIO resources
3fe314ca8c97 usb: dwc3: Add a glue driver for Synopsys HAPS platform
db9fc500e85a usb: dwc3: gadget: remove redundant variable maxpacket
714c95ce8bad Merge 4.18-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 118 ++++++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/core.h | 17 ++++++
drivers/usb/dwc3/gadget.c | 3 +-
drivers/usb/dwc3/gadget.h | 2 +-
4 files changed, 137 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 103807587dc6..88c80fcc39f5 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -78,6 +78,14 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
mode = USB_DR_MODE_PERIPHERAL;
+
+ /*
+ * dwc_usb31 does not support OTG mode. If the controller
+ * supports DRD but the dr_mode is not specified or set to OTG,
+ * then set the mode to peripheral.
+ */
+ if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc))
+ mode = USB_DR_MODE_PERIPHERAL;
}
if (mode != dwc->dr_mode) {
@@ -778,6 +786,98 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
static int dwc3_core_get_phy(struct dwc3 *dwc);
static int dwc3_core_ulpi_init(struct dwc3 *dwc);
+/* set global incr burst type configuration registers */
+static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+ /* incrx_mode : for INCR burst type. */
+ bool incrx_mode;
+ /* incrx_size : for size of INCRX burst. */
+ u32 incrx_size;
+ u32 *vals;
+ u32 cfg;
+ int ntype;
+ int ret;
+ int i;
+
+ cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+
+ /*
+ * Handle property "snps,incr-burst-type-adjustment".
+ * Get the number of value from this property:
+ * result <= 0, means this property is not supported.
+ * result = 1, means INCRx burst mode supported.
+ * result > 1, means undefined length burst mode supported.
+ */
+ ntype = device_property_read_u32_array(dev,
+ "snps,incr-burst-type-adjustment", NULL, 0);
+ if (ntype <= 0)
+ return;
+
+ vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL);
+ if (!vals) {
+ dev_err(dev, "Error to get memory\n");
+ return;
+ }
+
+ /* Get INCR burst type, and parse it */
+ ret = device_property_read_u32_array(dev,
+ "snps,incr-burst-type-adjustment", vals, ntype);
+ if (ret) {
+ dev_err(dev, "Error to get property\n");
+ return;
+ }
+
+ incrx_size = *vals;
+
+ if (ntype > 1) {
+ /* INCRX (undefined length) burst mode */
+ incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE;
+ for (i = 1; i < ntype; i++) {
+ if (vals[i] > incrx_size)
+ incrx_size = vals[i];
+ }
+ } else {
+ /* INCRX burst mode */
+ incrx_mode = INCRX_BURST_MODE;
+ }
+
+ /* Enable Undefined Length INCR Burst and Enable INCRx Burst */
+ cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK;
+ if (incrx_mode)
+ cfg |= DWC3_GSBUSCFG0_INCRBRSTENA;
+ switch (incrx_size) {
+ case 256:
+ cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA;
+ break;
+ case 128:
+ cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA;
+ break;
+ case 64:
+ cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA;
+ break;
+ case 32:
+ cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA;
+ break;
+ case 16:
+ cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA;
+ break;
+ case 8:
+ cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA;
+ break;
+ case 4:
+ cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA;
+ break;
+ case 1:
+ break;
+ default:
+ dev_err(dev, "Invalid property\n");
+ break;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
+}
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -840,6 +940,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc);
+ dwc3_set_incr_burst_type(dwc);
+
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
@@ -883,6 +985,22 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ if (dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+
+ /*
+ * Enable Auto retry Feature to make the controller operating in
+ * Host mode on seeing transaction errors(CRC errors or internal
+ * overrun scenerios) on IN transfers to reply to the device
+ * with a non-terminating retry ACK (i.e, an ACK transcation
+ * packet with Retry=1 & Nump != 0)
+ */
+ reg |= DWC3_GUCTL_HSTINAUTORETRY;
+
+ dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+ }
+
/*
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 285ce0ef3b91..5bfb62533e0f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -163,6 +163,17 @@
/* Bit fields */
+/* Global SoC Bus Configuration INCRx Register 0 */
+#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
+#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
+#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
+#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */
+#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */
+#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */
+#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */
+#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
+#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+
/* Global Debug Queue/FIFO Space Available Register */
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
@@ -227,6 +238,9 @@
#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
+/* Global User Control Register */
+#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
+
/* Global User Control 1 Register */
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
@@ -1157,6 +1171,9 @@ struct dwc3 {
u16 imod_interval;
};
+#define INCRX_BURST_MODE 0
+#define INCRX_UNDEF_LENGTH_BURST_MODE 1
+
#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
/* -------------------------------------------------------------------------- */
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 69bf137aab37..2b53194081ba 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -473,7 +473,6 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
/**
* dwc3_gadget_start_config - configure ep resources
- * @dwc: pointer to our controller context structure
* @dep: endpoint that is being enabled
*
* Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's
@@ -1121,7 +1120,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->request.short_not_ok,
req->request.no_interrupt);
} else if (req->request.zero && req->request.length &&
- (IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) {
+ (IS_ALIGNED(req->request.length, maxp))) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index db610c56f1d6..2aacd1afd9ff 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -25,7 +25,7 @@ struct dwc3;
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
-#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(12)
+#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13)
#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 23/64] usb: dwc3: import from kernel v4.20
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (21 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 22/64] usb: dwc3: import from kernel v4.19 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 24/64] usb: dwc3: import from kernel v5.0 Jens Wiklander
` (44 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.19 to v4.20.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.19..v4.20
Commits imported:
38317f5c0f2f Revert "usb: dwc3: gadget: skip Set/Clear Halt when invalid"
2fc6d4be35fb usb: dwc3: gadget: fix ISOC TRB type on unaligned transfers
7b412b04a0c7 usb: dwc3: Fix NULL pointer exception in dwc3_pci_remove()
ba3a51ac32eb usb: dwc3: gadget: Properly check last unaligned/zero chain TRB
08fd9a82fda8 usb: dwc3: core: Clean up ULPI device
6af19fd10595 usb: dwc3: Fix spelling of 'optimizations'
87dd96111b0b usb: dwc3: gadget: Check ENBLSLPM before sending ep command
3def4031b3e3 usb: dwc3: add EXTCON dependency for qcom
4c19cc14064d usb: dwc3: exynos: Add support for Exynos5433 variant with all clocks
9f2168367a0a usb: dwc3: exynos: Rework clock handling and prepare for new variants
1e041b6f313a usb: dwc3: exynos: Remove dead code
755801cb9feb Merge tag 'uvcg-20180925' of git://linuxtv.org/pinchartl/media into testing/next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 3 ++-
drivers/usb/dwc3/gadget.c | 42 +++++++++++++++++++++------------------
2 files changed, 25 insertions(+), 20 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 88c80fcc39f5..2f2048aa5fde 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -756,7 +756,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dev_info(dwc->dev, "Running with FPGA optmizations\n");
+ dev_info(dwc->dev, "Running with FPGA optimizations\n");
dwc->is_fpga = true;
}
@@ -1499,6 +1499,7 @@ static int dwc3_probe(struct platform_device *pdev)
err5:
dwc3_event_buffers_cleanup(dwc);
+ dwc3_ulpi_exit(dwc);
err4:
dwc3_free_scratch_buffers(dwc);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2b53194081ba..9f92ee03dde7 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -270,27 +270,36 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 1000;
+ u32 saved_config = 0;
u32 reg;
int cmd_status = 0;
- int susphy = false;
int ret = -EINVAL;
/*
- * Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
- * we're issuing an endpoint command, we must check if
- * GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
+ * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
+ * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
+ * endpoint command.
*
- * We will also set SUSPHY bit to what it was before returning as stated
- * by the same section on Synopsys databook.
+ * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
+ * settings. Restore them after the command is completed.
+ *
+ * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
if (dwc->gadget.speed <= USB_SPEED_HIGH) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
- susphy = true;
+ saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
+
+ if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
+ saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ }
+
+ if (saved_config)
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
@@ -389,9 +398,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
}
}
- if (unlikely(susphy)) {
+ if (saved_config) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ reg |= saved_config;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
@@ -1072,7 +1081,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
- maxp - rem, false, 0,
+ maxp - rem, false, 1,
req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
@@ -1116,7 +1125,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
- false, 0, req->request.stream_id,
+ false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else if (req->request.zero && req->request.length &&
@@ -1132,7 +1141,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
/* Now prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- false, 0, req->request.stream_id,
+ false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else {
@@ -1461,9 +1470,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
unsigned transfer_in_flight;
unsigned started;
- if (dep->flags & DWC3_EP_STALL)
- return 0;
-
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
else
@@ -1485,8 +1491,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
- if (!(dep->flags & DWC3_EP_STALL))
- return 0;
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
@@ -2250,7 +2254,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
- if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+ if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 24/64] usb: dwc3: import from kernel v5.0
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (22 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 23/64] usb: dwc3: import from kernel v4.20 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 25/64] usb: dwc3: import from kernel v5.1 Jens Wiklander
` (43 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v4.20 to v5.0.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v4.20..v5.0
Commits imported:
512e6fb589bc usb: dwc3: exynos: Fix error handling of clk_prepare_enable
1e19cdc80602 usb: dwc3: gadget: Handle 0 xfer length for OUT EP
88b1bb1f3b88 usb: dwc3: gadget: Fix the uninitialized link_state when udc starts
bd6742249b9c usb: dwc3: gadget: Clear req->needs_extra_trb flag on cleanup
01c10880d242 usb: dwc3: gadget: synchronize_irq dwc irq in suspend
926b02d3eb54 Merge tag 'pci-v4.21-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
b6061b1e566d PCI: Move Synopsys HAPS platform device IDs
5ac93d0c5d8c Merge tag 'usb-for-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
244add8ebfb2 usb: dwc3: gadget: Disable CSP for stream OUT ep
3004cfd62049 Revert "usb: dwc3: pci: Use devm functions to get the phy GPIOs"
54d48183d21e usb: dwc3: trace: add missing break statement to make compiler happy
6abfa0f5bb7c usb: dwc3: gadget: Report isoc transfer frame number
b7a4fbe2300a usb: dwc3: Correct the logic for checking TRB full in __dwc3_prepare_one_trb()
26d62b4d10ad usb: dwc3: don't issue no-op trb for stream capable endpoints
a7351807bd8b usb: dwc3: update stream id in depcmd
44c94100b6fa Merge 4.20-rc5 into usb-next
d53701067f04 usb: dwc3: gadget: check if dep->frame_number is still valid
3451f6affaef usb: dwc3: gadget: remove unnecessary dev_info()
1517265228b4 usb: dwc3: trace: log ep commands in hex
25abad6a0584 usb: dwc3: gadget: return errors from __dwc3_gadget_start_isoc()
fec9095bdef4 usb: dwc3: gadget: remove wait_end_transfer
d4f1afe5e896 usb: dwc3: gadget: move requests to cancelled_list
d5443bbf5fc8 usb: dwc3: gadget: introduce cancelled_list
7746a8dfb3f9 usb: dwc3: gadget: extract dwc3_gadget_ep_skip_trbs()
c3acd5901414 usb: dwc3: gadget: use num_trbs when skipping TRBs on ->dequeue()
09fe1f8d7e2f usb: dwc3: gadget: track number of TRBs per request
1a22ec643580 usb: dwc3: gadget: combine unaligned and zero flags
d92021f66063 usb: dwc3: Add workaround for isoc start transfer failure
475d8e0197f1 usb: dwc3: Track DWC_usb31 VERSIONTYPE
89a9cc47513e usb: dwc3: Set default mode for DWC_usb3 v3.30a and higher
268784ba14a7 usb: dwc3: drd: Add support for DR detection through extcon
85383756ae34 usb: dwc3: drd: Switch to device property for 'extcon' handling
408d3ba006af usb: dwc3: don't log probe deferrals; but do log other error codes
022a0208c0ff usb: dwc3: Support option to disable USB2 LPM
eafeacf11964 usb: dwc3: Set GUSB2PHYCFG.ENBLSLPM
d102444cac15 usb: dwc3: debugfs: Print/set link state for peripheral mode
0d36dede4578 usb: dwc3: debugfs: Properly print/set link state for HS
62ba09d6bb63 usb: dwc3: debugfs: Dump internal LSP and ep registers
0f874f79dc81 usb: dwc3: debugfs: Print eps Tx/RxFIFO in bytes
2c85a1817e4b usb: dwc3: debugfs: Properly name Tx/RxFIFO
a62d2d44902c Merge 4.20-rc4 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 19 ++-
drivers/usb/dwc3/core.h | 60 ++++++-
drivers/usb/dwc3/debug.h | 29 ++++
drivers/usb/dwc3/gadget.c | 348 +++++++++++++++++++++++++-------------
drivers/usb/dwc3/gadget.h | 15 ++
5 files changed, 336 insertions(+), 135 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2f2048aa5fde..a1b126f90261 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -80,11 +80,12 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
mode = USB_DR_MODE_PERIPHERAL;
/*
- * dwc_usb31 does not support OTG mode. If the controller
- * supports DRD but the dr_mode is not specified or set to OTG,
- * then set the mode to peripheral.
+ * DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG
+ * mode. If the controller supports DRD but the dr_mode is not
+ * specified or set to OTG, then set the mode to peripheral.
*/
- if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc))
+ if (mode == USB_DR_MODE_OTG &&
+ dwc->revision >= DWC3_REVISION_330A)
mode = USB_DR_MODE_PERIPHERAL;
}
@@ -661,6 +662,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ else
+ reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
if (dwc->dis_u2_freeclk_exists_quirk)
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
@@ -702,6 +705,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
/* Detected DWC_usb31 IP */
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
dwc->revision |= DWC3_REVISION_IS_DWC31;
+ dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
} else {
return false;
}
@@ -1244,8 +1248,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,is-utmi-l1-suspend");
device_property_read_u8(dev, "snps,hird-threshold",
&hird_threshold);
+ dwc->dis_start_transfer_quirk = device_property_read_bool(dev,
+ "snps,dis-start-transfer-quirk");
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
+ dwc->usb2_lpm_disable = device_property_read_bool(dev,
+ "snps,usb2-lpm-disable");
device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
&rx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,rx-max-burst-prd",
@@ -1482,7 +1490,8 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize core\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize core: %d\n", ret);
goto err4;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5bfb62533e0f..df876418cb78 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -37,6 +37,7 @@
#define DWC3_EP0_SETUP_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
+#define DWC3_ISOC_MAX_RETRIES 5
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
#define DWC3_EVENT_BUFFERS_SIZE 4096
@@ -174,13 +175,19 @@
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+/* Global Debug LSP MUX Select */
+#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
+#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
+#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4)
+#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf)
+
/* Global Debug Queue/FIFO Space Available Register */
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
-#define DWC3_TXFIFOQ 0
-#define DWC3_RXFIFOQ 1
+#define DWC3_TXFIFO 0
+#define DWC3_RXFIFO 1
#define DWC3_TXREQQ 2
#define DWC3_RXREQQ 3
#define DWC3_RXINFOQ 4
@@ -253,6 +260,9 @@
#define DWC3_GSTS_DEVICE_IP BIT(6)
#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
+#define DWC3_GSTS_CURMOD(n) ((n) & 0x3)
+#define DWC3_GSTS_CURMOD_DEVICE 0
+#define DWC3_GSTS_CURMOD_HOST 1
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
@@ -321,6 +331,7 @@
#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+#define DWC3_GHWPARAMS1_ENDBC BIT(31)
/* Global HWPARAMS3 Register */
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
@@ -636,9 +647,9 @@ struct dwc3_event_buffer {
/**
* struct dwc3_ep - device side endpoint representation
* @endpoint: usb endpoint
+ * @cancelled_list: list of cancelled requests for this endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
- * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
* @lock: spinlock for endpoint request queue traversal
* @regs: pointer to first endpoint register
* @trb_pool: array of transaction buffers
@@ -656,14 +667,17 @@ struct dwc3_event_buffer {
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
* @stream_capable: true when streams are enabled
+ * @combo_num: the test combination BIT[15:14] of the frame number to test
+ * isochronous START TRANSFER command failure workaround
+ * @start_cmd_status: the status of testing START TRANSFER command with
+ * combo_num = 'b00
*/
struct dwc3_ep {
struct usb_ep endpoint;
+ struct list_head cancelled_list;
struct list_head pending_list;
struct list_head started_list;
- wait_queue_head_t wait_end_transfer;
-
spinlock_t lock;
void __iomem *regs;
@@ -705,6 +719,10 @@ struct dwc3_ep {
unsigned direction:1;
unsigned stream_capable:1;
+
+ /* For isochronous START TRANSFER workaround only */
+ u8 combo_num;
+ int start_cmd_status;
};
enum dwc3_phy {
@@ -766,6 +784,7 @@ enum dwc3_link_state {
#define DWC3_TRB_CTRL_ISP_IMI BIT(10)
#define DWC3_TRB_CTRL_IOC BIT(11)
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
+#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14)
#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
@@ -847,11 +866,12 @@ struct dwc3_hwparams {
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
- * @unaligned: true for OUT endpoints with length not divisible by maxp
+ * @num_trbs: number of TRBs used by this request
+ * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
+ * or unaligned OUT)
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
* @started: request is started
- * @zero: wants a ZLP
*/
struct dwc3_request {
struct usb_request request;
@@ -867,11 +887,12 @@ struct dwc3_request {
struct dwc3_trb *trb;
dma_addr_t trb_dma;
- unsigned unaligned:1;
+ unsigned num_trbs;
+
+ unsigned needs_extra_trb:1;
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
- unsigned zero:1;
};
/*
@@ -918,6 +939,7 @@ struct dwc3_scratchpad_array {
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
+ * @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
* @current_dr_role: current role of operation when in dual-role mode
* @desired_dr_role: desired role of operation when in dual-role mode
@@ -945,6 +967,7 @@ struct dwc3_scratchpad_array {
* @hwparams: copy of hwparams registers
* @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
+ * @dbg_lsp_select: current debug lsp mux register selection
* @test_mode: true when we're entering a USB test mode
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
@@ -970,7 +993,10 @@ struct dwc3_scratchpad_array {
* @pullups_connected: true when Run/Stop bit is set
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @three_stage_setup: set if we perform a three phase setup
+ * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
+ * not needed for DWC_usb31 version 1.70a-ea06 and below
* @usb3_lpm_capable: set if hadrware supports Link Power Management
+ * @usb2_lpm_disable: set to disable usb2 lpm
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -1095,6 +1121,7 @@ struct dwc3 {
#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
+#define DWC3_REVISION_330A 0x5533330a
/*
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
@@ -1103,6 +1130,17 @@ struct dwc3 {
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
+
+ u32 version_type;
+
+#define DWC31_VERSIONTYPE_EA01 0x65613031
+#define DWC31_VERSIONTYPE_EA02 0x65613032
+#define DWC31_VERSIONTYPE_EA03 0x65613033
+#define DWC31_VERSIONTYPE_EA04 0x65613034
+#define DWC31_VERSIONTYPE_EA05 0x65613035
+#define DWC31_VERSIONTYPE_EA06 0x65613036
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -1121,6 +1159,8 @@ struct dwc3 {
struct dentry *root;
struct debugfs_regset32 *regset;
+ u32 dbg_lsp_select;
+
u8 test_mode;
u8 test_mode_nr;
u8 lpm_nyet_threshold;
@@ -1145,7 +1185,9 @@ struct dwc3 {
unsigned pullups_connected:1;
unsigned setup_packet_pending:1;
unsigned three_stage_setup:1;
+ unsigned dis_start_transfer_quirk:1;
unsigned usb3_lpm_capable:1;
+ unsigned usb2_lpm_disable:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index c66d216dcc30..4f75ab3505b7 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -116,6 +116,35 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
}
+/**
+ * dwc3_gadget_hs_link_string - returns highspeed and below link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
+{
+ switch (link_state) {
+ case DWC3_LINK_STATE_U0:
+ return "On";
+ case DWC3_LINK_STATE_U2:
+ return "Sleep";
+ case DWC3_LINK_STATE_U3:
+ return "Suspend";
+ case DWC3_LINK_STATE_SS_DIS:
+ return "Disconnected";
+ case DWC3_LINK_STATE_RX_DET:
+ return "Early Suspend";
+ case DWC3_LINK_STATE_RECOV:
+ return "Recovery";
+ case DWC3_LINK_STATE_RESET:
+ return "Reset";
+ case DWC3_LINK_STATE_RESUME:
+ return "Resume";
+ default:
+ return "UNKNOWN link state\n";
+ }
+}
+
/**
* dwc3_trb_type_string - returns TRB type as a string
* @type: the type of the TRB
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 9f92ee03dde7..6c9b76bcc2e1 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -27,7 +27,7 @@
#include "gadget.h"
#include "io.h"
-#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
+#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
& ~((d)->interval - 1))
/**
@@ -177,6 +177,7 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
req->started = false;
list_del(&req->list);
req->remaining = 0;
+ req->needs_extra_trb = false;
if (req->request.status == -EINPROGRESS)
req->request.status = status;
@@ -647,8 +648,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
- init_waitqueue_head(&dep->wait_end_transfer);
-
if (usb_endpoint_xfer_control(desc))
goto out;
@@ -672,7 +671,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if (usb_endpoint_xfer_bulk(desc) ||
+ if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
@@ -919,8 +918,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
struct usb_gadget *gadget = &dwc->gadget;
enum usb_device_speed speed = gadget->speed;
- dwc3_ep_inc_enq(dep);
-
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
trb->bph = upper_32_bits(dma);
@@ -990,16 +987,20 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
usb_endpoint_type(dep->endpoint.desc));
}
- /* always enable Continue on Short Packet */
+ /*
+ * Enable Continue on Short Packet
+ * when endpoint is not a stream capable
+ */
if (usb_endpoint_dir_out(dep->endpoint.desc)) {
- trb->ctrl |= DWC3_TRB_CTRL_CSP;
+ if (!dep->stream_capable)
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
if (short_not_ok)
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
if ((!no_interrupt && !chain) ||
- (dwc3_calc_trbs_left(dep) == 0))
+ (dwc3_calc_trbs_left(dep) == 1))
trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
@@ -1010,6 +1011,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
trb->ctrl |= DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_enq(dep);
+
trace_dwc3_prepare_trb(dep, trb);
}
@@ -1046,6 +1049,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
}
+ req->num_trbs++;
+
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
stream_id, short_not_ok, no_interrupt);
}
@@ -1073,13 +1078,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- req->unaligned = true;
+ req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, i);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
maxp - rem, false, 1,
req->request.stream_id,
@@ -1113,17 +1119,18 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
- if (rem && usb_endpoint_dir_out(dep->endpoint.desc)) {
+ if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- req->unaligned = true;
+ req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
false, 1, req->request.stream_id,
req->request.short_not_ok,
@@ -1133,13 +1140,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- req->zero = true;
+ req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 1, req->request.stream_id,
req->request.short_not_ok,
@@ -1232,6 +1240,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
params.param1 = lower_32_bits(req->trb_dma);
cmd = DWC3_DEPCMD_STARTTRANSFER;
+ if (dep->stream_capable)
+ cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id);
+
if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
} else {
@@ -1263,17 +1274,151 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
return DWC3_DSTS_SOFFN(reg);
}
-static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
+/**
+ * dwc3_gadget_start_isoc_quirk - workaround invalid frame number
+ * @dep: isoc endpoint
+ *
+ * This function tests for the correct combination of BIT[15:14] from the 16-bit
+ * microframe number reported by the XferNotReady event for the future frame
+ * number to start the isoc transfer.
+ *
+ * In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed
+ * isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the
+ * XferNotReady event are invalid. The driver uses this number to schedule the
+ * isochronous transfer and passes it to the START TRANSFER command. Because
+ * this number is invalid, the command may fail. If BIT[15:14] matches the
+ * internal 16-bit microframe, the START TRANSFER command will pass and the
+ * transfer will start at the scheduled time, if it is off by 1, the command
+ * will still pass, but the transfer will start 2 seconds in the future. For all
+ * other conditions, the START TRANSFER command will fail with bus-expiry.
+ *
+ * In order to workaround this issue, we can test for the correct combination of
+ * BIT[15:14] by sending START TRANSFER commands with different values of
+ * BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart
+ * (or 2 seconds). 4 seconds into the future will result in a bus-expiry status.
+ * As the result, within the 4 possible combinations for BIT[15:14], there will
+ * be 2 successful and 2 failure START COMMAND status. One of the 2 successful
+ * command status will result in a 2-second delay start. The smaller BIT[15:14]
+ * value is the correct combination.
+ *
+ * Since there are only 4 outcomes and the results are ordered, we can simply
+ * test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to
+ * deduce the smaller successful combination.
+ *
+ * Let test0 = test status for combination 'b00 and test1 = test status for 'b01
+ * of BIT[15:14]. The correct combination is as follow:
+ *
+ * if test0 fails and test1 passes, BIT[15:14] is 'b01
+ * if test0 fails and test1 fails, BIT[15:14] is 'b10
+ * if test0 passes and test1 fails, BIT[15:14] is 'b11
+ * if test0 passes and test1 passes, BIT[15:14] is 'b00
+ *
+ * Synopsys STAR 9001202023: Wrong microframe number for isochronous IN
+ * endpoints.
+ */
+static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
{
+ int cmd_status = 0;
+ bool test0;
+ bool test1;
+
+ while (dep->combo_num < 2) {
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 test_frame_number;
+ u32 cmd;
+
+ /*
+ * Check if we can start isoc transfer on the next interval or
+ * 4 uframes in the future with BIT[15:14] as dep->combo_num
+ */
+ test_frame_number = dep->frame_number & 0x3fff;
+ test_frame_number |= dep->combo_num << 14;
+ test_frame_number += max_t(u32, 4, dep->interval);
+
+ params.param0 = upper_32_bits(dep->dwc->bounce_addr);
+ params.param1 = lower_32_bits(dep->dwc->bounce_addr);
+
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+ cmd |= DWC3_DEPCMD_PARAM(test_frame_number);
+ cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+
+ /* Redo if some other failure beside bus-expiry is received */
+ if (cmd_status && cmd_status != -EAGAIN) {
+ dep->start_cmd_status = 0;
+ dep->combo_num = 0;
+ return 0;
+ }
+
+ /* Store the first test status */
+ if (dep->combo_num == 0)
+ dep->start_cmd_status = cmd_status;
+
+ dep->combo_num++;
+
+ /*
+ * End the transfer if the START_TRANSFER command is successful
+ * to wait for the next XferNotReady to test the command again
+ */
+ if (cmd_status == 0) {
+ dwc3_stop_active_transfer(dep, true);
+ return 0;
+ }
+ }
+
+ /* test0 and test1 are both completed at this point */
+ test0 = (dep->start_cmd_status == 0);
+ test1 = (cmd_status == 0);
+
+ if (!test0 && test1)
+ dep->combo_num = 1;
+ else if (!test0 && !test1)
+ dep->combo_num = 2;
+ else if (test0 && !test1)
+ dep->combo_num = 3;
+ else if (test0 && test1)
+ dep->combo_num = 0;
+
+ dep->frame_number &= 0x3fff;
+ dep->frame_number |= dep->combo_num << 14;
+ dep->frame_number += max_t(u32, 4, dep->interval);
+
+ /* Reinitialize test variables */
+ dep->start_cmd_status = 0;
+ dep->combo_num = 0;
+
+ return __dwc3_gadget_kick_transfer(dep);
+}
+
+static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int ret;
+ int i;
+
if (list_empty(&dep->pending_list)) {
- dev_info(dep->dwc->dev, "%s: ran out of requests\n",
- dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
- return;
+ return -EAGAIN;
}
- dep->frame_number = DWC3_ALIGN_FRAME(dep);
- __dwc3_gadget_kick_transfer(dep);
+ if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
+ (dwc->revision <= DWC3_USB31_REVISION_160A ||
+ (dwc->revision == DWC3_USB31_REVISION_170A &&
+ dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
+ dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
+
+ if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
+ return dwc3_gadget_start_isoc_quirk(dep);
+ }
+
+ for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
+ dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
+
+ ret = __dwc3_gadget_kick_transfer(dep);
+ if (ret != -EAGAIN)
+ break;
+ }
+
+ return ret;
}
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
@@ -1314,8 +1459,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
- __dwc3_gadget_start_isoc(dep);
- return 0;
+ return __dwc3_gadget_start_isoc(dep);
}
}
}
@@ -1341,6 +1485,40 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
return ret;
}
+static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+ int i;
+
+ /*
+ * If request was already started, this means we had to
+ * stop the transfer. With that we also need to ignore
+ * all TRBs used by the request, however TRBs can only
+ * be modified after completion of END_TRANSFER
+ * command. So what we do here is that we wait for
+ * END_TRANSFER completion and only after that, we jump
+ * over TRBs by clearing HWO and incrementing dequeue
+ * pointer.
+ */
+ for (i = 0; i < req->num_trbs; i++) {
+ struct dwc3_trb *trb;
+
+ trb = req->trb + i;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+}
+
+static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+
+ list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+ dwc3_gadget_ep_skip_trbs(dep, req);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ }
+}
+
static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
struct usb_request *request)
{
@@ -1371,68 +1549,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true);
- /*
- * If request was already started, this means we had to
- * stop the transfer. With that we also need to ignore
- * all TRBs used by the request, however TRBs can only
- * be modified after completion of END_TRANSFER
- * command. So what we do here is that we wait for
- * END_TRANSFER completion and only after that, we jump
- * over TRBs by clearing HWO and incrementing dequeue
- * pointer.
- *
- * Note that we have 2 possible types of transfers here:
- *
- * i) Linear buffer request
- * ii) SG-list based request
- *
- * SG-list based requests will have r->num_pending_sgs
- * set to a valid number (> 0). Linear requests,
- * normally use a single TRB.
- *
- * For each of these two cases, if r->unaligned flag is
- * set, one extra TRB has been used to align transfer
- * size to wMaxPacketSize.
- *
- * All of these cases need to be taken into
- * consideration so we don't mess up our TRB ring
- * pointers.
- */
- wait_event_lock_irq(dep->wait_end_transfer,
- !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
- dwc->lock);
-
if (!r->trb)
goto out0;
- if (r->num_pending_sgs) {
- struct dwc3_trb *trb;
- int i = 0;
-
- for (i = 0; i < r->num_pending_sgs; i++) {
- trb = r->trb + i;
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
- }
-
- if (r->unaligned || r->zero) {
- trb = r->trb + r->num_pending_sgs + 1;
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
- }
- } else {
- struct dwc3_trb *trb = r->trb;
-
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
-
- if (r->unaligned || r->zero) {
- trb = r->trb + 1;
- trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- dwc3_ep_inc_deq(dep);
- }
- }
- goto out1;
+ dwc3_gadget_move_cancelled_request(req);
+ goto out0;
}
dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name);
@@ -1440,9 +1561,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
goto out0;
}
-out1:
- /* giveback the request */
-
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
@@ -1867,6 +1985,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
+ dwc->link_state = DWC3_LINK_STATE_SS_DIS;
dwc3_ep0_out_start(dwc);
dwc3_gadget_enable_irq(dwc);
@@ -1934,8 +2053,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- int epnum;
- u32 tmo_eps = 0;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1944,36 +2061,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
__dwc3_gadget_stop(dwc);
- for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
- struct dwc3_ep *dep = dwc->eps[epnum];
- int ret;
-
- if (!dep)
- continue;
-
- if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
- continue;
-
- ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer,
- !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
- dwc->lock, msecs_to_jiffies(5));
-
- if (ret <= 0) {
- /* Timed out or interrupted! There's nothing much
- * we can do so we just log here and print which
- * endpoints timed out at the end.
- */
- tmo_eps |= 1 << epnum;
- dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
- }
- }
-
- if (tmo_eps) {
- dev_err(dwc->dev,
- "end transfer timed out on endpoints 0x%x [bitmap]\n",
- tmo_eps);
- }
-
out:
dwc->gadget_driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2148,6 +2235,8 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
dep->direction = direction;
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
+ dep->combo_num = 0;
+ dep->start_cmd_status = 0;
snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
direction ? "in" : "out");
@@ -2176,6 +2265,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
+ INIT_LIST_HEAD(&dep->cancelled_list);
return 0;
}
@@ -2235,6 +2325,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
dwc3_ep_inc_deq(dep);
trace_dwc3_complete_trb(dep, trb);
+ req->num_trbs--;
/*
* If we're in the middle of series of chained TRBs and we
@@ -2249,12 +2340,26 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ /*
+ * For isochronous transfers, the first TRB in a service interval must
+ * have the Isoc-First type. Track and report its interval frame number.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (trb->ctrl & DWC3_TRBCTL_ISOCHRONOUS_FIRST)) {
+ unsigned int frame_number;
+
+ frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl);
+ frame_number &= ~(dep->interval - 1);
+ req->request.frame_number = frame_number;
+ }
+
/*
* If we're dealing with unaligned size OUT transfer, we will be left
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
- if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
+
+ if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2331,11 +2436,10 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
- if (req->unaligned || req->zero) {
+ if (req->needs_extra_trb) {
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
- req->unaligned = false;
- req->zero = false;
+ req->needs_extra_trb = false;
}
req->request.actual = req->request.length - req->remaining;
@@ -2430,7 +2534,7 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
dwc3_gadget_endpoint_frame_from_event(dep, event);
- __dwc3_gadget_start_isoc(dep);
+ (void) __dwc3_gadget_start_isoc(dep);
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -2468,7 +2572,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
- wake_up(&dep->wait_end_transfer);
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
}
break;
case DWC3_DEPEVT_STREAMEVT:
@@ -3277,6 +3381,8 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
dwc3_disconnect_gadget(dwc);
__dwc3_gadget_stop(dwc);
+ synchronize_irq(dwc->irq_gadget);
+
return 0;
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 2aacd1afd9ff..023a473648eb 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -79,6 +79,21 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
list_move_tail(&req->list, &dep->started_list);
}
+/**
+ * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
+ * @req: the request to be moved
+ *
+ * Caller should take care of locking. This function will move @req from its
+ * current list to the endpoint's cancelled_list.
+ */
+static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
+{
+ struct dwc3_ep *dep = req->dep;
+
+ req->started = false;
+ list_move_tail(&req->list, &dep->cancelled_list);
+}
+
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 25/64] usb: dwc3: import from kernel v5.1
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (23 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 24/64] usb: dwc3: import from kernel v5.0 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 26/64] usb: dwc3: import from kernel v5.2 Jens Wiklander
` (42 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.0 to v5.1.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.0..v5.1
Commits imported:
7ae622c978db usb: dwc3: pci: add support for Comet Lake PCH ID
98bba546e82d usb: dwc3: drd: Defer probe if extcon device is not found
dc8b2a691dc8 Merge tag 'usb-for-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
c7152763f02e usb: dwc3: Reset num_trbs after skipping
c5353b225df9 usb: dwc3: gadget: don't enable interrupt when disabling endpoint
15e99b13b44b Merge 5.0-rc6 into usb-next
9c7ebc99b382 usb: dwc3: haps: Workaround matching VID PID
1381a5113caf usb: dwc3: debug: purge usage of strcat
7790b3556fcc usb: dwc3: trace: pass trace buffer size to decoding functions
3aec99154db3 usb: dwc3: gadget: remove DWC3_EP_END_TRANSFER_PENDING
9f45581f5eec usb: dwc3: gadget: early giveback if End Transfer already completed
974a1368c33e usb: dwc3: gadget: don't use resource_index as a flag
acbfa6c26f21 usb: dwc3: gadget: clear DWC3_EP_TRANSFER_STARTED on cmd complete
7c3d7dc89e57 usb: dwc3: gadget: remove req->started flag
b2b6d601365a usb: dwc3: gadget: prevent dwc3_request from being queued twice
a3af5e3ad3f1 usb: dwc3: gadget: add dwc3_request status tracking
eca6b49430c6 usb: dwc3: keystone: Add support for ti,am654-dwc3
169e3b68cadb usb: dwc3: gadget: Fix OTG events when gadget driver isn't loaded
54c9da1bcec4 usb: dwc3: qcom: Add support for MSM8998
c9381e185fdc Merge 5.0-rc4 into usb-next
cae8dc3b685f USB: add missing SPDX lines to Kconfig and Makefiles
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.h | 12 ++-
drivers/usb/dwc3/debug.h | 156 +++++++++++++++++++++-----------------
drivers/usb/dwc3/gadget.c | 65 ++++++++--------
drivers/usb/dwc3/gadget.h | 4 +-
4 files changed, 130 insertions(+), 107 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index df876418cb78..1528d395b156 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -692,7 +692,6 @@ struct dwc3_ep {
#define DWC3_EP_WEDGE BIT(2)
#define DWC3_EP_TRANSFER_STARTED BIT(3)
#define DWC3_EP_PENDING_REQUEST BIT(5)
-#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -863,6 +862,7 @@ struct dwc3_hwparams {
* @num_pending_sgs: counter to pending sgs
* @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining
+ * @status: internal dwc3 request status tracking
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
@@ -871,7 +871,6 @@ struct dwc3_hwparams {
* or unaligned OUT)
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
- * @started: request is started
*/
struct dwc3_request {
struct usb_request request;
@@ -883,6 +882,14 @@ struct dwc3_request {
unsigned num_pending_sgs;
unsigned int num_queued_sgs;
unsigned remaining;
+
+ unsigned int status;
+#define DWC3_REQUEST_STATUS_QUEUED 0
+#define DWC3_REQUEST_STATUS_STARTED 1
+#define DWC3_REQUEST_STATUS_CANCELLED 2
+#define DWC3_REQUEST_STATUS_COMPLETED 3
+#define DWC3_REQUEST_STATUS_UNKNOWN -1
+
u8 epnum;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
@@ -892,7 +899,6 @@ struct dwc3_request {
unsigned needs_extra_trb:1;
unsigned direction:1;
unsigned mapped:1;
- unsigned started:1;
};
/*
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 4f75ab3505b7..6759a7efd8d5 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -193,65 +193,69 @@ static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
* dwc3_gadget_event_string - returns event name
* @event: the event code
*/
-static inline const char *
-dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
+static inline const char *dwc3_gadget_event_string(char *str, size_t size,
+ const struct dwc3_event_devt *event)
{
enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
switch (event->type) {
case DWC3_DEVICE_EVENT_DISCONNECT:
- sprintf(str, "Disconnect: [%s]",
+ snprintf(str, size, "Disconnect: [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_RESET:
- sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state));
+ snprintf(str, size, "Reset [%s]",
+ dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_CONNECT_DONE:
- sprintf(str, "Connection Done [%s]",
+ snprintf(str, size, "Connection Done [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
- sprintf(str, "Link Change [%s]",
+ snprintf(str, size, "Link Change [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_WAKEUP:
- sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state));
+ snprintf(str, size, "WakeUp [%s]",
+ dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_EOPF:
- sprintf(str, "End-Of-Frame [%s]",
+ snprintf(str, size, "End-Of-Frame [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_SOF:
- sprintf(str, "Start-Of-Frame [%s]",
+ snprintf(str, size, "Start-Of-Frame [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- sprintf(str, "Erratic Error [%s]",
+ snprintf(str, size, "Erratic Error [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- sprintf(str, "Command Complete [%s]",
+ snprintf(str, size, "Command Complete [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state));
+ snprintf(str, size, "Overflow [%s]",
+ dwc3_gadget_link_string(state));
break;
default:
- sprintf(str, "UNKNOWN");
+ snprintf(str, size, "UNKNOWN");
}
return str;
}
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
+static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
+ size_t size)
{
switch (t & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
- sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
- i, l);
+ snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
+ i, l);
break;
case USB_RECIP_ENDPOINT:
- sprintf(str, "Get Endpoint Status(ep%d%s)",
+ snprintf(str, size, "Get Endpoint Status(ep%d%s)",
i & ~USB_DIR_IN,
i & USB_DIR_IN ? "in" : "out");
break;
@@ -259,11 +263,11 @@ static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
}
static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
- __u16 i, char *str)
+ __u16 i, char *str, size_t size)
{
switch (t & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- sprintf(str, "%s Device Feature(%s%s)",
+ snprintf(str, size, "%s Device Feature(%s%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
({char *s;
switch (v) {
@@ -311,13 +315,13 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
} s; }) : "");
break;
case USB_RECIP_INTERFACE:
- sprintf(str, "%s Interface Feature(%s)",
+ snprintf(str, size, "%s Interface Feature(%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
v == USB_INTRF_FUNC_SUSPEND ?
"Function Suspend" : "UNKNOWN");
break;
case USB_RECIP_ENDPOINT:
- sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
+ snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
i & ~USB_DIR_IN,
@@ -326,15 +330,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
}
}
-static inline void dwc3_decode_set_address(__u16 v, char *str)
+static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size)
{
- sprintf(str, "Set Address(Addr = %02x)", v);
+ snprintf(str, size, "Set Address(Addr = %02x)", v);
}
static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
- __u16 i, __u16 l, char *str)
+ __u16 i, __u16 l, char *str, size_t size)
{
- sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
+ snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
({ char *s;
switch (v >> 8) {
@@ -393,87 +397,92 @@ static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
}
-static inline void dwc3_decode_get_configuration(__u16 l, char *str)
+static inline void dwc3_decode_get_configuration(__u16 l, char *str,
+ size_t size)
{
- sprintf(str, "Get Configuration(Length = %d)", l);
+ snprintf(str, size, "Get Configuration(Length = %d)", l);
}
-static inline void dwc3_decode_set_configuration(__u8 v, char *str)
+static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size)
{
- sprintf(str, "Set Configuration(Config = %d)", v);
+ snprintf(str, size, "Set Configuration(Config = %d)", v);
}
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
+static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str,
+ size_t size)
{
- sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
+ snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l);
}
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
+static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size)
{
- sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
+ snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
}
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
+static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str,
+ size_t size)
{
- sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
+ snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
}
-static inline void dwc3_decode_set_sel(__u16 l, char *str)
+static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size)
{
- sprintf(str, "Set SEL(Length = %d)", l);
+ snprintf(str, size, "Set SEL(Length = %d)", l);
}
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
+static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size)
{
- sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
+ snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v);
}
/**
* dwc3_decode_ctrl - returns a string represetion of ctrl request
*/
-static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
- __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
+static inline const char *dwc3_decode_ctrl(char *str, size_t size,
+ __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex,
+ __u16 wLength)
{
switch (bRequest) {
case USB_REQ_GET_STATUS:
- dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
+ dwc3_decode_get_status(bRequestType, wIndex, wLength, str,
+ size);
break;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
- wIndex, str);
+ wIndex, str, size);
break;
case USB_REQ_SET_ADDRESS:
- dwc3_decode_set_address(wValue, str);
+ dwc3_decode_set_address(wValue, str, size);
break;
case USB_REQ_GET_DESCRIPTOR:
case USB_REQ_SET_DESCRIPTOR:
dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
- wIndex, wLength, str);
+ wIndex, wLength, str, size);
break;
case USB_REQ_GET_CONFIGURATION:
- dwc3_decode_get_configuration(wLength, str);
+ dwc3_decode_get_configuration(wLength, str, size);
break;
case USB_REQ_SET_CONFIGURATION:
- dwc3_decode_set_configuration(wValue, str);
+ dwc3_decode_set_configuration(wValue, str, size);
break;
case USB_REQ_GET_INTERFACE:
- dwc3_decode_get_intf(wIndex, wLength, str);
+ dwc3_decode_get_intf(wIndex, wLength, str, size);
break;
case USB_REQ_SET_INTERFACE:
- dwc3_decode_set_intf(wValue, wIndex, str);
+ dwc3_decode_set_intf(wValue, wIndex, str, size);
break;
case USB_REQ_SYNCH_FRAME:
- dwc3_decode_synch_frame(wIndex, wLength, str);
+ dwc3_decode_synch_frame(wIndex, wLength, str, size);
break;
case USB_REQ_SET_SEL:
- dwc3_decode_set_sel(wLength, str);
+ dwc3_decode_set_sel(wLength, str, size);
break;
case USB_REQ_SET_ISOCH_DELAY:
- dwc3_decode_set_isoch_delay(wValue, str);
+ dwc3_decode_set_isoch_delay(wValue, str, size);
break;
default:
- sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
bRequestType, bRequest,
cpu_to_le16(wValue) & 0xff,
cpu_to_le16(wValue) >> 8,
@@ -490,16 +499,15 @@ static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
* dwc3_ep_event_string - returns event name
* @event: then event code
*/
-static inline const char *
-dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
- u32 ep0state)
+static inline const char *dwc3_ep_event_string(char *str, size_t size,
+ const struct dwc3_event_depevt *event, u32 ep0state)
{
u8 epnum = event->endpoint_number;
size_t len;
int status;
int ret;
- ret = sprintf(str, "ep%d%s: ", epnum >> 1,
+ ret = snprintf(str, size, "ep%d%s: ", epnum >> 1,
(epnum & 1) ? "in" : "out");
if (ret < 0)
return "UNKNOWN";
@@ -509,7 +517,7 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
len = strlen(str);
- sprintf(str + len, "Transfer Complete (%c%c%c)",
+ snprintf(str + len, size - len, "Transfer Complete (%c%c%c)",
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
status & DEPEVT_STATUS_LST ? 'L' : 'l');
@@ -517,12 +525,13 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
len = strlen(str);
if (epnum <= 1)
- sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state));
+ snprintf(str + len, size - len, " [%s]",
+ dwc3_ep0_state_string(ep0state));
break;
case DWC3_DEPEVT_XFERINPROGRESS:
len = strlen(str);
- sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)",
+ snprintf(str + len, size - len, "Transfer In Progress [%d] (%c%c%c)",
event->parameters,
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
@@ -531,47 +540,51 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
case DWC3_DEPEVT_XFERNOTREADY:
len = strlen(str);
- sprintf(str + len, "Transfer Not Ready [%d]%s",
+ snprintf(str + len, size - len, "Transfer Not Ready [%d]%s",
event->parameters,
status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
" (Active)" : " (Not Active)");
+ len = strlen(str);
+
/* Control Endpoints */
if (epnum <= 1) {
int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status);
switch (phase) {
case DEPEVT_STATUS_CONTROL_DATA:
- strcat(str, " [Data Phase]");
+ snprintf(str + ret, size - ret,
+ " [Data Phase]");
break;
case DEPEVT_STATUS_CONTROL_STATUS:
- strcat(str, " [Status Phase]");
+ snprintf(str + ret, size - ret,
+ " [Status Phase]");
}
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- strcat(str, "FIFO");
+ snprintf(str + ret, size - ret, "FIFO");
break;
case DWC3_DEPEVT_STREAMEVT:
status = event->status;
switch (status) {
case DEPEVT_STREAMEVT_FOUND:
- sprintf(str + ret, " Stream %d Found",
+ snprintf(str + ret, size - ret, " Stream %d Found",
event->parameters);
break;
case DEPEVT_STREAMEVT_NOTFOUND:
default:
- strcat(str, " Stream Not Found");
+ snprintf(str + ret, size - ret, " Stream Not Found");
break;
}
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- strcat(str, "Endpoint Command Complete");
+ snprintf(str + ret, size - ret, "Endpoint Command Complete");
break;
default:
- sprintf(str, "UNKNOWN");
+ snprintf(str, size, "UNKNOWN");
}
return str;
@@ -611,14 +624,15 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
}
}
-static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state)
+static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
+ u32 ep0state)
{
const union dwc3_event evt = (union dwc3_event) event;
if (evt.type.is_devspec)
- return dwc3_gadget_event_string(str, &evt.devt);
+ return dwc3_gadget_event_string(str, size, &evt.devt);
else
- return dwc3_ep_event_string(str, &evt.depevt, ep0state);
+ return dwc3_ep_event_string(str, size, &evt.depevt, ep0state);
}
static inline const char *dwc3_ep_cmd_status_string(int status)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 6c9b76bcc2e1..e293400cc6e9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -174,7 +174,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
{
struct dwc3 *dwc = dep->dwc;
- req->started = false;
list_del(&req->list);
req->remaining = 0;
req->needs_extra_trb = false;
@@ -209,6 +208,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
struct dwc3 *dwc = dep->dwc;
dwc3_gadget_del_and_unmap_request(dep, req, status);
+ req->status = DWC3_REQUEST_STATUS_COMPLETED;
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
@@ -384,19 +384,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
- if (ret == 0) {
- switch (DWC3_DEPCMD_CMD(cmd)) {
- case DWC3_DEPCMD_STARTTRANSFER:
- dep->flags |= DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_get_transfer_index(dep);
- break;
- case DWC3_DEPCMD_ENDTRANSFER:
- dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- break;
- default:
- /* nothing */
- break;
- }
+ if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_get_transfer_index(dep);
}
if (saved_config) {
@@ -642,7 +632,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
- dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number);
@@ -698,12 +687,13 @@ out:
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force);
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
- dwc3_stop_active_transfer(dep, true);
+ dwc3_stop_active_transfer(dep, true, false);
/* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) {
@@ -748,7 +738,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false;
dep->type = 0;
- dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags = 0;
/* Clear out the ep descriptors for non-ep0 */
if (dep->number > 1) {
@@ -847,6 +837,7 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
req->direction = dep->direction;
req->epnum = dep->number;
req->dep = dep;
+ req->status = DWC3_REQUEST_STATUS_UNKNOWN;
trace_dwc3_alloc_request(req);
@@ -1360,7 +1351,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
* to wait for the next XferNotReady to test the command again
*/
if (cmd_status == 0) {
- dwc3_stop_active_transfer(dep, true);
+ dwc3_stop_active_transfer(dep, true, true);
return 0;
}
}
@@ -1435,6 +1426,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
&req->request, req->dep->name))
return -EINVAL;
+ if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
+ "%s: request %pK already in flight\n",
+ dep->name, &req->request))
+ return -EINVAL;
+
pm_runtime_get(dwc->dev);
req->request.actual = 0;
@@ -1443,6 +1439,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req);
list_add_tail(&req->list, &dep->pending_list);
+ req->status = DWC3_REQUEST_STATUS_QUEUED;
/*
* NOTICE: Isochronous endpoints should NEVER be prestarted. We must
@@ -1506,6 +1503,8 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
}
+
+ req->num_trbs = 0;
}
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
@@ -1547,13 +1546,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
if (r == req) {
/* wait until it is processed */
- dwc3_stop_active_transfer(dep, true);
+ dwc3_stop_active_transfer(dep, true, true);
if (!r->trb)
goto out0;
dwc3_gadget_move_cancelled_request(req);
- goto out0;
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
+ goto out0;
+ else
+ goto out1;
}
dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name);
@@ -1561,6 +1563,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
goto out0;
}
+out1:
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
@@ -2500,7 +2503,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
if (stop) {
- dwc3_stop_active_transfer(dep, true);
+ dwc3_stop_active_transfer(dep, true, true);
dep->flags = DWC3_EP_ENABLED;
}
@@ -2547,7 +2550,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep = dwc->eps[epnum];
if (!(dep->flags & DWC3_EP_ENABLED)) {
- if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
return;
/* Handle only EPCMDCMPLT when EP disabled */
@@ -2571,7 +2574,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
- dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
}
break;
@@ -2621,15 +2624,15 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt)
{
struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
- if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
- !dep->resource_index)
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
return;
/*
@@ -2665,17 +2668,15 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
- cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(¶ms, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
- if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) {
- dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
udelay(100);
- }
}
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
@@ -3339,6 +3340,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err4;
}
+ dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
+
return 0;
err4:
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 023a473648eb..3ed738e86ea7 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -75,7 +75,7 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
{
struct dwc3_ep *dep = req->dep;
- req->started = true;
+ req->status = DWC3_REQUEST_STATUS_STARTED;
list_move_tail(&req->list, &dep->started_list);
}
@@ -90,7 +90,7 @@ static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
{
struct dwc3_ep *dep = req->dep;
- req->started = false;
+ req->status = DWC3_REQUEST_STATUS_CANCELLED;
list_move_tail(&req->list, &dep->cancelled_list);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 26/64] usb: dwc3: import from kernel v5.2
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (24 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 25/64] usb: dwc3: import from kernel v5.1 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 27/64] usb: dwc3: import from kernel v5.3 Jens Wiklander
` (41 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.1 to v5.2.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.1..v5.2
Commits imported:
a2d635decbfa Merge tag 'drm-next-2019-05-09' of git://anongit.freedesktop.org/drm/drm
2e487d280525 usb: dwc3: Rename DWC3_DCTL_LPM_ERRATA
8d791929b2fb usb: dwc3: Fix default lpm_nyet_threshold value
dd24f9b604d3 usb: dwc3: debug: Print GET_STATUS(device) tracepoint
b873e2d0ea1e usb: dwc3: Do core validation early on probe
c729969b2b69 usb: dwc3: gadget: Set lpm_capable
41a91c606e7d usb: dwc3: move synchronize_irq() out of the spinlock protected block
75ecb9dd56a7 usb: dwc3: Free resource immediately after use
c0c61471ef86 usb: dwc3: of-simple: Convert to bulk clk API
c99993376f72 usb: dwc3: Add Amlogic G12A DWC3 glue
67130830ce42 usb: dwc3: Allow building USB_DWC3_QCOM without EXTCON
6e865c723014 Merge tag 'drm/tegra/for-5.2-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next
f06ddb53096b BackMerge v5.1-rc5 into drm-next
f31d5c24fb2e reset: Add acquired flag to of_reset_control_array_get()
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 18 +++++++++++-------
drivers/usb/dwc3/core.h | 3 +--
drivers/usb/dwc3/debug.h | 3 +++
drivers/usb/dwc3/gadget.c | 5 ++---
4 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a1b126f90261..4aff1d8dbc4f 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -828,6 +828,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
ret = device_property_read_u32_array(dev,
"snps,incr-burst-type-adjustment", vals, ntype);
if (ret) {
+ kfree(vals);
dev_err(dev, "Error to get property\n");
return;
}
@@ -846,6 +847,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
incrx_mode = INCRX_BURST_MODE;
}
+ kfree(vals);
+
/* Enable Undefined Length INCR Burst and Enable INCRx Burst */
cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK;
if (incrx_mode)
@@ -893,12 +896,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
u32 reg;
int ret;
- if (!dwc3_core_is_valid(dwc)) {
- dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
- ret = -ENODEV;
- goto err0;
- }
-
/*
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
@@ -1218,7 +1215,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 tx_max_burst_prd;
/* default to highest possible threshold */
- lpm_nyet_threshold = 0xff;
+ lpm_nyet_threshold = 0xf;
/* default to -3.5dB de-emphasis */
tx_de_emphasis = 1;
@@ -1426,6 +1423,11 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->regs = regs;
dwc->regs_size = resource_size(&dwc_res);
+ if (!dwc3_core_is_valid(dwc)) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ return -ENODEV;
+ }
+
dwc3_get_properties(dwc);
dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
@@ -1600,6 +1602,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ synchronize_irq(dwc->irq_gadget);
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
@@ -1632,6 +1635,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ synchronize_irq(dwc->irq_gadget);
}
dwc3_otg_exit(dwc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1528d395b156..f19cbeb01087 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -406,8 +406,7 @@
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
/* These apply for core versions 1.94a and later */
-#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
-#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
+#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20)
#define DWC3_DCTL_KEEP_CONNECT BIT(19)
#define DWC3_DCTL_L1_HIBER_EN BIT(18)
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 6759a7efd8d5..068259fdfb0c 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -250,6 +250,9 @@ static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
size_t size)
{
switch (t & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ snprintf(str, size, "Get Device Status(Length = %d)", l);
+ break;
case USB_RECIP_INTERFACE:
snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
i, l);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index e293400cc6e9..d67655384eb2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2863,7 +2863,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
- reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
+ reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
} else {
@@ -3301,6 +3301,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
+ dwc->gadget.lpm_capable = true;
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -3384,8 +3385,6 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
dwc3_disconnect_gadget(dwc);
__dwc3_gadget_stop(dwc);
- synchronize_irq(dwc->irq_gadget);
-
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 27/64] usb: dwc3: import from kernel v5.3
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (25 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 26/64] usb: dwc3: import from kernel v5.2 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 28/64] usb: dwc3: import from kernel v5.4 Jens Wiklander
` (40 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.2 to v5.3.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.2..v5.3
Commits imported:
332694f8a4f7 Revert "usb:gadget Separated decoding functions from dwc3 driver."
3db1b636c07e usb:gadget Separated decoding functions from dwc3 driver.
b3649dee5fbb usb: dwc3: pci: add support for TigerLake Devices
aa23ce847dda usb: dwc3: remove unused @lock member of dwc3_ep struct
dbb0569de852 usb: dwc3: pci: Add Support for Intel Elkhart Lake Devices
729dcffd1ed3 usb: dwc3: gadget: Add support for disabling U1 and U2 entries
4d20a6107050 usb: dwc3: qcom: Use of_clk_get_parent_count()
dc1b5d9aed17 usb: dwc3: Fix core validation in probe, move after clocks are enabled
7f5d6a469617 usb: dwc3: qcom: Improve error handling
a6e456209d08 usb: dwc3: qcom: Start USB in 'host mode' on the SDM845
2bc02355f8ba usb: dwc3: qcom: Add support for booting with ACPI
f90db10779ad usb: dwc3: meson-g12a: Add support for IRQ based OTG switching
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 16 +++++++++++-----
drivers/usb/dwc3/core.h | 6 ++++--
drivers/usb/dwc3/ep0.c | 9 ++++++++-
drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
drivers/usb/dwc3/gadget.h | 6 ++++++
5 files changed, 49 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 4aff1d8dbc4f..c9bb93a2c81e 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1282,6 +1282,10 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,dis_u2_susphy_quirk");
dwc->dis_enblslpm_quirk = device_property_read_bool(dev,
"snps,dis_enblslpm_quirk");
+ dwc->dis_u1_entry_quirk = device_property_read_bool(dev,
+ "snps,dis-u1-entry-quirk");
+ dwc->dis_u2_entry_quirk = device_property_read_bool(dev,
+ "snps,dis-u2-entry-quirk");
dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
"snps,dis_rxdet_inp3_quirk");
dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
@@ -1423,11 +1427,6 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->regs = regs;
dwc->regs_size = resource_size(&dwc_res);
- if (!dwc3_core_is_valid(dwc)) {
- dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
- return -ENODEV;
- }
-
dwc3_get_properties(dwc);
dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
@@ -1460,6 +1459,12 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret)
goto unprepare_clks;
+ if (!dwc3_core_is_valid(dwc)) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ ret = -ENODEV;
+ goto disable_clks;
+ }
+
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
@@ -1525,6 +1530,7 @@ err1:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+disable_clks:
clk_bulk_disable(dwc->num_clks, dwc->clks);
unprepare_clks:
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f19cbeb01087..3dd783b889cb 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -649,7 +649,6 @@ struct dwc3_event_buffer {
* @cancelled_list: list of cancelled requests for this endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
- * @lock: spinlock for endpoint request queue traversal
* @regs: pointer to first endpoint register
* @trb_pool: array of transaction buffers
* @trb_pool_dma: dma address of @trb_pool
@@ -677,7 +676,6 @@ struct dwc3_ep {
struct list_head pending_list;
struct list_head started_list;
- spinlock_t lock;
void __iomem *regs;
struct dwc3_trb *trb_pool;
@@ -1014,6 +1012,8 @@ struct dwc3_scratchpad_array {
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
* disabling the suspend signal to the PHY.
+ * @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled.
+ * @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled.
* @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3
* @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
* in GUSB2PHYCFG, specify that USB2 PHY doesn't
@@ -1205,6 +1205,8 @@ struct dwc3 {
unsigned dis_u3_susphy_quirk:1;
unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1;
+ unsigned dis_u1_entry_quirk:1;
+ unsigned dis_u2_entry_quirk:1;
unsigned dis_rxdet_inp3_quirk:1;
unsigned dis_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 8efde178eef4..3996b9c4ff8d 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -379,6 +379,8 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
return -EINVAL;
+ if (set && dwc->dis_u1_entry_quirk)
+ return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set)
@@ -401,6 +403,8 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
return -EINVAL;
+ if (set && dwc->dis_u2_entry_quirk)
+ return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set)
@@ -626,7 +630,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* nothing is pending from application.
*/
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+ if (!dwc->dis_u1_entry_quirk)
+ reg |= DWC3_DCTL_ACCEPTU1ENA;
+ if (!dwc->dis_u2_entry_quirk)
+ reg |= DWC3_DCTL_ACCEPTU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
break;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index d67655384eb2..173f5329d3d9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2073,6 +2073,25 @@ out:
return 0;
}
+static void dwc3_gadget_config_params(struct usb_gadget *g,
+ struct usb_dcd_config_params *params)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ /* U1 Device exit Latency */
+ if (dwc->dis_u1_entry_quirk)
+ params->bU1devExitLat = 0;
+ else
+ params->bU1devExitLat = DWC3_DEFAULT_U1_DEV_EXIT_LAT;
+
+ /* U2 Device exit Latency */
+ if (dwc->dis_u2_entry_quirk)
+ params->bU2DevExitLat = 0;
+ else
+ params->bU2DevExitLat =
+ cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT);
+}
+
static void dwc3_gadget_set_speed(struct usb_gadget *g,
enum usb_device_speed speed)
{
@@ -2142,6 +2161,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
.udc_set_speed = dwc3_gadget_set_speed,
+ .get_config_params = dwc3_gadget_config_params,
};
/* -------------------------------------------------------------------------- */
@@ -2251,8 +2271,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
dep->endpoint.comp_desc = NULL;
}
- spin_lock_init(&dep->lock);
-
if (num == 0)
ret = dwc3_gadget_init_control_endpoint(dep);
else if (direction)
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 3ed738e86ea7..5faf4d1249e0 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -48,6 +48,12 @@ struct dwc3;
/* DEPXFERCFG parameter 0 */
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
+/* U1 Device exit Latency */
+#define DWC3_DEFAULT_U1_DEV_EXIT_LAT 0x0A /* Less then 10 microsec */
+
+/* U2 Device exit Latency */
+#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */
+
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 28/64] usb: dwc3: import from kernel v5.4
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (26 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 27/64] usb: dwc3: import from kernel v5.3 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 29/64] usb: dwc3: import from kernel v5.5 Jens Wiklander
` (39 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.3 to v5.4.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.3..v5.4
Commits imported:
d8eca64eec71 usb: dwc3: gadget: fix race when disabling ep with cancelled xfers
a7d9874c6f3f usb: dwc3: remove the call trace of USBx_GFLADJ
9bbfceea12a8 usb: dwc3: pci: prevent memory leak in dwc3_pci_probe
a51bab592fbb usb: dwc3: select CONFIG_REGMAP_MMIO
d819f6584c20 usb: dwc3: Remove dev_err() on platform_get_irq() failure
f146b40ba1f0 usb: dwc3: Switch to platform_get_irq_byname_optional()
a0a465569b45 usb: dwc3: remove generic PHY calibrate() calls
96e46dcfb853 Merge tag 'usb-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
17b63704ec7c usb: dwc3: gadget: Workaround Mirosoft's BESL check
91f255a26bce usb: common: Separated decoding functions from dwc3 driver.
54fb5ba647f8 usb: dwc3: gadget: Set BESL config parameter
16fe4f304015 usb: dwc3: Separate field holding multiple properties
1cf084d161f4 usb: dwc3: meson-g12a: fix suspend resume regulator unbalanced disables
bceb418b65b9 usb: dwc3: st: Add of_dev_put() in probe function
e36721b90144 usb: dwc3: st: Add of_node_put() before return in probe function
c09b73cfac2a usb: dwc3: don't set gadget->is_otg flag
240b65dc1e89 usb: dwc3: Use clk_bulk_prepare_enable()
03bf32bbb607 usb: dwc3: Use devres to get clocks
3cd703f483d1 usb: dwc3: remove generic PHYs forwarding for XHCI device
4749e0e61241 usb: dwc3: Update soft-reset wait polling rate
b2a3974253d3 usb: dwc3: omap: squash include/linux/platform_data/dwc3-omap.h
a6e5e6794a53 usb: dwc3: Switch to use device_property_count_u32()
5b76f6a0b0f8 usb: dwc3: keystone: use devm_platform_ioremap_resource() to simplify code
58dd0bad2554 usb: dwc3: omap: use devm_platform_ioremap_resource() to simplify code
c6e4999cd930 usb: dwc3: meson-g12a: use devm_platform_ioremap_resource() to simplify code
d3523b631403 usb: dwc3: meson-g12a: use devm_platform_ioremap_resource() to simplify code
b33f37064b74 usb: Remove dev_err() usage after platform_get_irq()
41b57327b270 usb: dwc3: Switch to use device_property_count_u32()
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 64 +++++-----
drivers/usb/dwc3/core.h | 2 +
drivers/usb/dwc3/debug.h | 252 --------------------------------------
drivers/usb/dwc3/gadget.c | 37 ++++--
4 files changed, 60 insertions(+), 295 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index c9bb93a2c81e..97d6ae3c4df2 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -168,7 +168,6 @@ static void __dwc3_set_mode(struct work_struct *work)
otg_set_vbus(dwc->usb2_phy->otg, true);
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
- phy_calibrate(dwc->usb2_generic_phy);
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -252,12 +251,25 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
reg |= DWC3_DCTL_CSFTRST;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ /*
+ * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
+ * is cleared only after all the clocks are synchronized. This can
+ * take a little more than 50ms. Set the polling rate at 20ms
+ * for 10 times instead.
+ */
+ if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
+ retries = 10;
+
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
- udelay(1);
+ if (dwc3_is_usb31(dwc) &&
+ dwc->revision >= DWC3_USB31_REVISION_190A)
+ msleep(20);
+ else
+ udelay(1);
} while (--retries);
phy_exit(dwc->usb3_generic_phy);
@@ -267,11 +279,11 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
done:
/*
- * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
- * we must wait at least 50ms before accessing the PHY domain
- * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ * For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit
+ * is cleared, we must wait at least 50ms before accessing the PHY
+ * domain (synchronization delay).
*/
- if (dwc3_is_usb31(dwc))
+ if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
msleep(50);
return 0;
@@ -300,8 +312,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
- if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
- "request value same as default, ignoring\n")) {
+ if (dft != dwc->fladj) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
@@ -686,8 +697,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
- clk_bulk_disable(dwc->num_clks, dwc->clks);
- clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
reset_control_assert(dwc->reset);
}
@@ -813,8 +823,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
* result = 1, means INCRx burst mode supported.
* result > 1, means undefined length burst mode supported.
*/
- ntype = device_property_read_u32_array(dev,
- "snps,incr-burst-type-adjustment", NULL, 0);
+ ntype = device_property_count_u32(dev, "snps,incr-burst-type-adjustment");
if (ntype <= 0)
return;
@@ -1166,7 +1175,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dev_err(dev, "failed to initialize host\n");
return ret;
}
- phy_calibrate(dwc->usb2_generic_phy);
break;
case USB_DR_MODE_OTG:
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
@@ -1310,8 +1318,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
- dwc->hird_threshold = hird_threshold
- | (dwc->is_utmi_l1_suspend << 4);
+ dwc->hird_threshold = hird_threshold;
dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
dwc->rx_max_burst_prd = rx_max_burst_prd;
@@ -1436,7 +1443,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (dev->of_node) {
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
- ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
if (ret == -EPROBE_DEFER)
return ret;
/*
@@ -1449,16 +1456,12 @@ static int dwc3_probe(struct platform_device *pdev)
ret = reset_control_deassert(dwc->reset);
if (ret)
- goto put_clks;
+ return ret;
- ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
if (ret)
goto assert_reset;
- ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
- if (ret)
- goto unprepare_clks;
-
if (!dwc3_core_is_valid(dwc)) {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
@@ -1531,13 +1534,9 @@ err1:
pm_runtime_disable(&pdev->dev);
disable_clks:
- clk_bulk_disable(dwc->num_clks, dwc->clks);
-unprepare_clks:
- clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
reset_control_assert(dwc->reset);
-put_clks:
- clk_bulk_put(dwc->num_clks, dwc->clks);
return ret;
}
@@ -1560,7 +1559,6 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
- clk_bulk_put(dwc->num_clks, dwc->clks);
return 0;
}
@@ -1574,14 +1572,10 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
if (ret)
return ret;
- ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
if (ret)
goto assert_reset;
- ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
- if (ret)
- goto unprepare_clks;
-
ret = dwc3_core_init(dwc);
if (ret)
goto disable_clks;
@@ -1589,9 +1583,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
return 0;
disable_clks:
- clk_bulk_disable(dwc->num_clks, dwc->clks);
-unprepare_clks:
- clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
reset_control_assert(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 3dd783b889cb..1c8b349379af 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1137,6 +1137,8 @@ struct dwc3 {
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
u32 version_type;
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 068259fdfb0c..9baabed87d61 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -246,258 +246,6 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
return str;
}
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
- size_t size)
-{
- switch (t & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- snprintf(str, size, "Get Device Status(Length = %d)", l);
- break;
- case USB_RECIP_INTERFACE:
- snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
- i, l);
- break;
- case USB_RECIP_ENDPOINT:
- snprintf(str, size, "Get Endpoint Status(ep%d%s)",
- i & ~USB_DIR_IN,
- i & USB_DIR_IN ? "in" : "out");
- break;
- }
-}
-
-static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
- __u16 i, char *str, size_t size)
-{
- switch (t & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- snprintf(str, size, "%s Device Feature(%s%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- ({char *s;
- switch (v) {
- case USB_DEVICE_SELF_POWERED:
- s = "Self Powered";
- break;
- case USB_DEVICE_REMOTE_WAKEUP:
- s = "Remote Wakeup";
- break;
- case USB_DEVICE_TEST_MODE:
- s = "Test Mode";
- break;
- case USB_DEVICE_U1_ENABLE:
- s = "U1 Enable";
- break;
- case USB_DEVICE_U2_ENABLE:
- s = "U2 Enable";
- break;
- case USB_DEVICE_LTM_ENABLE:
- s = "LTM Enable";
- break;
- default:
- s = "UNKNOWN";
- } s; }),
- v == USB_DEVICE_TEST_MODE ?
- ({ char *s;
- switch (i) {
- case TEST_J:
- s = ": TEST_J";
- break;
- case TEST_K:
- s = ": TEST_K";
- break;
- case TEST_SE0_NAK:
- s = ": TEST_SE0_NAK";
- break;
- case TEST_PACKET:
- s = ": TEST_PACKET";
- break;
- case TEST_FORCE_EN:
- s = ": TEST_FORCE_EN";
- break;
- default:
- s = ": UNKNOWN";
- } s; }) : "");
- break;
- case USB_RECIP_INTERFACE:
- snprintf(str, size, "%s Interface Feature(%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- v == USB_INTRF_FUNC_SUSPEND ?
- "Function Suspend" : "UNKNOWN");
- break;
- case USB_RECIP_ENDPOINT:
- snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
- i & ~USB_DIR_IN,
- i & USB_DIR_IN ? "in" : "out");
- break;
- }
-}
-
-static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size)
-{
- snprintf(str, size, "Set Address(Addr = %02x)", v);
-}
-
-static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
- __u16 i, __u16 l, char *str, size_t size)
-{
- snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
- b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
- ({ char *s;
- switch (v >> 8) {
- case USB_DT_DEVICE:
- s = "Device";
- break;
- case USB_DT_CONFIG:
- s = "Configuration";
- break;
- case USB_DT_STRING:
- s = "String";
- break;
- case USB_DT_INTERFACE:
- s = "Interface";
- break;
- case USB_DT_ENDPOINT:
- s = "Endpoint";
- break;
- case USB_DT_DEVICE_QUALIFIER:
- s = "Device Qualifier";
- break;
- case USB_DT_OTHER_SPEED_CONFIG:
- s = "Other Speed Config";
- break;
- case USB_DT_INTERFACE_POWER:
- s = "Interface Power";
- break;
- case USB_DT_OTG:
- s = "OTG";
- break;
- case USB_DT_DEBUG:
- s = "Debug";
- break;
- case USB_DT_INTERFACE_ASSOCIATION:
- s = "Interface Association";
- break;
- case USB_DT_BOS:
- s = "BOS";
- break;
- case USB_DT_DEVICE_CAPABILITY:
- s = "Device Capability";
- break;
- case USB_DT_PIPE_USAGE:
- s = "Pipe Usage";
- break;
- case USB_DT_SS_ENDPOINT_COMP:
- s = "SS Endpoint Companion";
- break;
- case USB_DT_SSP_ISOC_ENDPOINT_COMP:
- s = "SSP Isochronous Endpoint Companion";
- break;
- default:
- s = "UNKNOWN";
- break;
- } s; }), v & 0xff, l);
-}
-
-
-static inline void dwc3_decode_get_configuration(__u16 l, char *str,
- size_t size)
-{
- snprintf(str, size, "Get Configuration(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size)
-{
- snprintf(str, size, "Set Configuration(Config = %d)", v);
-}
-
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str,
- size_t size)
-{
- snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size)
-{
- snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
-}
-
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str,
- size_t size)
-{
- snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size)
-{
- snprintf(str, size, "Set SEL(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size)
-{
- snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v);
-}
-
-/**
- * dwc3_decode_ctrl - returns a string represetion of ctrl request
- */
-static inline const char *dwc3_decode_ctrl(char *str, size_t size,
- __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex,
- __u16 wLength)
-{
- switch (bRequest) {
- case USB_REQ_GET_STATUS:
- dwc3_decode_get_status(bRequestType, wIndex, wLength, str,
- size);
- break;
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
- wIndex, str, size);
- break;
- case USB_REQ_SET_ADDRESS:
- dwc3_decode_set_address(wValue, str, size);
- break;
- case USB_REQ_GET_DESCRIPTOR:
- case USB_REQ_SET_DESCRIPTOR:
- dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
- wIndex, wLength, str, size);
- break;
- case USB_REQ_GET_CONFIGURATION:
- dwc3_decode_get_configuration(wLength, str, size);
- break;
- case USB_REQ_SET_CONFIGURATION:
- dwc3_decode_set_configuration(wValue, str, size);
- break;
- case USB_REQ_GET_INTERFACE:
- dwc3_decode_get_intf(wIndex, wLength, str, size);
- break;
- case USB_REQ_SET_INTERFACE:
- dwc3_decode_set_intf(wValue, wIndex, str, size);
- break;
- case USB_REQ_SYNCH_FRAME:
- dwc3_decode_synch_frame(wIndex, wLength, str, size);
- break;
- case USB_REQ_SET_SEL:
- dwc3_decode_set_sel(wLength, str, size);
- break;
- case USB_REQ_SET_ISOCH_DELAY:
- dwc3_decode_set_isoch_delay(wValue, str, size);
- break;
- default:
- snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
- bRequestType, bRequest,
- cpu_to_le16(wValue) & 0xff,
- cpu_to_le16(wValue) >> 8,
- cpu_to_le16(wIndex) & 0xff,
- cpu_to_le16(wIndex) >> 8,
- cpu_to_le16(wLength) & 0xff,
- cpu_to_le16(wLength) >> 8);
- }
-
- return str;
-}
-
/**
* dwc3_ep_event_string - returns event name
* @event: then event code
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 173f5329d3d9..a9aba716bf80 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -707,6 +707,12 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
+
+ while (!list_empty(&dep->cancelled_list)) {
+ req = next_request(&dep->cancelled_list);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
}
/**
@@ -2078,6 +2084,26 @@ static void dwc3_gadget_config_params(struct usb_gadget *g,
{
struct dwc3 *dwc = gadget_to_dwc(g);
+ params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED;
+ params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED;
+
+ /* Recommended BESL */
+ if (!dwc->dis_enblslpm_quirk) {
+ /*
+ * If the recommended BESL baseline is 0 or if the BESL deep is
+ * less than 2, Microsoft's Windows 10 host usb stack will issue
+ * a usb reset immediately after it receives the extended BOS
+ * descriptor and the enumeration will fail. To maintain
+ * compatibility with the Windows' usb stack, let's set the
+ * recommended BESL baseline to 1 and clamp the BESL deep to be
+ * within 2 to 15.
+ */
+ params->besl_baseline = 1;
+ if (dwc->is_utmi_l1_suspend)
+ params->besl_deep =
+ clamp_t(u8, dwc->hird_threshold, 2, 15);
+ }
+
/* U1 Device exit Latency */
if (dwc->dis_u1_entry_quirk)
params->bU1devExitLat = 0;
@@ -2868,7 +2894,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
- reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
+ reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
+ (dwc->is_utmi_l1_suspend << 4));
/*
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
@@ -3243,14 +3270,14 @@ static int dwc3_gadget_get_irq(struct dwc3 *dwc)
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
int irq;
- irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
+ irq = platform_get_irq_byname_optional(dwc3_pdev, "peripheral");
if (irq > 0)
goto out;
if (irq == -EPROBE_DEFER)
goto out;
- irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
if (irq > 0)
goto out;
@@ -3261,9 +3288,6 @@ static int dwc3_gadget_get_irq(struct dwc3 *dwc)
if (irq > 0)
goto out;
- if (irq != -EPROBE_DEFER)
- dev_err(dwc->dev, "missing peripheral IRQ\n");
-
if (!irq)
irq = -EINVAL;
@@ -3318,7 +3342,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
- dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
dwc->gadget.lpm_capable = true;
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 29/64] usb: dwc3: import from kernel v5.5
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (27 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 28/64] usb: dwc3: import from kernel v5.4 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 30/64] usb: dwc3: import from kernel v5.6 Jens Wiklander
` (38 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.4 to v5.5.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.4..v5.5
Commits imported:
ea0d762775e2 usb: dwc3: gadget: Fix request complete check
3c3caae4cd6e usb: dwc3: pci: add ID for the Intel Comet Lake -H variant
2d7b78f59e02 usb: dwc3: ep0: Clear started flag on completion
d3abda5a98a1 usb: dwc3: gadget: Clear started flag for non-IOC
8c7d4b7b3d43 usb: dwc3: gadget: Fix logical condition
c3afa2224345 usb: dwc3: Fix Kconfig indentation
7170d1a4cc4d Merge tag 'usb-for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
dcf5a2c390a3 usb: dwc3: create debugfs directory under usb root
16c8373ecf7b Merge 5.4-rc6 into usb-next
726b4fba94be usb: dwc3: of-simple: add a shutdown
038761ce68c2 usb: dwc3: debug: Remove newline printout
9ba3aca8fe82 usb: dwc3: Disable phy suspend after power-on reset
bf7c1d95af67 Merge 5.4-rc3 into usb-next
c2c1c66b5d43 usb: Fix Kconfig indentation
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 37 +++++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/debug.h | 4 ++--
drivers/usb/dwc3/ep0.c | 8 ++++++++
drivers/usb/dwc3/gadget.c | 12 +++++++++++-
4 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 97d6ae3c4df2..f561c6c9e8a9 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -566,8 +566,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
*/
static int dwc3_phy_setup(struct dwc3 *dwc)
{
+ unsigned int hw_mode;
u32 reg;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
@@ -585,6 +588,14 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->revision > DWC3_REVISION_194A)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ /*
+ * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after
+ * power-on reset, and it can be set after core initialization, which is
+ * after device soft-reset during initialization.
+ */
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
@@ -668,6 +679,14 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->revision > DWC3_REVISION_194A)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ /*
+ * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after
+ * power-on reset, and it can be set after core initialization, which is
+ * after device soft-reset during initialization.
+ */
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -902,9 +921,12 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
+ unsigned int hw_mode;
u32 reg;
int ret;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
/*
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
@@ -940,6 +962,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err0a;
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
+ dwc->revision > DWC3_REVISION_194A) {
+ if (!dwc->dis_u3_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ }
+
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+ }
+
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 9baabed87d61..e56beb9d1e36 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -112,7 +112,7 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
case DWC3_LINK_STATE_RESUME:
return "Resume";
default:
- return "UNKNOWN link state\n";
+ return "UNKNOWN link state";
}
}
@@ -141,7 +141,7 @@ dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
case DWC3_LINK_STATE_RESUME:
return "Resume";
default:
- return "UNKNOWN link state\n";
+ return "UNKNOWN link state";
}
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 3996b9c4ff8d..fd1b100d2927 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1117,6 +1117,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+ u8 cmd;
+
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_ep0_xfer_complete(dwc, event);
@@ -1129,7 +1132,12 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
case DWC3_DEPEVT_XFERINPROGRESS:
case DWC3_DEPEVT_RXTXFIFOEVT:
case DWC3_DEPEVT_STREAMEVT:
+ break;
case DWC3_DEPEVT_EPCMDCMPLT:
+ cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER)
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
break;
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a9aba716bf80..154f3f3e8cff 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2467,6 +2467,13 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
{
+ /*
+ * For OUT direction, host may send less than the setup
+ * length. Return true for all OUT requests.
+ */
+ if (!req->direction)
+ return true;
+
return req->request.actual == req->request.length;
}
@@ -2491,7 +2498,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->request.actual = req->request.length - req->remaining;
- if (!dwc3_gadget_ep_request_completed(req) &&
+ if (!dwc3_gadget_ep_request_completed(req) ||
req->num_pending_sgs) {
__dwc3_gadget_kick_transfer(dep);
goto out;
@@ -2719,6 +2726,9 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
WARN_ON_ONCE(ret);
dep->resource_index = 0;
+ if (!interrupt)
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+
if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
udelay(100);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 30/64] usb: dwc3: import from kernel v5.6
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (28 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 29/64] usb: dwc3: import from kernel v5.5 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-08 15:36 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 31/64] usb: dwc3: import from kernel v5.7 Jens Wiklander
` (37 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.5 to v5.6.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.5..v5.6
Commits imported:
dad2aff3e827 usb: dwc3: gadget: Update chain bit correctly when using sg list
42cd5ffe46c1 usb: dwc3: debug: fix string position formatting mixup with ret and len
5ee858975b13 usb: dwc3: gadget: Check for IOC/LST bit in TRB->ctrl fields
aac96626713f Merge tag 'usb-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
85c009e8e5d2 Merge tag 'devprop-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
f5ae88690955 usb: dwc3: pci: add ID for the Intel Comet Lake -V variant
cf2f8b63f7f1 usb: dwc3: gadget: Remove END_TRANSFER delay
da10bcdd6f70 usb: dwc3: gadget: Delay starting transfer
c58d8bfc77a2 usb: dwc3: gadget: Check END_TRANSFER completion
09ed259fac62 usb: dwc3: turn off VBUS when leaving host mode
2e708fa3b898 usb: dwc3: gadget: Clear DCTL.ULSTCHNGREQ before set
1b6009ea88ec usb: dwc3: gadget: Set link state to RX_Detect on disconnect
5b738211fb59 usb: dwc3: gadget: Don't send unintended link state change
845f081002ee Merge 5.5-rc6 into usb-next
dea7b202bd9c usb: exynos: Rename Samsung and Exynos to lowercase
5eb5afb07853 usb: dwc3: use proper initializers for property entries
e880c3744b31 Merge 5.5-rc2 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 3 ++
drivers/usb/dwc3/core.h | 2 ++
drivers/usb/dwc3/debug.h | 39 +++++++++-------------
drivers/usb/dwc3/ep0.c | 4 ++-
drivers/usb/dwc3/gadget.c | 68 +++++++++++++++++++++++++--------------
drivers/usb/dwc3/gadget.h | 14 ++++++++
6 files changed, 80 insertions(+), 50 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f561c6c9e8a9..1d85c42b9c67 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1246,6 +1246,9 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
/* do nothing */
break;
}
+
+ /* de-assert DRVVBUS for HOST and OTG mode */
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
static void dwc3_get_properties(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1c8b349379af..77c4a9abe365 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -688,7 +688,9 @@ struct dwc3_ep {
#define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2)
#define DWC3_EP_TRANSFER_STARTED BIT(3)
+#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
#define DWC3_EP_PENDING_REQUEST BIT(5)
+#define DWC3_EP_DELAY_START BIT(6)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index e56beb9d1e36..4a13ceaf4093 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -256,86 +256,77 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
u8 epnum = event->endpoint_number;
size_t len;
int status;
- int ret;
- ret = snprintf(str, size, "ep%d%s: ", epnum >> 1,
+ len = scnprintf(str, size, "ep%d%s: ", epnum >> 1,
(epnum & 1) ? "in" : "out");
- if (ret < 0)
- return "UNKNOWN";
status = event->status;
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- len = strlen(str);
- snprintf(str + len, size - len, "Transfer Complete (%c%c%c)",
+ len += scnprintf(str + len, size - len,
+ "Transfer Complete (%c%c%c)",
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
status & DEPEVT_STATUS_LST ? 'L' : 'l');
- len = strlen(str);
-
if (epnum <= 1)
- snprintf(str + len, size - len, " [%s]",
+ scnprintf(str + len, size - len, " [%s]",
dwc3_ep0_state_string(ep0state));
break;
case DWC3_DEPEVT_XFERINPROGRESS:
- len = strlen(str);
-
- snprintf(str + len, size - len, "Transfer In Progress [%d] (%c%c%c)",
+ scnprintf(str + len, size - len,
+ "Transfer In Progress [%d] (%c%c%c)",
event->parameters,
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
status & DEPEVT_STATUS_LST ? 'M' : 'm');
break;
case DWC3_DEPEVT_XFERNOTREADY:
- len = strlen(str);
-
- snprintf(str + len, size - len, "Transfer Not Ready [%d]%s",
+ len += scnprintf(str + len, size - len,
+ "Transfer Not Ready [%d]%s",
event->parameters,
status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
" (Active)" : " (Not Active)");
- len = strlen(str);
-
/* Control Endpoints */
if (epnum <= 1) {
int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status);
switch (phase) {
case DEPEVT_STATUS_CONTROL_DATA:
- snprintf(str + ret, size - ret,
+ scnprintf(str + len, size - len,
" [Data Phase]");
break;
case DEPEVT_STATUS_CONTROL_STATUS:
- snprintf(str + ret, size - ret,
+ scnprintf(str + len, size - len,
" [Status Phase]");
}
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- snprintf(str + ret, size - ret, "FIFO");
+ scnprintf(str + len, size - len, "FIFO");
break;
case DWC3_DEPEVT_STREAMEVT:
status = event->status;
switch (status) {
case DEPEVT_STREAMEVT_FOUND:
- snprintf(str + ret, size - ret, " Stream %d Found",
+ scnprintf(str + len, size - len, " Stream %d Found",
event->parameters);
break;
case DEPEVT_STREAMEVT_NOTFOUND:
default:
- snprintf(str + ret, size - ret, " Stream Not Found");
+ scnprintf(str + len, size - len, " Stream Not Found");
break;
}
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- snprintf(str + ret, size - ret, "Endpoint Command Complete");
+ scnprintf(str + len, size - len, "Endpoint Command Complete");
break;
default:
- snprintf(str, size, "UNKNOWN");
+ scnprintf(str + len, size - len, "UNKNOWN");
}
return str;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index fd1b100d2927..6dee4dabc0a4 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1136,8 +1136,10 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
case DWC3_DEPEVT_EPCMDCMPLT:
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
- if (cmd == DWC3_DEPCMD_ENDTRANSFER)
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ }
break;
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 154f3f3e8cff..1e00bf2d65a2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -57,7 +57,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
return -EINVAL;
}
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
return 0;
}
@@ -111,6 +111,9 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ /* set no action before sending new link state change */
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
/* set requested state */
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -1068,7 +1071,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
unsigned int rem = length % maxp;
unsigned chain = true;
- if (sg_is_last(s))
+ /*
+ * IOMMU driver is coalescing the list of sgs which shares a
+ * page boundary into one and giving it to USB driver. With
+ * this the number of sgs mapped is not equal to the number of
+ * sgs passed. So mark the chain bit to false if it isthe last
+ * mapped sg.
+ */
+ if (i == remaining - 1)
chain = false;
if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
@@ -1447,6 +1457,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
+ /* Start the transfer only after the END_TRANSFER is completed */
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
+ dep->flags |= DWC3_EP_DELAY_START;
+ return 0;
+ }
+
/*
* NOTICE: Isochronous endpoints should NEVER be prestarted. We must
* wait for a XferNotReady event so we will know what's the current
@@ -1828,7 +1844,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
dwc->pullups_connected = false;
}
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
do {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -2420,7 +2436,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
if (event->status & DEPEVT_STATUS_SHORT && !chain)
return 1;
- if (event->status & DEPEVT_STATUS_IOC)
+ if ((trb->ctrl & DWC3_TRB_CTRL_IOC) ||
+ (trb->ctrl & DWC3_TRB_CTRL_LST))
return 1;
return 0;
@@ -2625,8 +2642,14 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+ if ((dep->flags & DWC3_EP_DELAY_START) &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
+
+ dep->flags &= ~DWC3_EP_DELAY_START;
}
break;
case DWC3_DEPEVT_STREAMEVT:
@@ -2678,12 +2701,12 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
- struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
+ (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
/*
@@ -2693,16 +2716,13 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
* much trouble synchronizing between us and gadget driver.
*
* We have discussed this with the IP Provider and it was
- * suggested to giveback all requests here, but give HW some
- * extra time to synchronize with the interconnect. We're using
- * an arbitrary 100us delay for that.
+ * suggested to giveback all requests here.
*
* Note also that a similar handling was tested by Synopsys
* (thanks a lot Paul) and nothing bad has come out of it.
- * In short, what we're doing is:
- *
- * - Issue EndTransfer WITH CMDIOC bit set
- * - Wait 100us
+ * In short, what we're doing is issuing EndTransfer with
+ * CMDIOC bit set and delay kicking transfer until the
+ * EndTransfer command had completed.
*
* As of IP version 3.10a of the DWC_usb3 IP, the controller
* supports a mode to work around the above limitation. The
@@ -2711,8 +2731,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
* by writing GUCTL2[14]. This polling is already done in the
* dwc3_send_gadget_ep_cmd() function so if the mode is
* enabled, the EndTransfer command will have completed upon
- * returning from this function and we don't need to delay for
- * 100us.
+ * returning from this function.
*
* This mode is NOT available on the DWC_usb31 IP.
*/
@@ -2728,9 +2747,8 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
-
- if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
- udelay(100);
+ else
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
}
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
@@ -2759,12 +2777,12 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
int reg;
+ dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
dwc3_disconnect_gadget(dwc);
@@ -2816,7 +2834,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
dwc3_clear_stall_all_ep(dwc);
@@ -2920,11 +2938,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
}
dep = dwc->eps[0];
@@ -3033,7 +3051,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
reg &= ~u1u2;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 5faf4d1249e0..fbc7d8013f0b 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -127,4 +127,18 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
+/**
+ * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change
+ * @dwc: pointer to our context structure
+ * @value: value to write to DCTL
+ *
+ * Use this function when doing read-modify-write to DCTL. It will not
+ * send link state change request.
+ */
+static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
+{
+ value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, value);
+}
+
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 31/64] usb: dwc3: import from kernel v5.7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (29 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 30/64] usb: dwc3: import from kernel v5.6 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 32/64] usb: dwc3: import from kernel v5.8 Jens Wiklander
` (36 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.6 to v5.7.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.6..v5.7
Commits imported:
4748d396399a usb: dwc3: select USB_ROLE_SWITCH
066c09593454 usb: dwc3: pci: Enable extcon driver for Intel Merrifield
00e21763f2c8 dwc3: Remove check for HWO flag in dwc3_gadget_ep_reclaim_trb_sg()
49e0590e3a60 usb: dwc3: gadget: Fix request completion check
d0550cd20e52 usb: dwc3: gadget: Do link recovery for SS and SSP
d94ea5319813 usb: dwc3: gadget: Properly set maxpacket limit
586f4335700f usb: dwc3: Fix GTXFIFOSIZ.TXFDEP macro name
0339f7fbc82e usb: dwc3: fix up for role switch API change
a8ab3e76297e Merge tag 'usb-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
201c26c08db4 usb: dwc3: qcom: Replace <linux/clk-provider.h> by <linux/of_clk.h>
0227cc84c444 usb: dwc3: core: don't do suspend for device mode if already suspended
4a1d042af420 usb: dwc3: Rework resets initialization to be more flexible
0d3a97083e0c usb: dwc3: Rework clock initialization to be more flexible
98ed256a4dba usb: dwc3: Add support for role-switch-default-mode binding
8a0a13799744 usb: dwc3: Registering a role switch in the DRD code.
7ba6b09fda5e usb: dwc3: core: add support for disabling SS instances in park mode
2dedea035ae8 usb: dwc3: gadget: Wrap around when skip TRBs
a114c4ca64bd usb: dwc3: gadget: Don't clear flags before transfer ended
a33e5d639c9b usb: dwc3: Remove kernel doc annotation where it's not needed
11c39809070f usb: dwc3: Add ACPI support for xHCI ports
07f6842341ab usb: dwc3: exynos: Add support for Exynos5422 suspend clk
238d76021654 usb: dwc3: meson-g12a: Don't use ret uninitialized in dwc3_meson_g12a_otg_init
1e355f21d3fb usb: dwc3: Add Amlogic A1 DWC3 glue
30ad6273adad usb: dwc3: trace: print enqueue/dequeue pointers too
8e567ed9e244 Merge 5.6-rc5 into usb-next
bce3052f0c16 usb: roles: Provide the switch drivers handle to the switch in the API
24e6aea4801b Merge 5.6-rc3 into usb-next
882f7a4dae1d usb: dwc3: qcom: Replace <linux/clk-provider.h> by <linux/of_clk.h>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 29 ++++++-------
drivers/usb/dwc3/core.h | 18 ++++++++-
drivers/usb/dwc3/gadget.c | 85 ++++++++++++++++++++++-----------------
3 files changed, 78 insertions(+), 54 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 1d85c42b9c67..edc17155cb2b 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -289,12 +289,6 @@ done:
return 0;
}
-static const struct clk_bulk_data dwc3_core_clks[] = {
- { .id = "ref" },
- { .id = "bus_early" },
- { .id = "suspend" },
-};
-
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
@@ -1029,6 +1023,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->dis_tx_ipgap_linecheck_quirk)
reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+ if (dwc->parkmode_disable_ss_quirk)
+ reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@@ -1342,6 +1339,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,dis-del-phy-power-chg-quirk");
dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
"snps,dis-tx-ipgap-linecheck-quirk");
+ dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
+ "snps,parkmode-disable-ss-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
@@ -1441,11 +1440,6 @@ static int dwc3_probe(struct platform_device *pdev)
if (!dwc)
return -ENOMEM;
- dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks),
- GFP_KERNEL);
- if (!dwc->clks)
- return -ENOMEM;
-
dwc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1476,22 +1470,23 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
- dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
+ dwc->reset = devm_reset_control_array_get(dev, true, true);
if (IS_ERR(dwc->reset))
return PTR_ERR(dwc->reset);
if (dev->of_node) {
- dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
-
- ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ ret = devm_clk_bulk_get_all(dev, &dwc->clks);
if (ret == -EPROBE_DEFER)
return ret;
/*
* Clocks are optional, but new DT platforms should support all
* clocks as required by the DT-binding.
*/
- if (ret)
+ if (ret < 0)
dwc->num_clks = 0;
+ else
+ dwc->num_clks = ret;
+
}
ret = reset_control_deassert(dwc->reset);
@@ -1637,6 +1632,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ if (pm_runtime_suspended(dwc->dev))
+ break;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 77c4a9abe365..4c171a8e215f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -25,6 +25,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@@ -249,6 +250,7 @@
#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
/* Global User Control 1 Register */
+#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
@@ -305,10 +307,14 @@
/* Global TX Fifo Size Register */
#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
-#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
+#define DWC31_GTXFIFOSIZ_TXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
+#define DWC3_GTXFIFOSIZ_TXFDEP(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+/* Global RX Fifo Size Register */
+#define DWC31_GRXFIFOSIZ_RXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
+#define DWC3_GRXFIFOSIZ_RXFDEP(n) ((n) & 0xffff)
+
/* Global Event Size Registers */
#define DWC3_GEVNTSIZ_INTMASK BIT(31)
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
@@ -953,6 +959,9 @@ struct dwc3_scratchpad_array {
* @hsphy_mode: UTMI phy mode, one of following:
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
+ * @role_sw: usb_role_switch handle
+ * @role_switch_default_mode: default operation mode of controller while
+ * usb role is USB_ROLE_NONE.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -1024,6 +1033,8 @@ struct dwc3_scratchpad_array {
* change quirk.
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
* check during HS transmit.
+ * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
+ * instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -1086,6 +1097,8 @@ struct dwc3 {
struct extcon_dev *edev;
struct notifier_block edev_nb;
enum usb_phy_interface hsphy_mode;
+ struct usb_role_switch *role_sw;
+ enum usb_dr_mode role_switch_default_mode;
u32 fladj;
u32 irq_gadget;
@@ -1215,6 +1228,7 @@ struct dwc3 {
unsigned dis_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1;
unsigned dis_tx_ipgap_linecheck_quirk:1;
+ unsigned parkmode_disable_ss_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1e00bf2d65a2..585cb3deea7a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1521,7 +1521,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
for (i = 0; i < req->num_trbs; i++) {
struct dwc3_trb *trb;
- trb = req->trb + i;
+ trb = &dep->trb_pool[dep->trb_dequeue];
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
}
@@ -1728,7 +1728,6 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
u32 reg;
u8 link_state;
- u8 speed;
/*
* According to the Databook Remote wakeup request should
@@ -1738,16 +1737,13 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
*/
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
- speed = reg & DWC3_DSTS_CONNECTSPD;
- if ((speed == DWC3_DSTS_SUPERSPEED) ||
- (speed == DWC3_DSTS_SUPERSPEED_PLUS))
- return 0;
-
link_state = DWC3_DSTS_USBLNKST(reg);
switch (link_state) {
+ case DWC3_LINK_STATE_RESET:
case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
+ case DWC3_LINK_STATE_RESUME:
break;
default:
return -EINVAL;
@@ -2227,7 +2223,6 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
int mdwidth;
- int kbytes;
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
@@ -2236,24 +2231,24 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
if (dwc3_is_usb31(dwc))
- size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ size = DWC31_GTXFIFOSIZ_TXFDEP(size);
else
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ size = DWC3_GTXFIFOSIZ_TXFDEP(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
- kbytes = size / 1024;
- if (kbytes == 0)
- kbytes = 1;
-
/*
- * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
- * internal overhead. We don't really know how these are used,
- * but documentation say it exists.
+ * To meet performance requirement, a minimum TxFIFO size of 3x
+ * MaxPacketSize is recommended for endpoints that support burst and a
+ * minimum TxFIFO size of 2x MaxPacketSize for endpoints that don't
+ * support burst. Use those numbers and we can calculate the max packet
+ * limit as below.
*/
- size -= mdwidth * (kbytes + 1);
- size /= kbytes;
+ if (dwc->maximum_speed >= USB_SPEED_SUPER)
+ size /= 3;
+ else
+ size /= 2;
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
@@ -2271,8 +2266,39 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
+ int mdwidth;
+ int size;
+
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
- usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
+ /* MDWIDTH is represented in bits, convert to bytes */
+ mdwidth /= 8;
+
+ /* All OUT endpoints share a single RxFIFO space */
+ size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GRXFIFOSIZ_RXFDEP(size);
+ else
+ size = DWC3_GRXFIFOSIZ_RXFDEP(size);
+
+ /* FIFO depth is in MDWDITH bytes */
+ size *= mdwidth;
+
+ /*
+ * To meet performance requirement, a minimum recommended RxFIFO size
+ * is defined as follow:
+ * RxFIFO size >= (3 x MaxPacketSize) +
+ * (3 x 8 bytes setup packets size) + (16 bytes clock crossing margin)
+ *
+ * Then calculate the max packet limit as below.
+ */
+ size -= (3 * 8) + 16;
+ if (size < 0)
+ size = 0;
+ else
+ size /= 3;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
dep->endpoint.max_streams = 15;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
@@ -2457,9 +2483,6 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
for_each_sg(sg, s, pending, i) {
trb = &dep->trb_pool[dep->trb_dequeue];
- if (trb->ctrl & DWC3_TRB_CTRL_HWO)
- break;
-
req->sg = sg_next(s);
req->num_pending_sgs--;
@@ -2484,14 +2507,7 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
{
- /*
- * For OUT direction, host may send less than the setup
- * length. Return true for all OUT requests.
- */
- if (!req->direction)
- return true;
-
- return req->request.actual == req->request.length;
+ return req->num_pending_sgs == 0;
}
static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
@@ -2515,8 +2531,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->request.actual = req->request.length - req->remaining;
- if (!dwc3_gadget_ep_request_completed(req) ||
- req->num_pending_sgs) {
+ if (!dwc3_gadget_ep_request_completed(req)) {
__dwc3_gadget_kick_transfer(dep);
goto out;
}
@@ -2570,10 +2585,8 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
- if (stop) {
+ if (stop)
dwc3_stop_active_transfer(dep, true, true);
- dep->flags = DWC3_EP_ENABLED;
- }
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 32/64] usb: dwc3: import from kernel v5.8
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (30 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 31/64] usb: dwc3: import from kernel v5.7 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 33/64] usb: dwc3: import from kernel v5.9 Jens Wiklander
` (35 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.7 to v5.8.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.7..v5.8
Commits imported:
e25d1e8532c3 usb: dwc3: pci: add support for the Intel Jasper Lake
c3f595a81192 usb: dwc3: pci: add support for the Intel Tiger Lake PCH -H variant
cd37c6976f6a Revert "usb: dwc3: exynos: Add support for Exynos5422 suspend clk"
2655971ad4b3 usb: dwc3: pci: Fix reference count leak in dwc3_pci_resume_work
347052e3bf1b usb: dwc3: meson-g12a: fix USB2 PHY initialization on G12A and A1 SoCs
be8c1001a7e6 usb: dwc3: meson-g12a: fix error path when fetching the reset line fails
ca681aa49200 Merge tag 'usb-for-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
1c11e74e9079 usb: dwc3: keystone: Turn on USB3 PHY before controller
63c7bb299fc9 usb: dwc3: gadget: Check for prepared TRBs
1c0e69ae1b9f usb: dwc3: Increase timeout for CmdAct cleared by device controller
b10e1c253577 usb: dwc3: gadget: Use SET_EP_PRIME for NoStream
140ca4cfea8a usb: dwc3: gadget: Handle stream transfers
aefe3d232b66 usb: dwc3: gadget: Don't prepare beyond a transfer
e0d19563eb6c usb: dwc3: gadget: Wait for transfer completion
3eaecd0c2333 usb: dwc3: gadget: Handle XferComplete for streams
548f8b316563 usb: dwc3: gadget: Enable XferComplete event
2e6e9e4b2ed7 usb: dwc3: gadget: Refactor TRB completion handler
b6842d4938c3 usb: dwc3: gadget: Check for in-progress END_TRANSFER
4244ba02edb8 usb: dwc3: Get MDWIDTH for DWC_usb32
9af21dd6faeb usb: dwc3: Add support for DWC_usb32 IP
c685114f63b1 usb: dwc3: use true,false for dwc->otg_restart_host
f4cc91ddd856 usb: dwc3: of-simple: remove Amlogic GXL and AXG compatibles
a9fc15e0fd78 usb: dwc3: meson-g12a: add support for GXL and GXM SoCs
df7e37458151 usb: dwc3: meson-g12a: support the GXL/GXM DWC3 host phy disconnect
5b0ba0caaf3a usb: dwc3: meson-g12a: refactor usb init
d9feef974e0d usb: dwc3: gadget: Continue to process pending requests
48a789079a14 Merge 5.7-rc6 into usb-next
f7ac582effc6 usb: dwc3: gadget: WARN on no-resource status
36f05d36b035 usb: dwc3: gadget: Issue END_TRANSFER to retry isoc transfer
9bc3395c2496 usb: dwc3: gadget: Store resource index of start cmd
8d99087c2db8 usb: dwc3: gadget: Properly handle failed kick_transfer
8bb14308a869 usb: dwc3: core: Use role-switch default dr_mode
8cc6d55bc200 usb: dwc3: drd: Don't free non-existing irq
31306821d877 usb: dwc3: meson-g12a: refactor usb2 phy init
8f5bc1ec770c usb: dwc3: meson-g12a: check return of dwc3_meson_g12a_usb_init
6d9fa35a347a usb: dwc3: meson-g12a: get the reset as shared
013af227f58a usb: dwc3: meson-g12a: handle the phy and glue registers separately
5174564cb915 usb: dwc3: meson-g12a: specify phy names in soc data
fcd2def66392 usb: dwc3: gadget: Refactor dwc3_gadget_ep_dequeue
8411993e79df usb: dwc3: gadget: Remove unnecessary checks
a7027ca69d82 usb: dwc3: gadget: Give back staled requests
cb11ea56f37a usb: dwc3: gadget: Properly handle ClearFeature(halt)
c2cd3452d5f8 usb: dwc3: support continuous runtime PM with dual role
b33f69f56352 USB: dwc3: Use the correct style for SPDX License Identifier
f6402eb4a2b3 Merge 5.7-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 62 ++---
drivers/usb/dwc3/core.h | 83 ++++---
drivers/usb/dwc3/debug.h | 4 +-
drivers/usb/dwc3/gadget.c | 469 +++++++++++++++++++++++++++++---------
drivers/usb/dwc3/gadget.h | 2 +-
drivers/usb/dwc3/io.h | 2 +-
6 files changed, 452 insertions(+), 170 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index edc17155cb2b..25c686a752b0 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -85,7 +85,9 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
* specified or set to OTG, then set the mode to peripheral.
*/
if (mode == USB_DR_MODE_OTG &&
- dwc->revision >= DWC3_REVISION_330A)
+ (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
+ !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
+ !DWC3_VER_IS_PRIOR(DWC3, 330A))
mode = USB_DR_MODE_PERIPHERAL;
}
@@ -121,17 +123,19 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
+ pm_runtime_get_sync(dwc->dev);
+
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
dwc3_otg_update(dwc, 0);
if (!dwc->desired_dr_role)
- return;
+ goto out;
if (dwc->desired_dr_role == dwc->current_dr_role)
- return;
+ goto out;
if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
- return;
+ goto out;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
@@ -190,6 +194,9 @@ static void __dwc3_set_mode(struct work_struct *work)
break;
}
+out:
+ pm_runtime_mark_last_busy(dwc->dev);
+ pm_runtime_put_autosuspend(dwc->dev);
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -257,7 +264,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
* take a little more than 50ms. Set the polling rate at 20ms
* for 10 times instead.
*/
- if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
+ if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
retries = 10;
do {
@@ -265,8 +272,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
- if (dwc3_is_usb31(dwc) &&
- dwc->revision >= DWC3_USB31_REVISION_190A)
+ if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
msleep(20);
else
udelay(1);
@@ -283,7 +289,7 @@ done:
* is cleared, we must wait at least 50ms before accessing the PHY
* domain (synchronization delay).
*/
- if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
+ if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
msleep(50);
return 0;
@@ -298,7 +304,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
u32 reg;
u32 dft;
- if (dwc->revision < DWC3_REVISION_250A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
if (dwc->fladj == 0)
@@ -579,7 +585,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
* will be '0' when the core is reset. Application needs to set it
* to '1' after the core initialization is completed.
*/
- if (dwc->revision > DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
/*
@@ -670,7 +676,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
* be '0' when the core is reset. Application needs to set it to
* '1' after the core initialization is completed.
*/
- if (dwc->revision > DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
/*
@@ -719,15 +725,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ dwc->ip = DWC3_GSNPS_ID(reg);
/* This should read as U3 followed by revision number */
- if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
- /* Detected DWC_usb3 IP */
+ if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg;
- } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
- /* Detected DWC_usb31 IP */
+ } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
- dwc->revision |= DWC3_REVISION_IS_DWC31;
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
} else {
return false;
@@ -760,8 +764,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
*/
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
dwc->dr_mode == USB_DR_MODE_OTG) &&
- (dwc->revision >= DWC3_REVISION_210A &&
- dwc->revision <= DWC3_REVISION_250A))
+ DWC3_VER_IS_WITHIN(DWC3, 210A, 250A))
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
else
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
@@ -804,7 +807,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
- if (dwc->revision < DWC3_REVISION_190A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -957,7 +960,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err0a;
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
- dwc->revision > DWC3_REVISION_194A) {
+ !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
if (!dwc->dis_u3_susphy_quirk) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
@@ -1004,20 +1007,20 @@ static int dwc3_core_init(struct dwc3 *dwc)
* the DWC_usb3 controller. It is NOT available in the
* DWC_usb31 controller.
*/
- if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+ if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
- if (dwc->revision >= DWC3_REVISION_250A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
/*
* Enable hardware control of sending remote wakeup
* in HS when the device is in the L1 state.
*/
- if (dwc->revision >= DWC3_REVISION_290A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
if (dwc->dis_tx_ipgap_linecheck_quirk)
@@ -1049,7 +1052,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
*/
- if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
u8 rx_maxburst = dwc->rx_max_burst_prd;
u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
@@ -1371,10 +1374,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
/* check whether the core supports IMOD */
bool dwc3_has_imod(struct dwc3 *dwc)
{
- return ((dwc3_is_usb3(dwc) &&
- dwc->revision >= DWC3_REVISION_300A) ||
- (dwc3_is_usb31(dwc) &&
- dwc->revision >= DWC3_USB31_REVISION_120A));
+ return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
+ DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
+ DWC3_IP_IS(DWC32);
}
static void dwc3_check_params(struct dwc3 *dwc)
@@ -1395,7 +1397,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
* affected version.
*/
if (!dwc->imod_interval &&
- (dwc->revision == DWC3_REVISION_300A))
+ DWC3_VER_IS(DWC3, 300A))
dwc->imod_interval = 1;
/* Check the maximum_speed parameter */
@@ -1417,7 +1419,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
/*
* default to superspeed plus if we are capable.
*/
- if (dwc3_is_usb31(dwc) &&
+ if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) &&
(DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4c171a8e215f..013f42a2b5dc 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* core.h - DesignWare USB3 DRD Core Header
*
@@ -69,6 +69,7 @@
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
+#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)
/* DWC3 registers memory space boundries */
#define DWC3_XHCI_REGS_START 0x0
@@ -365,6 +366,9 @@
#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
+/* DWC_usb32 only */
+#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8))
+
/* Global HWPARAMS7 Register */
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
@@ -491,6 +495,7 @@
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
+#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
@@ -697,6 +702,10 @@ struct dwc3_ep {
#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
#define DWC3_EP_PENDING_REQUEST BIT(5)
#define DWC3_EP_DELAY_START BIT(6)
+#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
+#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
+#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
+#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -949,7 +958,8 @@ struct dwc3_scratchpad_array {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
- * @revision: revision register contents
+ * @ip: controller's ID
+ * @revision: controller's version of an IP
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
* @current_dr_role: current role of operation when in dual-role mode
@@ -1110,15 +1120,15 @@ struct dwc3 {
u32 u1u2;
u32 maximum_speed;
- /*
- * All 3.1 IP version constants are greater than the 3.0 IP
- * version constants. This works for most version checks in
- * dwc3. However, in the future, this may not apply as
- * features may be developed on newer versions of the 3.0 IP
- * that are not in the 3.1 IP.
- */
+ u32 ip;
+
+#define DWC3_IP 0x5533
+#define DWC31_IP 0x3331
+#define DWC32_IP 0x3332
+
u32 revision;
+#define DWC3_REVISION_ANY 0x0
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
#define DWC3_REVISION_180A 0x5533180a
@@ -1143,20 +1153,20 @@ struct dwc3 {
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_330A 0x5533330a
-/*
- * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
- * just so dwc31 revisions are always larger than dwc3.
- */
-#define DWC3_REVISION_IS_DWC31 0x80000000
-#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
+#define DWC31_REVISION_ANY 0x0
+#define DWC31_REVISION_110A 0x3131302a
+#define DWC31_REVISION_120A 0x3132302a
+#define DWC31_REVISION_160A 0x3136302a
+#define DWC31_REVISION_170A 0x3137302a
+#define DWC31_REVISION_180A 0x3138302a
+#define DWC31_REVISION_190A 0x3139302a
+
+#define DWC32_REVISION_ANY 0x0
+#define DWC32_REVISION_100A 0x3130302a
u32 version_type;
+#define DWC31_VERSIONTYPE_ANY 0x0
#define DWC31_VERSIONTYPE_EA01 0x65613031
#define DWC31_VERSIONTYPE_EA02 0x65613032
#define DWC31_VERSIONTYPE_EA03 0x65613033
@@ -1298,6 +1308,10 @@ struct dwc3_event_depevt {
#define DEPEVT_STREAMEVT_FOUND 1
#define DEPEVT_STREAMEVT_NOTFOUND 2
+/* Stream event parameter */
+#define DEPEVT_STREAM_PRIME 0xfffe
+#define DEPEVT_STREAM_NOSTREAM 0x0
+
/* Control-only Status */
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
@@ -1400,17 +1414,26 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
-/* check whether we are on the DWC_usb3 core */
-static inline bool dwc3_is_usb3(struct dwc3 *dwc)
-{
- return !(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_IP_IS(_ip) \
+ (dwc->ip == _ip##_IP)
-/* check whether we are on the DWC_usb31 core */
-static inline bool dwc3_is_usb31(struct dwc3 *dwc)
-{
- return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_VER_IS(_ip, _ver) \
+ (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_PRIOR(_ip, _ver) \
+ (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \
+ (DWC3_IP_IS(_ip) && \
+ dwc->revision >= _ip##_REVISION_##_from && \
+ (!(_ip##_REVISION_##_to) || \
+ dwc->revision <= _ip##_REVISION_##_to))
+
+#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \
+ (DWC3_VER_IS(_ip, _ver) && \
+ dwc->version_type >= _ip##_VERSIONTYPE_##_from && \
+ (!(_ip##_VERSIONTYPE_##_to) || \
+ dwc->version_type <= _ip##_VERSIONTYPE_##_to))
bool dwc3_has_imod(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 4a13ceaf4093..d8f600e0e88f 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
@@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)
return "All FIFO Flush";
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
return "Set Endpoint NRDY";
+ case DWC3_DGCMD_SET_ENDPOINT_PRIME:
+ return "Set Endpoint Prime";
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
return "Run SoC Bus Loopback Test";
default:
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 585cb3deea7a..80c3ef134e41 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -95,7 +95,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
* Wait until device controller is ready. Only applies to 1.94a and
* later RTL.
*/
- if (dwc->revision >= DWC3_REVISION_194A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD)
@@ -122,7 +122,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
* The following code is racy when called from dwc3_gadget_wakeup,
* and is not needed, at least on newer versions
*/
- if (dwc->revision >= DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
return 0;
/* wait for a change in DSTS */
@@ -273,7 +273,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
- u32 timeout = 1000;
+ u32 timeout = 5000;
u32 saved_config = 0;
u32 reg;
@@ -356,6 +356,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
ret = 0;
break;
case DEPEVT_TRANSFER_NO_RESOURCE:
+ dev_WARN(dwc->dev, "No resource for %s\n",
+ dep->name);
ret = -EINVAL;
break;
case DEPEVT_TRANSFER_BUS_EXPIRY:
@@ -387,9 +389,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
- if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- dep->flags |= DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_get_transfer_index(dep);
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ if (ret == 0)
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+
+ if (ret != -ETIMEDOUT)
+ dwc3_gadget_ep_get_transfer_index(dep);
}
if (saved_config) {
@@ -415,7 +420,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
* IN transfers due to a mishandled error condition. Synopsys
* STAR 9000614252.
*/
- if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
+ if (dep->direction &&
+ !DWC3_VER_IS_PRIOR(DWC3, 260A) &&
(dwc->gadget.speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
@@ -573,6 +579,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+ | DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_STREAM_EVENT_EN;
dep->stream_capable = true;
}
@@ -603,6 +610,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
}
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt);
+
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
@@ -663,7 +673,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
+ if (usb_endpoint_xfer_bulk(desc) ||
usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
@@ -682,6 +692,29 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret < 0)
return ret;
+
+ if (dep->stream_capable) {
+ /*
+ * For streams, at start, there maybe a race where the
+ * host primes the endpoint before the function driver
+ * queues a request to initiate a stream. In that case,
+ * the controller will not see the prime to generate the
+ * ERDY and start stream. To workaround this, issue a
+ * no-op TRB as normal, but end it immediately. As a
+ * result, when the function driver queues the request,
+ * the next START_TRANSFER command will cause the
+ * controller to generate an ERDY to initiate the
+ * stream.
+ */
+ dwc3_stop_active_transfer(dep, true, true);
+
+ /*
+ * All stream eps will reinitiate stream on NoStream
+ * rejection until we can determine that the host can
+ * prime after the first transfer.
+ */
+ dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+ }
}
out:
@@ -690,8 +723,6 @@ out:
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
- bool interrupt);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
@@ -912,7 +943,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
- unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
+ unsigned stream_id, unsigned short_not_ok,
+ unsigned no_interrupt, unsigned is_last)
{
struct dwc3 *dwc = dep->dwc;
struct usb_gadget *gadget = &dwc->gadget;
@@ -1005,6 +1037,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
+ else if (dep->stream_capable && is_last)
+ trb->ctrl |= DWC3_TRB_CTRL_LST;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
@@ -1032,6 +1066,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
unsigned stream_id = req->request.stream_id;
unsigned short_not_ok = req->request.short_not_ok;
unsigned no_interrupt = req->request.no_interrupt;
+ unsigned is_last = req->request.is_last;
if (req->request.num_sgs > 0) {
length = sg_dma_len(req->start_sg);
@@ -1052,7 +1087,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
- stream_id, short_not_ok, no_interrupt);
+ stream_id, short_not_ok, no_interrupt, is_last);
}
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
@@ -1097,7 +1132,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
maxp - rem, false, 1,
req->request.stream_id,
req->request.short_not_ok,
- req->request.no_interrupt);
+ req->request.no_interrupt,
+ req->request.is_last);
} else {
dwc3_prepare_one_trb(dep, req, chain, i);
}
@@ -1141,7 +1177,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
false, 1, req->request.stream_id,
req->request.short_not_ok,
- req->request.no_interrupt);
+ req->request.no_interrupt,
+ req->request.is_last);
} else if (req->request.zero && req->request.length &&
(IS_ALIGNED(req->request.length, maxp))) {
struct dwc3 *dwc = dep->dwc;
@@ -1158,7 +1195,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 1, req->request.stream_id,
req->request.short_not_ok,
- req->request.no_interrupt);
+ req->request.no_interrupt,
+ req->request.is_last);
} else {
dwc3_prepare_one_trb(dep, req, false, 0);
}
@@ -1194,6 +1232,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return;
+
+ /*
+ * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+ * burst capability may try to read and use TRBs beyond the
+ * active transfer instead of stopping.
+ */
+ if (dep->stream_capable && req->request.is_last)
+ return;
}
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
@@ -1217,9 +1263,19 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return;
+
+ /*
+ * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+ * burst capability may try to read and use TRBs beyond the
+ * active transfer instead of stopping.
+ */
+ if (dep->stream_capable && req->request.is_last)
+ return;
}
}
+static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
+
static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
@@ -1259,17 +1315,26 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret < 0) {
- /*
- * FIXME we need to iterate over the list of requests
- * here and stop, unmap, free and del each of the linked
- * requests instead of what we do now.
- */
- if (req->trb)
- memset(req->trb, 0, sizeof(struct dwc3_trb));
- dwc3_gadget_del_and_unmap_request(dep, req, ret);
+ struct dwc3_request *tmp;
+
+ if (ret == -EAGAIN)
+ return ret;
+
+ dwc3_stop_active_transfer(dep, true, true);
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req);
+
+ /* If ep isn't started, then there's no end transfer pending */
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
return ret;
}
+ if (dep->stream_capable && req->request.is_last)
+ dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
return 0;
}
@@ -1402,17 +1467,15 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
int ret;
int i;
- if (list_empty(&dep->pending_list)) {
+ if (list_empty(&dep->pending_list) &&
+ list_empty(&dep->started_list)) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
return -EAGAIN;
}
- if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
- (dwc->revision <= DWC3_USB31_REVISION_160A ||
- (dwc->revision == DWC3_USB31_REVISION_170A &&
- dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
- dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
-
+ if (!dwc->dis_start_transfer_quirk &&
+ (DWC3_VER_IS_PRIOR(DWC31, 170A) ||
+ DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
return dwc3_gadget_start_isoc_quirk(dep);
}
@@ -1425,6 +1488,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
break;
}
+ /*
+ * After a number of unsuccessful start attempts due to bus-expiry
+ * status, issue END_TRANSFER command and retry on the next XferNotReady
+ * event.
+ */
+ if (ret == -EAGAIN) {
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER |
+ DWC3_DEPCMD_CMDIOC |
+ DWC3_DEPCMD_PARAM(dep->resource_index);
+
+ dep->resource_index = 0;
+ memset(¶ms, 0, sizeof(params));
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ if (!ret)
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ }
+
return ret;
}
@@ -1457,6 +1541,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
+ if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
+ return 0;
+
/* Start the transfer only after the END_TRANSFER is completed */
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
dep->flags |= DWC3_EP_DELAY_START;
@@ -1508,6 +1595,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
{
int i;
+ /* If req->trb is not set, then the request has not started */
+ if (!req->trb)
+ return;
+
/*
* If request was already started, this means we had to
* stop the transfer. With that we also need to ignore
@@ -1556,39 +1647,40 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
spin_lock_irqsave(&dwc->lock, flags);
- list_for_each_entry(r, &dep->pending_list, list) {
+ list_for_each_entry(r, &dep->cancelled_list, list) {
if (r == req)
- break;
+ goto out;
}
- if (r != req) {
- list_for_each_entry(r, &dep->started_list, list) {
- if (r == req)
- break;
+ list_for_each_entry(r, &dep->pending_list, list) {
+ if (r == req) {
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ goto out;
}
+ }
+
+ list_for_each_entry(r, &dep->started_list, list) {
if (r == req) {
+ struct dwc3_request *t;
+
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
- if (!r->trb)
- goto out0;
+ /*
+ * Remove any started request if the transfer is
+ * cancelled.
+ */
+ list_for_each_entry_safe(r, t, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(r);
- dwc3_gadget_move_cancelled_request(req);
- if (dep->flags & DWC3_EP_TRANSFER_STARTED)
- goto out0;
- else
- goto out1;
+ goto out;
}
- dev_err(dwc->dev, "request %pK was not queued to %s\n",
- request, ep->name);
- ret = -EINVAL;
- goto out0;
}
-out1:
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
-
-out0:
+ dev_err(dwc->dev, "request %pK was not queued to %s\n",
+ request, ep->name);
+ ret = -EINVAL;
+out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1598,6 +1690,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
int ret;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -1634,13 +1728,37 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
+ /*
+ * Don't issue CLEAR_STALL command to control endpoints. The
+ * controller automatically clears the STALL when it receives
+ * the SETUP token.
+ */
+ if (dep->number <= 1) {
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ return 0;
+ }
ret = dwc3_send_clear_stall_ep_cmd(dep);
- if (ret)
+ if (ret) {
dev_err(dwc->dev, "failed to clear STALL on %s\n",
dep->name);
- else
- dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ return ret;
+ }
+
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+
+ dwc3_stop_active_transfer(dep, true, true);
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req);
+
+ list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
+ dwc3_gadget_move_cancelled_request(req);
+
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
+ dep->flags &= ~DWC3_EP_DELAY_START;
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+ }
}
return ret;
@@ -1756,7 +1874,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
}
/* Recent versions do this automatically */
- if (dwc->revision < DWC3_REVISION_194A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
@@ -1818,12 +1936,12 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
- if (dwc->revision <= DWC3_REVISION_187A) {
+ if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= DWC3_DCTL_TRGTULST_RX_DET;
}
- if (dwc->revision >= DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
@@ -1897,7 +2015,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
DWC3_DEVTEN_USBRSTEN |
DWC3_DEVTEN_DISCONNEVTEN);
- if (dwc->revision < DWC3_REVISION_250A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
reg |= DWC3_DEVTEN_ULSTCNGEN;
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
@@ -1942,6 +2060,8 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
nump = min_t(u32, nump, 16);
@@ -1978,10 +2098,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- if (dwc3_is_usb31(dwc))
- reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
- else
+ if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
@@ -2154,7 +2274,7 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
* STAR#9000525659: Clock Domain Crossing on DCTL in
* USB 2.0 Mode
*/
- if (dwc->revision < DWC3_REVISION_220A &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk) {
reg |= DWC3_DCFG_SUPERSPEED;
} else {
@@ -2172,18 +2292,18 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
reg |= DWC3_DCFG_SUPERSPEED;
break;
case USB_SPEED_SUPER_PLUS:
- if (dwc3_is_usb31(dwc))
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- else
+ if (DWC3_IP_IS(DWC3))
reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
break;
default:
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
- if (dwc->revision & DWC3_REVISION_IS_DWC31)
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- else
+ if (DWC3_IP_IS(DWC3))
reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
}
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2226,14 +2346,17 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GTXFIFOSIZ_TXFDEP(size);
- else
+ if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
+ else
+ size = DWC31_GTXFIFOSIZ_TXFDEP(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
@@ -2270,16 +2393,18 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
/* MDWIDTH is represented in bits, convert to bytes */
mdwidth /= 8;
/* All OUT endpoints share a single RxFIFO space */
size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GRXFIFOSIZ_RXFDEP(size);
- else
+ if (DWC3_IP_IS(DWC3))
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
+ else
+ size = DWC31_GRXFIFOSIZ_RXFDEP(size);
/* FIFO depth is in MDWDITH bytes */
size *= mdwidth;
@@ -2531,10 +2656,8 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->request.actual = req->request.length - req->remaining;
- if (!dwc3_gadget_ep_request_completed(req)) {
- __dwc3_gadget_kick_transfer(dep);
+ if (!dwc3_gadget_ep_request_completed(req))
goto out;
- }
dwc3_gadget_giveback(dep, req, status);
@@ -2558,41 +2681,53 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
}
}
+static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+
+ if (!list_empty(&dep->pending_list))
+ return true;
+
+ /*
+ * We only need to check the first entry of the started list. We can
+ * assume the completed requests are removed from the started list.
+ */
+ req = next_request(&dep->started_list);
+ if (!req)
+ return false;
+
+ return !dwc3_gadget_ep_request_completed(req);
+}
+
static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
dep->frame_number = event->parameters;
}
-static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
+static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
{
struct dwc3 *dwc = dep->dwc;
- unsigned status = 0;
- bool stop = false;
-
- dwc3_gadget_endpoint_frame_from_event(dep, event);
-
- if (event->status & DEPEVT_STATUS_BUSERR)
- status = -ECONNRESET;
-
- if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
- status = -EXDEV;
-
- if (list_empty(&dep->started_list))
- stop = true;
- }
+ bool no_started_trb = true;
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
- if (stop)
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+ goto out;
+
+ if (status == -EXDEV && list_empty(&dep->started_list))
dwc3_stop_active_transfer(dep, true, true);
+ else if (dwc3_gadget_ep_should_continue(dep))
+ if (__dwc3_gadget_kick_transfer(dep) == 0)
+ no_started_trb = false;
+out:
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
*/
- if (dwc->revision < DWC3_REVISION_183A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
u32 reg;
int i;
@@ -2603,7 +2738,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
continue;
if (!list_empty(&dep->started_list))
- return;
+ return no_started_trb;
}
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -2612,15 +2747,124 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
dwc->u1u2 = 0;
}
+
+ return no_started_trb;
+}
+
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ int status = 0;
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (event->status & DEPEVT_STATUS_MISSED_ISOC)
+ status = -EXDEV;
+
+ dwc3_gadget_endpoint_trbs_complete(dep, event, status);
+}
+
+static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ int status = 0;
+
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
+ dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
}
static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ /*
+ * The XferNotReady event is generated only once before the endpoint
+ * starts. It will be generated again when END_TRANSFER command is
+ * issued. For some controller versions, the XferNotReady event may be
+ * generated while the END_TRANSFER command is still in process. Ignore
+ * it and wait for the next XferNotReady event after the command is
+ * completed.
+ */
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+ return;
+
(void) __dwc3_gadget_start_isoc(dep);
}
+static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ if (event->status == DEPEVT_STREAMEVT_FOUND) {
+ dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+ goto out;
+ }
+
+ /* Note: NoStream rejection event param value is 0 and not 0xFFFF */
+ switch (event->parameters) {
+ case DEPEVT_STREAM_PRIME:
+ /*
+ * If the host can properly transition the endpoint state from
+ * idle to prime after a NoStream rejection, there's no need to
+ * force restarting the endpoint to reinitiate the stream. To
+ * simplify the check, assume the host follows the USB spec if
+ * it primed the endpoint more than once.
+ */
+ if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
+ if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
+ dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
+ else
+ dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+ }
+
+ break;
+ case DEPEVT_STREAM_NOSTREAM:
+ if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
+ !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
+ !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))
+ break;
+
+ /*
+ * If the host rejects a stream due to no active stream, by the
+ * USB and xHCI spec, the endpoint will be put back to idle
+ * state. When the host is ready (buffer added/updated), it will
+ * prime the endpoint to inform the usb device controller. This
+ * triggers the device controller to issue ERDY to restart the
+ * stream. However, some hosts don't follow this and keep the
+ * endpoint in the idle state. No prime will come despite host
+ * streams are updated, and the device controller will not be
+ * triggered to generate ERDY to move the next stream data. To
+ * workaround this and maintain compatibility with various
+ * hosts, force to reinitate the stream until the host is ready
+ * instead of waiting for the host to prime the endpoint.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
+ unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
+
+ dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
+ } else {
+ dep->flags |= DWC3_EP_DELAY_START;
+ dwc3_stop_active_transfer(dep, true, true);
+ return;
+ }
+ break;
+ }
+
+out:
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+}
+
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
@@ -2665,8 +2909,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_DELAY_START;
}
break;
- case DWC3_DEPEVT_STREAMEVT:
case DWC3_DEPEVT_XFERCOMPLETE:
+ dwc3_gadget_endpoint_transfer_complete(dep, event);
+ break;
+ case DWC3_DEPEVT_STREAMEVT:
+ dwc3_gadget_endpoint_stream_event(dep, event);
+ break;
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
}
@@ -2758,6 +3006,14 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
WARN_ON_ONCE(ret);
dep->resource_index = 0;
+ /*
+ * The END_TRANSFER command will cause the controller to generate a
+ * NoStream Event, and it's not due to the host DP NoStream rejection.
+ * Ignore the next NoStream event.
+ */
+ if (dep->stream_capable)
+ dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else
@@ -2838,7 +3094,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
* STAR#9000466709: RTL: Device : Disconnect event not
* generated if setup packet pending in FIFO
*/
- if (dwc->revision < DWC3_REVISION_188A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 188A)) {
if (dwc->setup_packet_pending)
dwc3_gadget_disconnect_interrupt(dwc);
}
@@ -2897,7 +3153,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* STAR#9000483510: RTL: SS : USB3 reset event may
* not be generated always when the link enters poll
*/
- if (dwc->revision < DWC3_REVISION_190A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 190A))
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -2925,7 +3181,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
/* Enable USB2 LPM Capability */
- if ((dwc->revision > DWC3_REVISION_194A) &&
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -2944,11 +3200,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* BESL value in the LPM token is less than or equal to LPM
* NYET threshold.
*/
- WARN_ONCE(dwc->revision < DWC3_REVISION_240A
- && dwc->has_lpm_erratum,
+ WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
- if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
+ if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -3019,7 +3274,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
* operational mode
*/
pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
- if ((dwc->revision < DWC3_REVISION_250A) &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A) &&
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
@@ -3045,7 +3300,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
* core send LGO_Ux entering U0
*/
- if (dwc->revision < DWC3_REVISION_183A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
if (next == DWC3_LINK_STATE_U0) {
u32 u1u2;
u32 reg;
@@ -3156,7 +3411,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEVICE_EVENT_EOPF:
/* It changed to be suspend event for version 2.30a and above */
- if (dwc->revision >= DWC3_REVISION_230A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
/*
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
@@ -3401,7 +3656,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* is less than super speed because we don't have means, yet, to tell
* composite.c that we are USB 2.0 + LPM ECN.
*/
- if (dwc->revision < DWC3_REVISION_220A &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index fbc7d8013f0b..24dca3872022 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 70acdf94a0bf..9bbe5d4bf076 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* io.h - DesignWare USB3 DRD IO Header
*
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 33/64] usb: dwc3: import from kernel v5.9
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (31 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 32/64] usb: dwc3: import from kernel v5.8 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 34/64] usb: dwc3: import from kernel v5.10 Jens Wiklander
` (34 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.8 to v5.9.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.8..v5.9
Commits imported:
a6498d51821e Revert "usb: dwc3: meson-g12a: fix shared reset control use"
6f0306d1bfbc Merge tag 'usb-5.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
df561f6688fe treewide: Use fallthrough pseudo-keyword
bc9a2e226ea9 usb: dwc3: gadget: Handle ZLP for sg requests
d2ee3ff79e6a usb: dwc3: gadget: Fix handling ZLP
5d187c0454ef usb: dwc3: gadget: Don't setup more than requested
db123bea9625 usb: dwc3: convert to devm_platform_ioremap_resource_byname
f5e46aa4a124 usb: dwc3: gadget: when the started list is empty stop the active xfer
c5a7092f4015 usb: dwc3: gadget: make starting isoc transfers more robust
ca14378560db usb: dwc3: gadget: add frame number mask
7a410953d1fb usb: dwc3: meson-g12a: fix shared reset control use
eed3c957dd8c Merge 5.8-rc6 into usb-next
10623b879da3 usb: dwc3: Replace HTTP links with HTTPS ones
b789710de583 usb: dwc3: dwc3-qcom: Do not define 'struct acpi_device_id' when !CONFIG_ACPI
26ef796f9306 usb: dwc3: dwc3-of-simple: Function headers are not good candidates for kerneldoc
4d72cf0cb9e2 usb: dwc3: dwc3-haps: Function headers are not suitable for kerneldoc
3c373454a9f2 usb: dwc3: dwc3-omap: Do not read DMA status
fb678a5a76a4 usb: dwc3: ulpi: File headers are not doc headers
7679defc3e29 usb: dwc3: drd: File headers are not doc headers
9cf6ffae3800 Merge 5.8-rc3 into usb-next
62fb45d317c5 USB: ch9: add "USB_" prefix in front of TEST defines
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 7 +-
drivers/usb/dwc3/core.h | 2 +-
drivers/usb/dwc3/debug.h | 2 +-
drivers/usb/dwc3/ep0.c | 12 +--
drivers/usb/dwc3/gadget.c | 149 +++++++++++++++++++++++++++++++-------
drivers/usb/dwc3/gadget.h | 4 +-
drivers/usb/dwc3/io.h | 2 +-
7 files changed, 137 insertions(+), 41 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 25c686a752b0..2eb34c8b4065 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -2,7 +2,7 @@
/**
* core.c - DesignWare USB3 DRD Controller Core file
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -646,9 +646,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
- /* FALLTHROUGH */
default:
break;
}
@@ -1411,7 +1410,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
default:
dev_err(dev, "invalid maximum_speed parameter %d\n",
dwc->maximum_speed);
- /* fall through */
+ fallthrough;
case USB_SPEED_UNKNOWN:
/* default to superspeed */
dwc->maximum_speed = USB_SPEED_SUPER;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 013f42a2b5dc..2f04b3e42bf1 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -2,7 +2,7 @@
/*
* core.h - DesignWare USB3 DRD Core Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index d8f600e0e88f..3d16dac4e5cc 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -2,7 +2,7 @@
/**
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 6dee4dabc0a4..59f2e8c31bd1 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -2,7 +2,7 @@
/*
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -425,11 +425,11 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
return -EINVAL;
switch (wIndex >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
dwc->test_mode_nr = wIndex >> 8;
dwc->test_mode = true;
break;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 80c3ef134e41..c2a0f64f8d1e 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2,7 +2,7 @@
/*
* gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -46,11 +46,11 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
reg |= mode << 1;
break;
default:
@@ -1054,27 +1054,25 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
* dwc3_prepare_one_trb - setup one TRB from one request
* @dep: endpoint for which this request is prepared
* @req: dwc3_request pointer
+ * @trb_length: buffer size of the TRB
* @chain: should this TRB be chained to the next?
* @node: only for isochronous endpoints. First TRB needs different type.
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
- struct dwc3_request *req, unsigned chain, unsigned node)
+ struct dwc3_request *req, unsigned int trb_length,
+ unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
- unsigned int length;
dma_addr_t dma;
unsigned stream_id = req->request.stream_id;
unsigned short_not_ok = req->request.short_not_ok;
unsigned no_interrupt = req->request.no_interrupt;
unsigned is_last = req->request.is_last;
- if (req->request.num_sgs > 0) {
- length = sg_dma_len(req->start_sg);
+ if (req->request.num_sgs > 0)
dma = sg_dma_address(req->start_sg);
- } else {
- length = req->request.length;
+ else
dma = req->request.dma;
- }
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -1086,7 +1084,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
+ __dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
stream_id, short_not_ok, no_interrupt, is_last);
}
@@ -1096,16 +1094,27 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct scatterlist *sg = req->start_sg;
struct scatterlist *s;
int i;
-
+ unsigned int length = req->request.length;
unsigned int remaining = req->request.num_mapped_sgs
- req->num_queued_sgs;
+ /*
+ * If we resume preparing the request, then get the remaining length of
+ * the request and resume where we left off.
+ */
+ for_each_sg(req->request.sg, s, req->num_queued_sgs, i)
+ length -= sg_dma_len(s);
+
for_each_sg(sg, s, remaining, i) {
- unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
+ unsigned int trb_length;
unsigned chain = true;
+ trb_length = min_t(unsigned int, length, sg_dma_len(s));
+
+ length -= trb_length;
+
/*
* IOMMU driver is coalescing the list of sgs which shares a
* page boundary into one and giving it to USB driver. With
@@ -1113,7 +1122,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
* sgs passed. So mark the chain bit to false if it isthe last
* mapped sg.
*/
- if (i == remaining - 1)
+ if ((i == remaining - 1) || !length)
chain = false;
if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
@@ -1123,7 +1132,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->needs_extra_trb = true;
/* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, true, i);
+ dwc3_prepare_one_trb(dep, req, trb_length, true, i);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -1134,8 +1143,39 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
+ } else if (req->request.zero && req->request.length &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ !rem && !chain) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->needs_extra_trb = true;
+
+ /* Prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, trb_length, true, i);
+
+ /* Prepare one extra TRB to handle ZLP */
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
+ !req->direction, 1,
+ req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt,
+ req->request.is_last);
+
+ /* Prepare one more TRB to handle MPS alignment */
+ if (!req->direction) {
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
+ false, 1, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt,
+ req->request.is_last);
+ }
} else {
- dwc3_prepare_one_trb(dep, req, chain, i);
+ dwc3_prepare_one_trb(dep, req, trb_length, chain, i);
}
/*
@@ -1150,6 +1190,16 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->num_queued_sgs++;
+ /*
+ * The number of pending SG entries may not correspond to the
+ * number of mapped SG entries. If all the data are queued, then
+ * don't include unused SG entries.
+ */
+ if (length == 0) {
+ req->num_pending_sgs -= req->request.num_mapped_sgs - req->num_queued_sgs;
+ break;
+ }
+
if (!dwc3_calc_trbs_left(dep))
break;
}
@@ -1169,7 +1219,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->needs_extra_trb = true;
/* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, true, 0);
+ dwc3_prepare_one_trb(dep, req, length, true, 0);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -1180,6 +1230,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->request.no_interrupt,
req->request.is_last);
} else if (req->request.zero && req->request.length &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
(IS_ALIGNED(req->request.length, maxp))) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
@@ -1187,18 +1238,29 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->needs_extra_trb = true;
/* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, true, 0);
+ dwc3_prepare_one_trb(dep, req, length, true, 0);
- /* Now prepare one extra TRB to handle ZLP */
+ /* Prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- false, 1, req->request.stream_id,
+ !req->direction, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
+
+ /* Prepare one more TRB to handle MPS alignment for OUT */
+ if (!req->direction) {
+ trb = &dep->trb_pool[dep->trb_enqueue];
+ req->num_trbs++;
+ __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
+ false, 1, req->request.stream_id,
+ req->request.short_not_ok,
+ req->request.no_interrupt,
+ req->request.is_last);
+ }
} else {
- dwc3_prepare_one_trb(dep, req, false, 0);
+ dwc3_prepare_one_trb(dep, req, length, false, 0);
}
}
@@ -1403,7 +1465,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
* Check if we can start isoc transfer on the next interval or
* 4 uframes in the future with BIT[15:14] as dep->combo_num
*/
- test_frame_number = dep->frame_number & 0x3fff;
+ test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK;
test_frame_number |= dep->combo_num << 14;
test_frame_number += max_t(u32, 4, dep->interval);
@@ -1450,7 +1512,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
else if (test0 && test1)
dep->combo_num = 0;
- dep->frame_number &= 0x3fff;
+ dep->frame_number &= DWC3_FRNUMBER_MASK;
dep->frame_number |= dep->combo_num << 14;
dep->frame_number += max_t(u32, 4, dep->interval);
@@ -1463,6 +1525,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
int ret;
int i;
@@ -1480,6 +1543,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
return dwc3_gadget_start_isoc_quirk(dep);
}
+ if (desc->bInterval <= 14 &&
+ dwc->gadget.speed >= USB_SPEED_HIGH) {
+ u32 frame = __dwc3_gadget_get_frame(dwc);
+ bool rollover = frame <
+ (dep->frame_number & DWC3_FRNUMBER_MASK);
+
+ /*
+ * frame_number is set from XferNotReady and may be already
+ * out of date. DSTS only provides the lower 14 bit of the
+ * current frame number. So add the upper two bits of
+ * frame_number and handle a possible rollover.
+ * This will provide the correct frame_number unless more than
+ * rollover has happened since XferNotReady.
+ */
+
+ dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) |
+ frame;
+ if (rollover)
+ dep->frame_number += BIT(14);
+ }
+
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
@@ -2649,8 +2733,17 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
status);
if (req->needs_extra_trb) {
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
+
+ /* Reclaim MPS padding TRB for ZLP */
+ if (!req->direction && req->request.zero && req->request.length &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (IS_ALIGNED(req->request.length, maxp)))
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);
+
req->needs_extra_trb = false;
}
@@ -2716,7 +2809,9 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
goto out;
- if (status == -EXDEV && list_empty(&dep->started_list))
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ list_empty(&dep->started_list) &&
+ (list_empty(&dep->pending_list) || status == -EXDEV))
dwc3_stop_active_transfer(dep, true, true);
else if (dwc3_gadget_ep_should_continue(dep))
if (__dwc3_gadget_kick_transfer(dep) == 0)
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 24dca3872022..bd85eb7fa9ef 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -2,7 +2,7 @@
/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -54,6 +54,8 @@ struct dwc3;
/* U2 Device exit Latency */
#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */
+/* Frame/Microframe Number Mask */
+#define DWC3_FRNUMBER_MASK 0x3fff
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 9bbe5d4bf076..76b73b116862 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -2,7 +2,7 @@
/**
* io.h - DesignWare USB3 DRD IO Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 34/64] usb: dwc3: import from kernel v5.10
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (32 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 33/64] usb: dwc3: import from kernel v5.9 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 35/64] usb: dwc3: import from kernel v5.11 Jens Wiklander
` (33 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.9 to v5.10.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.9..v5.10
Commits imported:
58d51f330e76 Merge tag 'fixes-for-v5.10-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus
cbdc0f54560f usb: fix kernel-doc markups
fa27e2f6c5e6 usb: dwc3: ep0: Fix delay status handling
1384ab4fee12 usb: dwc3: pci: add support for the Intel Alder Lake-S
e2c53515b2a6 usb: dwc3: of-simple: Add compatible string for Intel Keem Bay platform
e0a93d98f488 usb: dwc3: gadget: Support up to max stream id
2338484d14f3 usb: dwc3: gadget: Return early if no TRB update
346a15cdf652 usb: dwc3: gadget: Keep TRBs in request order
8dbbe48c7a99 usb: dwc3: gadget: Revise setting IOC when no TRB left
f9cc581badb1 usb: dwc3: gadget: Look ahead when setting IOC
d72ecc08dee4 usb: dwc3: gadget: Allow restarting a transfer
ae7e86108b12 usb: dwc3: Stop active transfers before halting the controller
f580170f135a usb: dwc3: Add splitdisable quirk for Hisilicon Kirin Soc
7f2958d9ad58 usb: dwc3: gadget: Rename misleading function names
cb1b3997b636 usb: dwc3: gadget: Refactor preparing last TRBs
30892cba5596 usb: dwc3: gadget: Set IOC if not enough for extra TRBs
490410b2e73c usb: dwc3: gadget: Check for number of TRBs prepared
13111fcb0d64 usb: dwc3: gadget: Return the number of prepared TRBs
66706077dc89 usb: dwc3: ep0: Fix ZLP for OUT ep0 requests
a2841f41d07f usb: dwc3: gadget: Improve TRB ZLP setup
2b80357b773c usb: dwc3: gadget: Refactor preparing extra TRB
690e5c2dc29f usb: dwc3: gadget: Reclaim extra TRBs after request completion
ca3df3468eec usb: dwc3: gadget: Check MPS of the request length
8266b08ed90c usb: dwc3: gadget: Refactor ep command completion
5bde3f020a15 usb: dwc3: debugfs: do not queue work if try to change mode on non-drd
03c1fd622f72 usb: dwc3: core: add phy cleanup for probe error handling
266d0493900a usb: dwc3: core: don't trigger runtime pm when remove driver
e81a7018d93a usb: dwc3: allocate gadget structure dynamically
a609ce2a1336 usb: dwc3: pci: Allow Elkhart Lake to utilize DSM method for PM functionality
65f3d449f438 usb: dwc-meson-g12a: Add support for USB on AXG SoCs
dc336b19e82d usb: dwc3: core: do not queue work if dr_mode is not USB_DR_MODE_OTG
e319bd62292c usb: dwc3: gadget: fix checkpatch warnings
87b923a2e059 usb: dwc3: core: fix checkpatch warnings
9ae0eb455b91 usb: dwc3: debugfs: fix checkpatch warnings
993ffc5b32d2 usb: dwc3: qcom: fix checkpatch warnings
c64b475b8488 usb: dwc3: ep0: fix checkpatch warnings
035cbca1360a usb: dwc3: debug: fix checkpatch warning
159fdf295c67 usb: dwc3: trace: fix checkpatch warnings
2a499b452952 usb: dwc3: ulpi: fix checkpatch warning
e5ee93d42b3f usb: dwc3: meson: fix checkpatch errors and warnings
27c7ab0fdd0b usb: dwc3: debug: fix sparse warning
a793cf81ad0c usb: dwc3: meson: fix coccinelle WARNING
bea46b981515 usb: dwc3: qcom: Add interconnect support in dwc3 driver
e518bdd9f02c usb: dwc3: core: Print warning on unsupported speed
b574ce3ee459 usb: dwc3: core: Properly default unspecified speed
b68d9251561f usb: dwc3: simple: add support for Hikey 970
d97c78a1908e usb: dwc3: gadget: END_TRANSFER before CLEAR_STALL command
c503672abe13 usb: dwc3: gadget: Resume pending requests after CLEAR_STALL
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 92 ++++--
drivers/usb/dwc3/core.h | 49 ++--
drivers/usb/dwc3/debug.h | 8 +-
drivers/usb/dwc3/ep0.c | 62 ++--
drivers/usb/dwc3/gadget.c | 593 ++++++++++++++++++++++----------------
drivers/usb/dwc3/gadget.h | 3 +-
6 files changed, 499 insertions(+), 308 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2eb34c8b4065..841daec70b6e 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* core.c - DesignWare USB3 DRD Controller Core file
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
@@ -119,9 +119,7 @@ static void __dwc3_set_mode(struct work_struct *work)
struct dwc3 *dwc = work_to_dwc(work);
unsigned long flags;
int ret;
-
- if (dwc->dr_mode != USB_DR_MODE_OTG)
- return;
+ u32 reg;
pm_runtime_get_sync(dwc->dev);
@@ -172,6 +170,11 @@ static void __dwc3_set_mode(struct work_struct *work)
otg_set_vbus(dwc->usb2_phy->otg, true);
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+ if (dwc->dis_split_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg |= DWC3_GUCTL3_SPLITDISABLE;
+ dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ }
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -203,6 +206,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
unsigned long flags;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
spin_lock_irqsave(&dwc->lock, flags);
dwc->desired_dr_role = mode;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -929,13 +935,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
*/
dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
- /* Handle USB2.0-only core configuration */
- if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
- if (dwc->maximum_speed == USB_SPEED_SUPER)
- dwc->maximum_speed = USB_SPEED_HIGH;
- }
-
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
@@ -1356,6 +1355,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");
+ dwc->dis_split_quirk = device_property_read_bool(dev,
+ "snps,dis-split-quirk");
+
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -1381,6 +1383,8 @@ bool dwc3_has_imod(struct dwc3 *dwc)
static void dwc3_check_params(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
+ unsigned int hwparam_gen =
+ DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
/* Check for proper value of imod_interval */
if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
@@ -1404,25 +1408,40 @@ static void dwc3_check_params(struct dwc3 *dwc)
case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
+ break;
case USB_SPEED_SUPER:
+ if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS)
+ dev_warn(dev, "UDC doesn't support Gen 1\n");
+ break;
case USB_SPEED_SUPER_PLUS:
+ if ((DWC3_IP_IS(DWC32) &&
+ hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) ||
+ (!DWC3_IP_IS(DWC32) &&
+ hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dev_warn(dev, "UDC doesn't support SSP\n");
break;
default:
dev_err(dev, "invalid maximum_speed parameter %d\n",
dwc->maximum_speed);
fallthrough;
case USB_SPEED_UNKNOWN:
- /* default to superspeed */
- dwc->maximum_speed = USB_SPEED_SUPER;
-
- /*
- * default to superspeed plus if we are capable.
- */
- if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) &&
- (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ switch (hwparam_gen) {
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
-
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
+ if (DWC3_IP_IS(DWC32))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+ else
+ dwc->maximum_speed = USB_SPEED_SUPER;
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_DIS:
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ break;
+ default:
+ dwc->maximum_speed = USB_SPEED_SUPER;
+ break;
+ }
break;
}
}
@@ -1554,6 +1573,17 @@ static int dwc3_probe(struct platform_device *pdev)
err5:
dwc3_event_buffers_cleanup(dwc);
+
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
+
dwc3_ulpi_exit(dwc);
err4:
@@ -1589,9 +1619,9 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_allow(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
@@ -1865,10 +1895,26 @@ static int dwc3_resume(struct device *dev)
return 0;
}
+
+static void dwc3_complete(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ u32 reg;
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
+ dwc->dis_split_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg |= DWC3_GUCTL3_SPLITDISABLE;
+ dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ }
+}
+#else
+#define dwc3_complete NULL
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dwc3_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+ .complete = dwc3_complete,
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
dwc3_runtime_idle)
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 2f04b3e42bf1..2f95f08ca511 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -138,6 +138,7 @@
#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
#define DWC3_GHWPARAMS8 0xc600
+#define DWC3_GUCTL3 0xc60c
#define DWC3_GFLADJ 0xc630
/* Device Registers */
@@ -380,6 +381,9 @@
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
+/* Global User Control Register 3 */
+#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -634,7 +638,7 @@ struct dwc3_trb;
struct dwc3_event_buffer {
void *buf;
void *cache;
- unsigned length;
+ unsigned int length;
unsigned int lpos;
unsigned int count;
unsigned int flags;
@@ -694,7 +698,7 @@ struct dwc3_ep {
struct dwc3 *dwc;
u32 saved_state;
- unsigned flags;
+ unsigned int flags;
#define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2)
@@ -706,6 +710,7 @@ struct dwc3_ep {
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
+#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -893,9 +898,9 @@ struct dwc3_request {
struct scatterlist *sg;
struct scatterlist *start_sg;
- unsigned num_pending_sgs;
+ unsigned int num_pending_sgs;
unsigned int num_queued_sgs;
- unsigned remaining;
+ unsigned int remaining;
unsigned int status;
#define DWC3_REQUEST_STATUS_QUEUED 0
@@ -908,11 +913,11 @@ struct dwc3_request {
struct dwc3_trb *trb;
dma_addr_t trb_dma;
- unsigned num_trbs;
+ unsigned int num_trbs;
- unsigned needs_extra_trb:1;
- unsigned direction:1;
- unsigned mapped:1;
+ unsigned int needs_extra_trb:1;
+ unsigned int direction:1;
+ unsigned int mapped:1;
};
/*
@@ -1010,8 +1015,8 @@ struct dwc3_scratchpad_array {
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
- * 0 - utmi_sleep_n
- * 1 - utmi_l1_suspend_n
+ * 0 - utmi_sleep_n
+ * 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
* @pending_events: true when we have pending IRQs to be handled
* @pullups_connected: true when Run/Stop bit is set
@@ -1047,13 +1052,14 @@ struct dwc3_scratchpad_array {
* instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
- * 0 - -6dB de-emphasis
- * 1 - -3.5dB de-emphasis
- * 2 - No de-emphasis
- * 3 - Reserved
+ * 0 - -6dB de-emphasis
+ * 1 - -3.5dB de-emphasis
+ * 2 - No de-emphasis
+ * 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
+ * @dis_split_quirk: set to disable split boundary.
* @imod_interval: set the interrupt moderation interval in 250ns
- * increments or 0 to disable.
+ * increments or 0 to disable.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1079,7 +1085,7 @@ struct dwc3 {
struct dwc3_event_buffer *ev_buf;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
- struct usb_gadget gadget;
+ struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver;
struct clk_bulk_data *clks;
@@ -1245,6 +1251,8 @@ struct dwc3 {
unsigned dis_metastability_quirk:1;
+ unsigned dis_split_quirk:1;
+
u16 imod_interval;
};
@@ -1269,7 +1277,7 @@ struct dwc3_event_type {
#define DWC3_DEPEVT_EPCMDCMPLT 0x07
/**
- * struct dwc3_event_depvt - Device Endpoint Events
+ * struct dwc3_event_depevt - Device Endpoint Events
* @one_bit: indicates this is an endpoint event (not used)
* @endpoint_number: number of the endpoint
* @endpoint_event: The event we have:
@@ -1456,9 +1464,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
-int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params);
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
+ u32 param);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1472,7 +1481,7 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
enum dwc3_link_state state)
{ return 0; }
-static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params)
{ return 0; }
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 3d16dac4e5cc..8ab394942360 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -371,7 +371,9 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
u32 ep0state)
{
- const union dwc3_event evt = (union dwc3_event) event;
+ union dwc3_event evt;
+
+ memcpy(&evt, &event, sizeof(event));
if (evt.type.is_devspec)
return dwc3_gadget_event_string(str, size, &evt.devt);
@@ -411,8 +413,8 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
#ifdef CONFIG_DEBUG_FS
-extern void dwc3_debugfs_init(struct dwc3 *);
-extern void dwc3_debugfs_exit(struct dwc3 *);
+extern void dwc3_debugfs_init(struct dwc3 *d);
+extern void dwc3_debugfs_exit(struct dwc3 *d);
#else
static inline void dwc3_debugfs_init(struct dwc3 *d)
{ }
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 59f2e8c31bd1..8b668ef46f7f 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -105,7 +105,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* IRQ we were waiting for is long gone.
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- unsigned direction;
+ unsigned int direction;
direction = !!(dep->flags & DWC3_EP0_DIR_IN);
@@ -127,11 +127,11 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* handle it here.
*/
if (dwc->delayed_status) {
- unsigned direction;
+ unsigned int direction;
direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED);
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
@@ -172,7 +172,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* XferNotReady(STATUS).
*/
if (dwc->three_stage_setup) {
- unsigned direction;
+ unsigned int direction;
direction = dwc->ep0_expect_in;
dwc->ep0state = EP0_DATA_PHASE;
@@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc) {
+ if (!dep->endpoint.desc || !dwc->pullups_connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
ret = -ESHUTDOWN;
@@ -325,7 +325,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
/*
* LTM will be set once we know how to set this in HW.
*/
- usb_status |= dwc->gadget.is_selfpowered;
+ usb_status |= dwc->gadget->is_selfpowered;
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
@@ -450,7 +450,7 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
- state = dwc->gadget.state;
+ state = dwc->gadget->state;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
@@ -524,6 +524,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
ret = __dwc3_gadget_ep_set_halt(dep, set, true);
if (ret)
return -EINVAL;
+
+ /* ClearFeature(Halt) may need delayed status */
+ if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return USB_GADGET_DELAYED_STATUS;
+
break;
default:
return -EINVAL;
@@ -559,7 +564,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u32 addr;
u32 reg;
@@ -580,9 +585,9 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
- usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
else
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT);
return 0;
}
@@ -592,14 +597,14 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
int ret;
spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
spin_lock(&dwc->lock);
return ret;
}
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u32 cfg;
int ret;
u32 reg;
@@ -622,7 +627,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* to change the state on the next usb_ep_queue()
*/
if (ret == 0)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED);
/*
@@ -641,7 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_ADDRESS);
break;
default:
@@ -697,7 +702,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
struct dwc3_ep *dep;
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u16 wLength;
if (state == USB_STATE_DEFAULT)
@@ -741,7 +746,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct
if (wIndex || wLength)
return -EINVAL;
- dwc->gadget.isoch_delay = wValue;
+ dwc->gadget->isoch_delay = wValue;
return 0;
}
@@ -942,12 +947,16 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req)
{
+ unsigned int trb_length = 0;
int ret;
req->direction = !!dep->number;
if (req->request.length == 0) {
- dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
+ if (!req->direction)
+ trb_length = dep->endpoint.maxpacket;
+
+ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length,
DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dep);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
@@ -994,9 +1003,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
+ if (!req->direction)
+ trb_length = dep->endpoint.maxpacket;
+
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
- 0, DWC3_TRBCTL_CONTROL_DATA,
+ trb_length, DWC3_TRBCTL_CONTROL_DATA,
false);
ret = dwc3_ep0_start_trans(dep);
} else {
@@ -1042,6 +1054,18 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
__dwc3_ep0_do_control_status(dwc, dep);
}
+void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
+{
+ unsigned int direction = !dwc->ep0_expect_in;
+
+ dwc->delayed_status = false;
+
+ if (dwc->ep0state != EP0_STATUS_PHASE)
+ return;
+
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
+}
+
static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
@@ -1102,7 +1126,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
*/
if (!list_empty(&dep->pending_list)) {
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED);
dwc3_ep0_do_control_status(dwc, event);
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c2a0f64f8d1e..78cb4db8a6e4 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -227,7 +227,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
* Caller should take care of locking. Issue @cmd with a given @param to @dwc
* and wait for its completion.
*/
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
+ u32 param)
{
u32 timeout = 500;
int status = 0;
@@ -268,7 +269,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
* Caller should handle locking. This function will issue @cmd with given
* @params to @dep and wait for its completion.
*/
-int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
@@ -290,7 +291,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
*
* DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
- if (dwc->gadget.speed <= USB_SPEED_HIGH) {
+ if (dwc->gadget->speed <= USB_SPEED_HIGH) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -422,7 +423,7 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
*/
if (dep->direction &&
!DWC3_VER_IS_PRIOR(DWC3, 260A) &&
- (dwc->gadget.speed >= USB_SPEED_SUPER))
+ (dwc->gadget->speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(¶ms, 0, sizeof(params));
@@ -562,8 +563,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget.speed >= USB_SPEED_SUPER) {
+ if (dwc->gadget->speed >= USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst;
+
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
@@ -942,12 +944,13 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
}
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
- dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
- unsigned stream_id, unsigned short_not_ok,
- unsigned no_interrupt, unsigned is_last)
+ dma_addr_t dma, unsigned int length, unsigned int chain,
+ unsigned int node, unsigned int stream_id,
+ unsigned int short_not_ok, unsigned int no_interrupt,
+ unsigned int is_last, bool must_interrupt)
{
struct dwc3 *dwc = dep->dwc;
- struct usb_gadget *gadget = &dwc->gadget;
+ struct usb_gadget *gadget = dwc->gadget;
enum usb_device_speed speed = gadget->speed;
trb->size = DWC3_TRB_SIZE_LENGTH(length);
@@ -1031,8 +1034,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
- if ((!no_interrupt && !chain) ||
- (dwc3_calc_trbs_left(dep) == 1))
+ if ((!no_interrupt && !chain) || must_interrupt)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
@@ -1057,19 +1059,24 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
* @trb_length: buffer size of the TRB
* @chain: should this TRB be chained to the next?
* @node: only for isochronous endpoints. First TRB needs different type.
+ * @use_bounce_buffer: set to use bounce buffer
+ * @must_interrupt: set to interrupt on TRB completion
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned int trb_length,
- unsigned chain, unsigned node)
+ unsigned int chain, unsigned int node, bool use_bounce_buffer,
+ bool must_interrupt)
{
struct dwc3_trb *trb;
dma_addr_t dma;
- unsigned stream_id = req->request.stream_id;
- unsigned short_not_ok = req->request.short_not_ok;
- unsigned no_interrupt = req->request.no_interrupt;
- unsigned is_last = req->request.is_last;
-
- if (req->request.num_sgs > 0)
+ unsigned int stream_id = req->request.stream_id;
+ unsigned int short_not_ok = req->request.short_not_ok;
+ unsigned int no_interrupt = req->request.no_interrupt;
+ unsigned int is_last = req->request.is_last;
+
+ if (use_bounce_buffer)
+ dma = dep->dwc->bounce_addr;
+ else if (req->request.num_sgs > 0)
dma = sg_dma_address(req->start_sg);
else
dma = req->request.dma;
@@ -1085,10 +1092,63 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
- stream_id, short_not_ok, no_interrupt, is_last);
+ stream_id, short_not_ok, no_interrupt, is_last,
+ must_interrupt);
}
-static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
+static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = req->request.length % maxp;
+
+ if ((req->request.length && req->request.zero && !rem &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc)) ||
+ (!req->direction && rem))
+ return true;
+
+ return false;
+}
+
+/**
+ * dwc3_prepare_last_sg - prepare TRBs for the last SG entry
+ * @dep: The endpoint that the request belongs to
+ * @req: The request to prepare
+ * @entry_length: The last SG entry size
+ * @node: Indicates whether this is not the first entry (for isoc only)
+ *
+ * Return the number of TRBs prepared.
+ */
+static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int entry_length,
+ unsigned int node)
+{
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = req->request.length % maxp;
+ unsigned int num_trbs = 1;
+
+ if (dwc3_needs_extra_trb(dep, req))
+ num_trbs++;
+
+ if (dwc3_calc_trbs_left(dep) < num_trbs)
+ return 0;
+
+ req->needs_extra_trb = num_trbs > 1;
+
+ /* Prepare a normal TRB */
+ if (req->direction || req->request.length)
+ dwc3_prepare_one_trb(dep, req, entry_length,
+ req->needs_extra_trb, node, false, false);
+
+ /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */
+ if ((!req->direction && !req->request.length) || req->needs_extra_trb)
+ dwc3_prepare_one_trb(dep, req,
+ req->direction ? 0 : maxp - rem,
+ false, 1, true, false);
+
+ return num_trbs;
+}
+
+static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct scatterlist *sg = req->start_sg;
@@ -1097,6 +1157,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
unsigned int length = req->request.length;
unsigned int remaining = req->request.num_mapped_sgs
- req->num_queued_sgs;
+ unsigned int num_trbs = req->num_trbs;
+ bool needs_extra_trb = dwc3_needs_extra_trb(dep, req);
/*
* If we resume preparing the request, then get the remaining length of
@@ -1106,10 +1168,10 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
length -= sg_dma_len(s);
for_each_sg(sg, s, remaining, i) {
- unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
- unsigned int rem = length % maxp;
+ unsigned int num_trbs_left = dwc3_calc_trbs_left(dep);
unsigned int trb_length;
- unsigned chain = true;
+ bool must_interrupt = false;
+ bool last_sg = false;
trb_length = min_t(unsigned int, length, sg_dma_len(s));
@@ -1123,59 +1185,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
* mapped sg.
*/
if ((i == remaining - 1) || !length)
- chain = false;
-
- if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, trb_length, true, i);
-
- /* Now prepare one extra TRB to align transfer size */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
- maxp - rem, false, 1,
- req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt,
- req->request.is_last);
- } else if (req->request.zero && req->request.length &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !rem && !chain) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* Prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, trb_length, true, i);
-
- /* Prepare one extra TRB to handle ZLP */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- !req->direction, 1,
- req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt,
- req->request.is_last);
-
- /* Prepare one more TRB to handle MPS alignment */
- if (!req->direction) {
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt,
- req->request.is_last);
- }
+ last_sg = true;
+
+ if (!num_trbs_left)
+ break;
+
+ if (last_sg) {
+ if (!dwc3_prepare_last_sg(dep, req, trb_length, i))
+ break;
} else {
- dwc3_prepare_one_trb(dep, req, trb_length, chain, i);
+ /*
+ * Look ahead to check if we have enough TRBs for the
+ * next SG entry. If not, set interrupt on this TRB to
+ * resume preparing the next SG entry when more TRBs are
+ * free.
+ */
+ if (num_trbs_left == 1 || (needs_extra_trb &&
+ num_trbs_left <= 2 &&
+ sg_dma_len(sg_next(s)) >= length))
+ must_interrupt = true;
+
+ dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false,
+ must_interrupt);
}
/*
@@ -1185,7 +1216,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
* we have free trbs we can continue queuing from where we
* previously stopped
*/
- if (chain)
+ if (!last_sg)
req->start_sg = sg_next(s);
req->num_queued_sgs++;
@@ -1200,68 +1231,17 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
break;
}
- if (!dwc3_calc_trbs_left(dep))
+ if (must_interrupt)
break;
}
+
+ return req->num_trbs - num_trbs;
}
-static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
+static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- unsigned int length = req->request.length;
- unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
- unsigned int rem = length % maxp;
-
- if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, length, true, 0);
-
- /* Now prepare one extra TRB to align transfer size */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt,
- req->request.is_last);
- } else if (req->request.zero && req->request.length &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (IS_ALIGNED(req->request.length, maxp))) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, length, true, 0);
-
- /* Prepare one extra TRB to handle ZLP */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- !req->direction, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt,
- req->request.is_last);
-
- /* Prepare one more TRB to handle MPS alignment for OUT */
- if (!req->direction) {
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt,
- req->request.is_last);
- }
- } else {
- dwc3_prepare_one_trb(dep, req, length, false, 0);
- }
+ return dwc3_prepare_last_sg(dep, req, req->request.length, 0);
}
/*
@@ -1271,10 +1251,13 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
* The function goes through the requests list and sets up TRBs for the
* transfers. The function returns once there are no more TRBs available or
* it runs out of requests.
+ *
+ * Returns the number of TRBs prepared or negative errno.
*/
-static void dwc3_prepare_trbs(struct dwc3_ep *dep)
+static int dwc3_prepare_trbs(struct dwc3_ep *dep)
{
struct dwc3_request *req, *n;
+ int ret = 0;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
@@ -1289,11 +1272,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
* break things.
*/
list_for_each_entry(req, &dep->started_list, list) {
- if (req->num_pending_sgs > 0)
- dwc3_prepare_one_trb_sg(dep, req);
+ if (req->num_pending_sgs > 0) {
+ ret = dwc3_prepare_trbs_sg(dep, req);
+ if (!ret || req->num_pending_sgs)
+ return ret;
+ }
if (!dwc3_calc_trbs_left(dep))
- return;
+ return ret;
/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
@@ -1301,30 +1287,32 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
- return;
+ return ret;
}
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
struct dwc3 *dwc = dep->dwc;
- int ret;
ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
dep->direction);
if (ret)
- return;
+ return ret;
req->sg = req->request.sg;
req->start_sg = req->sg;
req->num_queued_sgs = 0;
req->num_pending_sgs = req->request.num_mapped_sgs;
- if (req->num_pending_sgs > 0)
- dwc3_prepare_one_trb_sg(dep, req);
- else
- dwc3_prepare_one_trb_linear(dep, req);
+ if (req->num_pending_sgs > 0) {
+ ret = dwc3_prepare_trbs_sg(dep, req);
+ if (req->num_pending_sgs)
+ return ret;
+ } else {
+ ret = dwc3_prepare_trbs_linear(dep, req);
+ }
- if (!dwc3_calc_trbs_left(dep))
- return;
+ if (!ret || !dwc3_calc_trbs_left(dep))
+ return ret;
/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
@@ -1332,8 +1320,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
- return;
+ return ret;
}
+
+ return ret;
}
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
@@ -1346,12 +1336,24 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
int ret;
u32 cmd;
- if (!dwc3_calc_trbs_left(dep))
- return 0;
+ /*
+ * Note that it's normal to have no new TRBs prepared (i.e. ret == 0).
+ * This happens when we need to stop and restart a transfer such as in
+ * the case of reinitiating a stream or retrying an isoc transfer.
+ */
+ ret = dwc3_prepare_trbs(dep);
+ if (ret < 0)
+ return ret;
starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
- dwc3_prepare_trbs(dep);
+ /*
+ * If there's no new TRB prepared and we don't need to restart a
+ * transfer, there's no need to update the transfer.
+ */
+ if (!ret && !starting)
+ return ret;
+
req = next_request(&dep->started_list);
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
@@ -1539,12 +1541,12 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
if (!dwc->dis_start_transfer_quirk &&
(DWC3_VER_IS_PRIOR(DWC31, 170A) ||
DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
- if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
+ if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction)
return dwc3_gadget_start_isoc_quirk(dep);
}
if (desc->bInterval <= 14 &&
- dwc->gadget.speed >= USB_SPEED_HIGH) {
+ dwc->gadget->speed >= USB_SPEED_HIGH) {
u32 frame = __dwc3_gadget_get_frame(dwc);
bool rollover = frame <
(dep->frame_number & DWC3_FRNUMBER_MASK);
@@ -1600,7 +1602,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
- if (!dep->endpoint.desc) {
+ if (!dep->endpoint.desc || !dwc->pullups_connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
return -ESHUTDOWN;
@@ -1628,8 +1630,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
return 0;
- /* Start the transfer only after the END_TRANSFER is completed */
- if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
+ /*
+ * Start the transfer only after the END_TRANSFER is completed
+ * and endpoint STALL is cleared.
+ */
+ if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
+ (dep->flags & DWC3_EP_WEDGE) ||
+ (dep->flags & DWC3_EP_STALL)) {
dep->flags |= DWC3_EP_DELAY_START;
return 0;
}
@@ -1648,9 +1655,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return 0;
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
return __dwc3_gadget_start_isoc(dep);
- }
}
}
@@ -1788,8 +1794,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (value) {
struct dwc3_trb *trb;
- unsigned transfer_in_flight;
- unsigned started;
+ unsigned int transfer_in_flight;
+ unsigned int started;
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
@@ -1822,6 +1828,18 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
return 0;
}
+ dwc3_stop_active_transfer(dep, true, true);
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req);
+
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
+ dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+ return 0;
+ }
+
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret) {
dev_err(dwc->dev, "failed to clear STALL on %s\n",
@@ -1831,18 +1849,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
- dwc3_stop_active_transfer(dep, true, true);
-
- list_for_each_entry_safe(req, tmp, &dep->started_list, list)
- dwc3_gadget_move_cancelled_request(req);
-
- list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
- dwc3_gadget_move_cancelled_request(req);
+ if ((dep->flags & DWC3_EP_DELAY_START) &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
- if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
- dep->flags &= ~DWC3_EP_DELAY_START;
- dwc3_gadget_ep_cleanup_cancelled_requests(dep);
- }
+ dep->flags &= ~DWC3_EP_DELAY_START;
}
return ret;
@@ -2010,6 +2021,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
return 0;
}
+static void dwc3_stop_active_transfers(struct dwc3 *dwc)
+{
+ u32 epnum;
+
+ for (epnum = 2; epnum < dwc->num_eps; epnum++) {
+ struct dwc3_ep *dep;
+
+ dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
+
+ dwc3_remove_requests(dwc, dep);
+ }
+}
+
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
u32 reg;
@@ -2055,6 +2081,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
return 0;
}
+static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
+static void __dwc3_gadget_stop(struct dwc3 *dwc);
+
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
struct dwc3 *dwc = gadget_to_dwc(g);
@@ -2078,7 +2107,46 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
}
}
+ /*
+ * Synchronize any pending event handling before executing the controller
+ * halt routine.
+ */
+ if (!is_on) {
+ dwc3_gadget_disable_irq(dwc);
+ synchronize_irq(dwc->irq_gadget);
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
+
+ if (!is_on) {
+ u32 count;
+
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.8 Table 4-7, it states that for a device-initiated
+ * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
+ * command for any active transfers" before clearing the RunStop
+ * bit.
+ */
+ dwc3_stop_active_transfers(dwc);
+ __dwc3_gadget_stop(dwc);
+
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the
+ * "software needs to acknowledge the events that are generated
+ * (by writing to GEVNTCOUNTn) while it is waiting for this bit
+ * to be set to '1'."
+ */
+ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ count &= DWC3_GEVNTCOUNT_MASK;
+ if (count > 0) {
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+ dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) %
+ dwc->ev_buf->length;
+ }
+ }
+
ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2244,7 +2312,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver) {
dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
+ dwc->gadget->name,
dwc->gadget_driver->driver.name);
ret = -EBUSY;
goto err1;
@@ -2416,7 +2484,7 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!dep->direction)
- dwc->gadget.ep0 = &dep->endpoint;
+ dwc->gadget->ep0 = &dep->endpoint;
dep->endpoint.caps.type_control = true;
@@ -2459,10 +2527,10 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- dep->endpoint.max_streams = 15;
+ dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc->gadget->ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
@@ -2508,10 +2576,10 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
size /= 3;
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- dep->endpoint.max_streams = 15;
+ dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc->gadget->ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
@@ -2572,7 +2640,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
{
u8 epnum;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ INIT_LIST_HEAD(&dwc->gadget->ep_list);
for (epnum = 0; epnum < total; epnum++) {
int ret;
@@ -2652,12 +2720,12 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
}
/*
- * If we're dealing with unaligned size OUT transfer, we will be left
- * with one TRB pending in the ring. We need to manually clear HWO bit
- * from that TRB.
+ * We use bounce buffer for requests that needs extra TRB or OUT ZLP. If
+ * this TRB points to the bounce buffer address, it's a MPS alignment
+ * TRB. Don't add it to req->remaining calculation.
*/
-
- if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
+ if (trb->bpl == lower_32_bits(dep->dwc->bounce_addr) &&
+ trb->bph == upper_32_bits(dep->dwc->bounce_addr)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2732,26 +2800,17 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
- if (req->needs_extra_trb) {
- unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ req->request.actual = req->request.length - req->remaining;
+
+ if (!dwc3_gadget_ep_request_completed(req))
+ goto out;
+ if (req->needs_extra_trb) {
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
-
- /* Reclaim MPS padding TRB for ZLP */
- if (!req->direction && req->request.zero && req->request.length &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (IS_ALIGNED(req->request.length, maxp)))
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);
-
req->needs_extra_trb = false;
}
- req->request.actual = req->request.length - req->remaining;
-
- if (!dwc3_gadget_ep_request_completed(req))
- goto out;
-
dwc3_gadget_giveback(dep, req, status);
out:
@@ -2896,6 +2955,43 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
(void) __dwc3_gadget_start_isoc(dep);
}
+static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ u8 cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd != DWC3_DEPCMD_ENDTRANSFER)
+ return;
+
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
+ if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
+ struct dwc3 *dwc = dep->dwc;
+
+ dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
+ if (dwc3_send_clear_stall_ep_cmd(dep)) {
+ struct usb_ep *ep0 = &dwc->eps[0]->endpoint;
+
+ dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name);
+ if (dwc->delayed_status)
+ __dwc3_gadget_ep0_set_halt(ep0, 1);
+ return;
+ }
+
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ if (dwc->delayed_status)
+ dwc3_ep0_send_delayed_status(dwc);
+ }
+
+ if ((dep->flags & DWC3_EP_DELAY_START) &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
+
+ dep->flags &= ~DWC3_EP_DELAY_START;
+}
+
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
@@ -2965,7 +3061,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
{
struct dwc3_ep *dep;
u8 epnum = event->endpoint_number;
- u8 cmd;
dep = dwc->eps[epnum];
@@ -2991,18 +3086,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dwc3_gadget_endpoint_transfer_not_ready(dep, event);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- cmd = DEPEVT_PARAMETER_CMD(event->parameters);
-
- if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
- dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
- dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_cleanup_cancelled_requests(dep);
- if ((dep->flags & DWC3_EP_DELAY_START) &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc))
- __dwc3_gadget_kick_transfer(dep);
-
- dep->flags &= ~DWC3_EP_DELAY_START;
- }
+ dwc3_gadget_endpoint_command_complete(dep, event);
break;
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_gadget_endpoint_transfer_complete(dep, event);
@@ -3019,7 +3103,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(&dwc->gadget);
+ dwc->gadget_driver->disconnect(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3028,7 +3112,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->suspend(&dwc->gadget);
+ dwc->gadget_driver->suspend(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3037,7 +3121,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
{
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3047,9 +3131,9 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
if (!dwc->gadget_driver)
return;
- if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (dwc->gadget->speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
- usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
+ usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
@@ -3150,9 +3234,9 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_disconnect_gadget(dwc);
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
dwc->connected = false;
}
@@ -3195,6 +3279,13 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
}
dwc3_reset_gadget(dwc);
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
+ * needs to ensure that it sends "a DEPENDXFER command for any active
+ * transfers."
+ */
+ dwc3_stop_active_transfers(dwc);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
@@ -3231,8 +3322,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
switch (speed) {
case DWC3_DSTS_SUPERSPEED_PLUS:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget->ep0->maxpacket = 512;
+ dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
break;
case DWC3_DSTS_SUPERSPEED:
/*
@@ -3252,27 +3343,27 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER;
+ dwc->gadget->ep0->maxpacket = 512;
+ dwc->gadget->speed = USB_SPEED_SUPER;
break;
case DWC3_DSTS_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_HIGH;
+ dwc->gadget->ep0->maxpacket = 64;
+ dwc->gadget->speed = USB_SPEED_HIGH;
break;
case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_FULL;
+ dwc->gadget->ep0->maxpacket = 64;
+ dwc->gadget->speed = USB_SPEED_FULL;
break;
case DWC3_DSTS_LOWSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
- dwc->gadget.ep0->maxpacket = 8;
- dwc->gadget.speed = USB_SPEED_LOW;
+ dwc->gadget->ep0->maxpacket = 8;
+ dwc->gadget->speed = USB_SPEED_LOW;
break;
}
- dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket;
/* Enable USB2 LPM Capability */
@@ -3340,7 +3431,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3511,7 +3602,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
*/
- if (dwc->gadget.state >= USB_STATE_CONFIGURED)
+ if (dwc->gadget->state >= USB_STATE_CONFIGURED)
dwc3_gadget_suspend_interrupt(dwc,
event->event_info);
}
@@ -3686,6 +3777,13 @@ out:
return irq;
}
+static void dwc_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev);
+
+ kfree(gadget);
+}
+
/**
* dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -3696,6 +3794,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
int irq;
+ struct device *dev;
irq = dwc3_gadget_get_irq(dwc);
if (irq < 0) {
@@ -3728,12 +3827,21 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
init_completion(&dwc->ep0_in_setup);
+ dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);
+ if (!dwc->gadget) {
+ ret = -ENOMEM;
+ goto err3;
+ }
- dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
- dwc->gadget.sg_supported = true;
- dwc->gadget.name = "dwc3-gadget";
- dwc->gadget.lpm_capable = true;
+
+ usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
+ dev = &dwc->gadget->dev;
+ dev->platform_data = dwc;
+ dwc->gadget->ops = &dwc3_gadget_ops;
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->sg_supported = true;
+ dwc->gadget->name = "dwc3-gadget";
+ dwc->gadget->lpm_capable = true;
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -3756,7 +3864,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
- dwc->gadget.max_speed = dwc->maximum_speed;
+ dwc->gadget->max_speed = dwc->maximum_speed;
/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -3765,21 +3873,22 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
if (ret)
- goto err3;
+ goto err4;
- ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
+ ret = usb_add_gadget(dwc->gadget);
if (ret) {
- dev_err(dwc->dev, "failed to register udc\n");
- goto err4;
+ dev_err(dwc->dev, "failed to add gadget\n");
+ goto err5;
}
- dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
+ dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
return 0;
-err4:
+err5:
dwc3_gadget_free_endpoints(dwc);
-
+err4:
+ usb_put_gadget(dwc->gadget);
err3:
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
@@ -3799,7 +3908,7 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
- usb_del_gadget_udc(&dwc->gadget);
+ usb_del_gadget_udc(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index bd85eb7fa9ef..0cd281949970 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -17,7 +17,7 @@
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
-#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+#define gadget_to_dwc(g) (dev_get_platdata(&g->dev))
/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
@@ -113,6 +113,7 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
+void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 35/64] usb: dwc3: import from kernel v5.11
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (33 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 34/64] usb: dwc3: import from kernel v5.10 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 36/64] usb: dwc3: import from kernel v5.12 Jens Wiklander
` (32 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.10 to v5.11.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.10..v5.11
Commits imported:
0e5a3c8284a3 usb: dwc3: fix clock issue during resume in OTG mode
a5c7682aaaa1 usb: dwc3: gadget: Clear wait flag on dequeue
a1383b3537a7 usb: dwc3: gadget: Restart DWC3 gadget when enabling pullup
e5f4ca3fce90 usb: dwc3: ulpi: Fix USB2.0 HS/FS/LS PHY suspend regression
fca3f1381057 usb: dwc3: ulpi: Replace CPU-based busyloop with Protocol-based one
ce722da66d3e usb: dwc3: ulpi: Use VStsDone to detect PHY regs access completion
a5ada3dfe6a2 usb: dwc3: meson-g12a: disable clk on error handling path in probe
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 2 +-
drivers/usb/dwc3/core.h | 1 +
drivers/usb/dwc3/gadget.c | 16 +++++-----------
3 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 841daec70b6e..3101f0dcf6ae 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1758,7 +1758,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (PMSG_IS_AUTO(msg))
break;
- ret = dwc3_core_init(dwc);
+ ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 2f95f08ca511..1b241f937d8f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -285,6 +285,7 @@
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25)
+#define DWC3_GUSB2PHYACC_DONE BIT(24)
#define DWC3_GUSB2PHYACC_BUSY BIT(23)
#define DWC3_GUSB2PHYACC_WRITE BIT(22)
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 78cb4db8a6e4..ee44321fee38 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1763,6 +1763,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
list_for_each_entry_safe(r, t, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(r);
+ dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
goto out;
}
}
@@ -2083,6 +2085,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
static void __dwc3_gadget_stop(struct dwc3 *dwc);
+static int __dwc3_gadget_start(struct dwc3 *dwc);
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
@@ -2145,6 +2148,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) %
dwc->ev_buf->length;
}
+ } else {
+ __dwc3_gadget_start(dwc);
}
ret = dwc3_gadget_run_stop(dwc, is_on, false);
@@ -2319,10 +2324,6 @@ static int dwc3_gadget_start(struct usb_gadget *g,
}
dwc->gadget_driver = driver;
-
- if (pm_runtime_active(dwc->dev))
- __dwc3_gadget_start(dwc);
-
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
@@ -2348,13 +2349,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
-
- if (pm_runtime_suspended(dwc->dev))
- goto out;
-
- __dwc3_gadget_stop(dwc);
-
-out:
dwc->gadget_driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 36/64] usb: dwc3: import from kernel v5.12
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (34 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 35/64] usb: dwc3: import from kernel v5.11 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 37/64] usb: dwc3: import from kernel v5.13 Jens Wiklander
` (31 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.11 to v5.12.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.11..v5.12
Commits imported:
5aef629704ad usb: dwc3: gadget: Clear DEP flags after stop transfers in ep disable
b522f830d351 usb: dwc3: pci: Enable dis_uX_susphy_quirk for Intel Merrifield
93f1d43c5767 usb: dwc3: gadget: Use max speed if unspecified
cdb651b6021e usb: dwc3: gadget: Set gadget_max_speed when set ssp_rate
5e4010e36a58 usb: dwc3: qcom: skip interconnect init for ACPI probe
f09ddcfcb8c5 usb: dwc3: gadget: Prevent EP queuing while stopping transfers
1cffb1c66499 usb: dwc3: qcom: Add missing DWC3 OF node refcount decrement
2664deb09306 usb: dwc3: qcom: Honor wakeup enabled/disabled state
1edbff9c80ed usb: dwc3: qcom: add ACPI device id for sc8180x
a99163e9e708 Merge tag 'devicetree-for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux
83c4a4eec06a of: Remove of_dev_{get,put}()
4b049f55ed95 usb: dwc3: gadget: Fix dep->interval for fullspeed interrupt
a1679af85b2a usb: dwc3: gadget: Fix setting of DEPCFG.bInterval_m1
d8c849037d93 Merge 5.11-rc7 into usb-next
8cf9045b9138 usb: dwc3: gadget: Remove check for bounded driver
450b9e9fabd8 usb: dwc3: gadget: Set speed only up to the max supported
f551037c0770 usb: dwc3: gadget: Track connected SSP rate and lane count
072cab8a0fe2 usb: dwc3: gadget: Implement setting of SSP rate
678481467d2e usb: dwc3: core: Check maximum_speed SSP genXxY
b1638ee56c1a usb: dwc3: host: Use software node API with the properties
8dc6e6dd1bee usb: dwc3: qcom: Constify the software node
efc9812edc61 usb: dwc3: haps: Constify the software node
c25c210f590e usb: dwc3: qcom: add URS Host support for sdm845 ACPI boot
f08fc2c30e78 usb: dwc3: pci: add support for the Intel Alder Lake-P
73203bde3a95 usb: dwc3: pci: ID for Tiger Lake CPU
e492ce9bcaa1 usb: dwc3: pci: Register a software node for the dwc3 platform device
5dc71f1eb870 USB: dwc3: document gadget_max_speed
132ee0da6e9f usb: dwc3: gadget: Disable Vendor Test LMP Received event
0c0a20f6da04 usb: dwc3: Simplify with dev_err_probe()
370e3d5b711d usb: dwc3: keystone: Simplify with dev_err_probe()
babbdfc9d229 usb: dwc3: core: Replace devm_reset_control_array_get()
a66a7d48f34a Merge 5.11-rc3 into usb-next
7c9a2598463a usb: dwc3: gadget: Preserve UDC max speed setting
77adb8bdf422 usb: dwc3: gadget: Allow runtime suspend if UDC unbinded
82c46b8ed9dc usb: dwc3: gadget: Introduce a DWC3 VBUS draw callback
6dd2565989b4 usb: dwc3: add imx8mp dwc3 glue layer driver
cb4d9b52ec06 usb: dwc3: drd: Improve dwc3_get_extcon() style
c824c73a5e08 usb: dwc3: drd: Avoid error when extcon is missing
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 83 +++++++-----
drivers/usb/dwc3/core.h | 11 ++
drivers/usb/dwc3/gadget.c | 263 ++++++++++++++++++++++++++------------
3 files changed, 243 insertions(+), 114 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3101f0dcf6ae..f2448d0a9d39 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1126,11 +1126,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
ret = PTR_ERR(dwc->usb2_phy);
if (ret == -ENXIO || ret == -ENODEV) {
dwc->usb2_phy = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
} else {
- dev_err(dev, "no usb2 phy configured\n");
- return ret;
+ return dev_err_probe(dev, ret, "no usb2 phy configured\n");
}
}
@@ -1138,11 +1135,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
ret = PTR_ERR(dwc->usb3_phy);
if (ret == -ENXIO || ret == -ENODEV) {
dwc->usb3_phy = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
} else {
- dev_err(dev, "no usb3 phy configured\n");
- return ret;
+ return dev_err_probe(dev, ret, "no usb3 phy configured\n");
}
}
@@ -1151,11 +1145,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
ret = PTR_ERR(dwc->usb2_generic_phy);
if (ret == -ENOSYS || ret == -ENODEV) {
dwc->usb2_generic_phy = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
} else {
- dev_err(dev, "no usb2 phy configured\n");
- return ret;
+ return dev_err_probe(dev, ret, "no usb2 phy configured\n");
}
}
@@ -1164,11 +1155,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
ret = PTR_ERR(dwc->usb3_generic_phy);
if (ret == -ENOSYS || ret == -ENODEV) {
dwc->usb3_generic_phy = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
} else {
- dev_err(dev, "no usb3 phy configured\n");
- return ret;
+ return dev_err_probe(dev, ret, "no usb3 phy configured\n");
}
}
@@ -1190,11 +1178,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize gadget\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize gadget\n");
break;
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
@@ -1205,20 +1190,14 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
ret = dwc3_host_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize host\n");
break;
case USB_DR_MODE_OTG:
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
ret = dwc3_drd_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize dual-role\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize dual-role\n");
break;
default:
dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
@@ -1273,6 +1252,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
hird_threshold = 12;
dwc->maximum_speed = usb_get_maximum_speed(dev);
+ dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
@@ -1444,6 +1424,42 @@ static void dwc3_check_params(struct dwc3 *dwc)
}
break;
}
+
+ /*
+ * Currently the controller does not have visibility into the HW
+ * parameter to determine the maximum number of lanes the HW supports.
+ * If the number of lanes is not specified in the device property, then
+ * set the default to support dual-lane for DWC_usb32 and single-lane
+ * for DWC_usb31 for super-speed-plus.
+ */
+ if (dwc->maximum_speed == USB_SPEED_SUPER_PLUS) {
+ switch (dwc->max_ssp_rate) {
+ case USB_SSP_GEN_2x1:
+ if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_GEN1)
+ dev_warn(dev, "UDC only supports Gen 1\n");
+ break;
+ case USB_SSP_GEN_1x2:
+ case USB_SSP_GEN_2x2:
+ if (DWC3_IP_IS(DWC31))
+ dev_warn(dev, "UDC only supports single lane\n");
+ break;
+ case USB_SSP_GEN_UNKNOWN:
+ default:
+ switch (hwparam_gen) {
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
+ if (DWC3_IP_IS(DWC32))
+ dwc->max_ssp_rate = USB_SSP_GEN_2x2;
+ else
+ dwc->max_ssp_rate = USB_SSP_GEN_2x1;
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
+ if (DWC3_IP_IS(DWC32))
+ dwc->max_ssp_rate = USB_SSP_GEN_1x2;
+ break;
+ }
+ break;
+ }
+ }
}
static int dwc3_probe(struct platform_device *pdev)
@@ -1490,7 +1506,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
- dwc->reset = devm_reset_control_array_get(dev, true, true);
+ dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset))
return PTR_ERR(dwc->reset);
@@ -1555,8 +1571,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init(dwc);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize core: %d\n", ret);
+ dev_err_probe(dev, ret, "failed to initialize core\n");
goto err4;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1b241f937d8f..052b20d52651 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -386,6 +386,8 @@
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
/* Device Configuration Register */
+#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */
+
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -459,6 +461,8 @@
#define DWC3_DEVTEN_USBRSTEN BIT(1)
#define DWC3_DEVTEN_DISCONNEVTEN BIT(0)
+#define DWC3_DSTS_CONNLANES(n) (((n) >> 30) & 0x3) /* DWC_usb32 only */
+
/* Device Status Register */
#define DWC3_DSTS_DCNRD BIT(29)
@@ -964,6 +968,10 @@ struct dwc3_scratchpad_array {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
+ * @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count
+ * @gadget_max_speed: maximum gadget speed requested
+ * @gadget_ssp_rate: Gadget driver's maximum supported SuperSpeed Plus signaling
+ * rate and lane count.
* @ip: controller's ID
* @revision: controller's version of an IP
* @version_type: VERSIONTYPE register contents, a sub release of a revision
@@ -1126,6 +1134,9 @@ struct dwc3 {
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
+ u32 gadget_max_speed;
+ enum usb_ssp_rate max_ssp_rate;
+ enum usb_ssp_rate gadget_ssp_rate;
u32 ip;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index ee44321fee38..c7ef218e7a8c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -605,8 +605,23 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1);
if (desc->bInterval) {
- params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1);
- dep->interval = 1 << (desc->bInterval - 1);
+ u8 bInterval_m1;
+
+ /*
+ * Valid range for DEPCFG.bInterval_m1 is from 0 to 13, and it
+ * must be set to 0 when the controller operates in full-speed.
+ */
+ bInterval_m1 = min_t(u8, desc->bInterval - 1, 13);
+ if (dwc->gadget->speed == USB_SPEED_FULL)
+ bInterval_m1 = 0;
+
+ if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+ dwc->gadget->speed == USB_SPEED_FULL)
+ dep->interval = desc->bInterval;
+ else
+ dep->interval = 1 << (desc->bInterval - 1);
+
+ params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1);
}
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
@@ -768,8 +783,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
trace_dwc3_gadget_ep_disable(dep);
- dwc3_remove_requests(dwc, dep);
-
/* make sure HW endpoint isn't stalled */
if (dep->flags & DWC3_EP_STALL)
__dwc3_gadget_ep_set_halt(dep, 0, false);
@@ -778,16 +791,18 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
reg &= ~DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
- dep->stream_capable = false;
- dep->type = 0;
- dep->flags = 0;
-
/* Clear out the ep descriptors for non-ep0 */
if (dep->number > 1) {
dep->endpoint.comp_desc = NULL;
dep->endpoint.desc = NULL;
}
+ dwc3_remove_requests(dwc, dep);
+
+ dep->stream_capable = false;
+ dep->type = 0;
+ dep->flags = 0;
+
return 0;
}
@@ -1602,7 +1617,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
- if (!dep->endpoint.desc || !dwc->pullups_connected) {
+ if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
return -ESHUTDOWN;
@@ -2038,6 +2053,102 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
}
}
+static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
+{
+ enum usb_ssp_rate ssp_rate = dwc->gadget_ssp_rate;
+ u32 reg;
+
+ if (ssp_rate == USB_SSP_GEN_UNKNOWN)
+ ssp_rate = dwc->max_ssp_rate;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_SPEED_MASK;
+ reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+ if (ssp_rate == USB_SSP_GEN_1x2)
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else if (dwc->max_ssp_rate != USB_SSP_GEN_1x2)
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+
+ if (ssp_rate != USB_SSP_GEN_2x1 &&
+ dwc->max_ssp_rate != USB_SSP_GEN_2x1)
+ reg |= DWC3_DCFG_NUMLANES(1);
+
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
+{
+ enum usb_device_speed speed;
+ u32 reg;
+
+ speed = dwc->gadget_max_speed;
+ if (speed == USB_SPEED_UNKNOWN || speed > dwc->maximum_speed)
+ speed = dwc->maximum_speed;
+
+ if (speed == USB_SPEED_SUPER_PLUS &&
+ DWC3_IP_IS(DWC32)) {
+ __dwc3_gadget_set_ssp_rate(dwc);
+ return;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+ /*
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
+ !dwc->dis_metastability_quirk) {
+ reg |= DWC3_DCFG_SUPERSPEED;
+ } else {
+ switch (speed) {
+ case USB_SPEED_LOW:
+ reg |= DWC3_DCFG_LOWSPEED;
+ break;
+ case USB_SPEED_FULL:
+ reg |= DWC3_DCFG_FULLSPEED;
+ break;
+ case USB_SPEED_HIGH:
+ reg |= DWC3_DCFG_HIGHSPEED;
+ break;
+ case USB_SPEED_SUPER:
+ reg |= DWC3_DCFG_SUPERSPEED;
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ if (DWC3_IP_IS(DWC3))
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ break;
+ default:
+ dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+
+ if (DWC3_IP_IS(DWC3))
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ }
+ }
+
+ if (DWC3_IP_IS(DWC32) &&
+ speed > USB_SPEED_UNKNOWN &&
+ speed < USB_SPEED_SUPER_PLUS)
+ reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
u32 reg;
@@ -2060,6 +2171,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
if (dwc->has_hibernation)
reg |= DWC3_DCTL_KEEP_CONNECT;
+ __dwc3_gadget_set_speed(dwc);
dwc->pullups_connected = true;
} else {
reg &= ~DWC3_DCTL_RUN_STOP;
@@ -2110,6 +2222,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
}
}
+ /*
+ * Check the return value for successful resume, or error. For a
+ * successful resume, the DWC3 runtime PM resume routine will handle
+ * the run stop sequence, so avoid duplicate operations here.
+ */
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (!ret || ret < 0) {
+ pm_runtime_put(dwc->dev);
+ return 0;
+ }
+
/*
* Synchronize any pending event handling before executing the controller
* halt routine.
@@ -2124,6 +2247,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
if (!is_on) {
u32 count;
+ dwc->connected = false;
/*
* In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
* Section 4.1.8 Table 4-7, it states that for a device-initiated
@@ -2154,6 +2278,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put(dwc->dev);
return ret;
}
@@ -2163,8 +2288,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
u32 reg;
/* Enable all but Start and End of Frame IRQs */
- reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
- DWC3_DEVTEN_EVNTOVERFLOWEN |
+ reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
DWC3_DEVTEN_CMDCMPLTEN |
DWC3_DEVTEN_ERRTICERREN |
DWC3_DEVTEN_WKUPEVTEN |
@@ -2302,7 +2426,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- int ret = 0;
+ int ret;
int irq;
irq = dwc->irq_gadget;
@@ -2311,29 +2435,14 @@ static int dwc3_gadget_start(struct usb_gadget *g,
if (ret) {
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
irq, ret);
- goto err0;
+ return ret;
}
spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver) {
- dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget->name,
- dwc->gadget_driver->driver.name);
- ret = -EBUSY;
- goto err1;
- }
-
dwc->gadget_driver = driver;
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
-
-err1:
- spin_unlock_irqrestore(&dwc->lock, flags);
- free_irq(irq, dwc);
-
-err0:
- return ret;
}
static void __dwc3_gadget_stop(struct dwc3 *dwc)
@@ -2401,62 +2510,34 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- u32 reg;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
- reg &= ~(DWC3_DCFG_SPEED_MASK);
-
- /*
- * WORKAROUND: DWC3 revision < 2.20a have an issue
- * which would cause metastability state on Run/Stop
- * bit if we try to force the IP to USB2-only mode.
- *
- * Because of that, we cannot configure the IP to any
- * speed other than the SuperSpeed
- *
- * Refers to:
- *
- * STAR#9000525659: Clock Domain Crossing on DCTL in
- * USB 2.0 Mode
- */
- if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
- !dwc->dis_metastability_quirk) {
- reg |= DWC3_DCFG_SUPERSPEED;
- } else {
- switch (speed) {
- case USB_SPEED_LOW:
- reg |= DWC3_DCFG_LOWSPEED;
- break;
- case USB_SPEED_FULL:
- reg |= DWC3_DCFG_FULLSPEED;
- break;
- case USB_SPEED_HIGH:
- reg |= DWC3_DCFG_HIGHSPEED;
- break;
- case USB_SPEED_SUPER:
- reg |= DWC3_DCFG_SUPERSPEED;
- break;
- case USB_SPEED_SUPER_PLUS:
- if (DWC3_IP_IS(DWC3))
- reg |= DWC3_DCFG_SUPERSPEED;
- else
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- break;
- default:
- dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+ dwc->gadget_max_speed = speed;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
- if (DWC3_IP_IS(DWC3))
- reg |= DWC3_DCFG_SUPERSPEED;
- else
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- }
- }
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
+ enum usb_ssp_rate rate)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->gadget_max_speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget_ssp_rate = rate;
spin_unlock_irqrestore(&dwc->lock, flags);
}
+static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ if (dwc->usb2_phy)
+ return usb_phy_set_power(dwc->usb2_phy, mA);
+
+ return 0;
+}
+
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
@@ -2465,7 +2546,9 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
.udc_set_speed = dwc3_gadget_set_speed,
+ .udc_set_ssp_rate = dwc3_gadget_set_ssp_rate,
.get_config_params = dwc3_gadget_config_params,
+ .vbus_draw = dwc3_gadget_vbus_draw,
};
/* -------------------------------------------------------------------------- */
@@ -3239,8 +3322,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
- dwc->connected = true;
-
/*
* WORKAROUND: DWC3 revisions <1.88a have an issue which
* would cause a missing Disconnect Event if there's a
@@ -3280,6 +3361,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
* transfers."
*/
dwc3_stop_active_transfers(dwc);
+ dwc->connected = true;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
@@ -3298,12 +3380,18 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
struct dwc3_ep *dep;
int ret;
u32 reg;
+ u8 lanes = 1;
u8 speed;
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
+ if (DWC3_IP_IS(DWC32))
+ lanes = DWC3_DSTS_CONNLANES(reg) + 1;
+
+ dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
+
/*
* RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
* each time on Connect Done.
@@ -3318,6 +3406,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dwc->gadget->ep0->maxpacket = 512;
dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+
+ if (lanes > 1)
+ dwc->gadget->ssp_rate = USB_SSP_GEN_2x2;
+ else
+ dwc->gadget->ssp_rate = USB_SSP_GEN_2x1;
break;
case DWC3_DSTS_SUPERSPEED:
/*
@@ -3339,6 +3432,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dwc->gadget->ep0->maxpacket = 512;
dwc->gadget->speed = USB_SPEED_SUPER;
+
+ if (lanes > 1) {
+ dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget->ssp_rate = USB_SSP_GEN_1x2;
+ }
break;
case DWC3_DSTS_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
@@ -3833,6 +3931,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dev->platform_data = dwc;
dwc->gadget->ops = &dwc3_gadget_ops;
dwc->gadget->speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
dwc->gadget->sg_supported = true;
dwc->gadget->name = "dwc3-gadget";
dwc->gadget->lpm_capable = true;
@@ -3859,6 +3958,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->revision);
dwc->gadget->max_speed = dwc->maximum_speed;
+ dwc->gadget->max_ssp_rate = dwc->max_ssp_rate;
/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -3875,7 +3975,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err5;
}
- dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
+ if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
+ dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate);
+ else
+ dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 37/64] usb: dwc3: import from kernel v5.13
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (35 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 36/64] usb: dwc3: import from kernel v5.12 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 38/64] usb: dwc3: import from kernel v5.14 Jens Wiklander
` (30 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.12 to v5.13.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.12..v5.13
Commits imported:
4bf584a03eec usb: dwc3: core: fix kernel panic when do reboot
d00889080ab6 usb: dwc3: ep0: fix NULL pointer exception
4d2aa178d2ad usb: dwc3-meson-g12a: fix usb2 PHY glue init when phy0 is disabled
1d0d3d818eaf usb: dwc3: meson-g12a: Disable the regulator in the error handling path of the probe
8f11fe7e4068 Revert "usb: dwc3: core: Add shutdown callback for dwc3"
8d396bb0a5b6 usb: dwc3: debugfs: Add and remove endpoint dirs dynamically
03715ea2e3db usb: dwc3: gadget: Bail from dwc3_gadget_exit() if dwc->gadget is NULL
8212937305f8 usb: dwc3: gadget: Disable gadget IRQ during pullup disable
25dda9fc56bd usb: dwc3: gadget: Properly track pending and queued SG
e17b02d49709 usb: dwc3: omap: improve extcon initialization
0b2b149e918f usb: dwc3: imx8mp: fix error return code in dwc3_imx8mp_probe()
b96992081fde usb: dwc3: imx8mp: detect dwc3 core node via compatible string
18ffa988dbae usb: dwc3: gadget: Return success always for kick transfer in ep queue
bb9c74a5bd14 usb: dwc3: gadget: Free gadget structure only after freeing endpoints
6f26ebb79a84 usb: dwc3: gadget: Rename EOPF event macros to Suspend
d1d90dd27254 usb: dwc3: gadget: Enable suspend events
9cbc7eb17cdf usb: dwc3: core: Add missing GHWPARAMS9 doc
04357fafea9c usb: dwc3: pci: Enable usb2-gadget-lpm-disable for Intel Merrifield
ddae7979cdd5 usb: dwc3: gadget: Handle DEV_TXF_FLUSH_BYPASS capability
16710380d3aa usb: dwc3: Capture new capability register GHWPARAMS9
f88359e1588b usb: dwc3: core: Do core softreset when switch mode
3232a3ce55ed usb: dwc3: gadget: Remove FS bInterval_m1 limitation
c560e76319a9 usb: dwc3: gadget: Fix START_TRANSFER link state check
475e8be53d04 usb: dwc3: gadget: Check for disabled LPM quirk
568262bf5492 usb: dwc3: core: Add shutdown callback for dwc3
e66bbfb0fbbe usb: dwc3: gadget: Ignore Packet Pending bit
3db53374405f usb: dwc3: qcom: Detect DWC3 DT-nodes using compatible string
dc1e7e9a27e0 usb: dwc3: qcom: Remove redundant dev_err call in dwc3_qcom_probe()
124b11cc4f62 usb: dwc3: xilinx: Remove the extra freeing of clocks
3a2a68ecb25e usb: dwc3: Resolve kernel-doc warning for Xilinx DWC3 driver
782de5e7190d usb: dwc3: pci: add support for the Intel Alder Lake-M
de800f290dfa Merge 5.12-rc6 into usb-next
04dd6e76b228 usb: dwc3: add cancelled reasons for dwc3 requests
048b14e1f28b usb: dwc3: exynos: fix incorrect kernel-doc comment syntax
27088e00b623 usb: dwc3: fix incorrect kernel-doc comment syntax in files
c9714d65eac8 usb: dwc3: st: fix incorrect kernel-doc comment syntax in file
ed577c325b64 usb: dwc3: imx8mp: fix incorrect kernel-doc comment syntax
d00be779cc50 usb: dwc3: Create helper function getting MDWIDTH
8a5b5c3c1634 usb: dwc3: gadget: modify the scale in vbus_draw callback
3fc63d0724bb usb: dwc3: trace: Print register read and write offset
71ca43f30df9 usb: dwc3: gadget: Ignore EP queue requests during bus reset
02fa4b980245 usb: dwc3: gadget: Avoid continuing preparing TRBs during teardown
84770f028fab usb: dwc3: Add driver for Xilinx platforms
0c59f678fcfc usb: dwc3: gadget: Remove invalid low-speed setting
f85142af3641 Merge 5.12-rc4 into usb-next
064ece8d7ca7 drivers: usb: Fix a typo in dwc3-qcom.c
aa403f257e99 Merge 5.12-rc3 into usb-next
0f3edf99c239 usb: dwc3: document usb_psy in struct dwc3
b0bf77cd389d usb: dwc3: Fix dereferencing of null dwc->usb_psy
99288de36020 usb: dwc3: add an alternate path in vbus_draw callback
6f0764b5adea usb: dwc3: add a power supply for current control
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 50 ++++++++++-
drivers/usb/dwc3/core.h | 60 +++++++++++---
drivers/usb/dwc3/debug.h | 13 +--
drivers/usb/dwc3/ep0.c | 3 +
drivers/usb/dwc3/gadget.c | 169 ++++++++++++++++++++++++++------------
drivers/usb/dwc3/gadget.h | 6 +-
drivers/usb/dwc3/io.h | 2 +-
7 files changed, 229 insertions(+), 74 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f2448d0a9d39..4ac397e43e19 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -114,6 +114,8 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
dwc->current_dr_role = mode;
}
+static int dwc3_core_soft_reset(struct dwc3 *dwc);
+
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
@@ -121,6 +123,8 @@ static void __dwc3_set_mode(struct work_struct *work)
int ret;
u32 reg;
+ mutex_lock(&dwc->mutex);
+
pm_runtime_get_sync(dwc->dev);
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
@@ -154,6 +158,25 @@ static void __dwc3_set_mode(struct work_struct *work)
break;
}
+ /* For DRD host or device mode only */
+ if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) {
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg |= DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /*
+ * Wait for internal clocks to synchronized. DWC_usb31 and
+ * DWC_usb32 may need at least 50ms (less for DWC_usb3). To
+ * keep it consistent across different IPs, let's wait up to
+ * 100ms before clearing GCTL.CORESOFTRESET.
+ */
+ msleep(100);
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_prtcap(dwc, dwc->desired_dr_role);
@@ -178,6 +201,8 @@ static void __dwc3_set_mode(struct work_struct *work)
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_core_soft_reset(dwc);
+
dwc3_event_buffers_setup(dwc);
if (dwc->usb2_phy)
@@ -200,6 +225,7 @@ static void __dwc3_set_mode(struct work_struct *work)
out:
pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev);
+ mutex_unlock(&dwc->mutex);
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -544,6 +570,9 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+
+ if (DWC3_IP_IS(DWC32))
+ parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
@@ -1238,6 +1267,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
+ const char *usb_psy_name;
+ int ret;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xf;
@@ -1263,6 +1294,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
else
dwc->sysdev = dwc->dev;
+ ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
+ if (ret >= 0) {
+ dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
+ if (!dwc->usb_psy)
+ dev_err(dev, "couldn't get usb power supply\n");
+ }
+
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
device_property_read_u8(dev, "snps,lpm-nyet-threshold",
@@ -1277,6 +1315,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,usb3_lpm_capable");
dwc->usb2_lpm_disable = device_property_read_bool(dev,
"snps,usb2-lpm-disable");
+ dwc->usb2_gadget_lpm_disable = device_property_read_bool(dev,
+ "snps,usb2-gadget-lpm-disable");
device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
&rx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,rx-max-burst-prd",
@@ -1385,7 +1425,6 @@ static void dwc3_check_params(struct dwc3 *dwc)
/* Check the maximum_speed parameter */
switch (dwc->maximum_speed) {
- case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
break;
@@ -1543,6 +1582,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_cache_hwparams(dwc);
spin_lock_init(&dwc->lock);
+ mutex_init(&dwc->mutex);
pm_runtime_set_active(dev);
pm_runtime_use_autosuspend(dev);
@@ -1619,6 +1659,9 @@ disable_clks:
assert_reset:
reset_control_assert(dwc->reset);
+ if (dwc->usb_psy)
+ power_supply_put(dwc->usb_psy);
+
return ret;
}
@@ -1628,8 +1671,8 @@ static int dwc3_remove(struct platform_device *pdev)
pm_runtime_get_sync(&pdev->dev);
- dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
+ dwc3_debugfs_exit(dwc);
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
@@ -1641,6 +1684,9 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
+ if (dwc->usb_psy)
+ power_supply_put(dwc->usb_psy);
+
return 0;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 052b20d52651..c5d5760cdf53 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/bitops.h>
@@ -30,6 +31,8 @@
#include <linux/phy/phy.h>
+#include <linux/power_supply.h>
+
#define DWC3_MSG_MAX 500
/* Global constants */
@@ -54,7 +57,7 @@
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
#define DWC3_DEVICE_EVENT_WAKEUP 4
#define DWC3_DEVICE_EVENT_HIBER_REQ 5
-#define DWC3_DEVICE_EVENT_EOPF 6
+#define DWC3_DEVICE_EVENT_SUSPEND 6
#define DWC3_DEVICE_EVENT_SOF 7
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
@@ -140,6 +143,7 @@
#define DWC3_GHWPARAMS8 0xc600
#define DWC3_GUCTL3 0xc60c
#define DWC3_GFLADJ 0xc630
+#define DWC3_GHWPARAMS9 0xc680
/* Device Registers */
#define DWC3_DCFG 0xc700
@@ -375,6 +379,9 @@
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
+/* Global HWPARAMS9 Register */
+#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS BIT(0)
+
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
@@ -396,12 +403,12 @@
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
#define DWC3_DCFG_FULLSPEED BIT(0)
-#define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
#define DWC3_DCFG_LPM_CAP BIT(22)
+#define DWC3_DCFG_IGNSTRMPP BIT(23)
/* Device Control Register */
#define DWC3_DCTL_RUN_STOP BIT(31)
@@ -453,7 +460,7 @@
#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
#define DWC3_DEVTEN_ERRTICERREN BIT(9)
#define DWC3_DEVTEN_SOFEN BIT(7)
-#define DWC3_DEVTEN_EOPFEN BIT(6)
+#define DWC3_DEVTEN_U3L2L1SUSPEN BIT(6)
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
@@ -490,7 +497,6 @@
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
#define DWC3_DSTS_FULLSPEED BIT(0)
-#define DWC3_DSTS_LOWSPEED (2 << 0)
/* Device Generic Command Register */
#define DWC3_DGCMD_SET_LMP 0x01
@@ -844,6 +850,7 @@ struct dwc3_trb {
* @hwparams6: GHWPARAMS6
* @hwparams7: GHWPARAMS7
* @hwparams8: GHWPARAMS8
+ * @hwparams9: GHWPARAMS9
*/
struct dwc3_hwparams {
u32 hwparams0;
@@ -855,13 +862,12 @@ struct dwc3_hwparams {
u32 hwparams6;
u32 hwparams7;
u32 hwparams8;
+ u32 hwparams9;
};
/* HWPARAMS0 */
#define DWC3_MODE(n) ((n) & 0x7)
-#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8)
-
/* HWPARAMS1 */
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
@@ -908,11 +914,13 @@ struct dwc3_request {
unsigned int remaining;
unsigned int status;
-#define DWC3_REQUEST_STATUS_QUEUED 0
-#define DWC3_REQUEST_STATUS_STARTED 1
-#define DWC3_REQUEST_STATUS_CANCELLED 2
-#define DWC3_REQUEST_STATUS_COMPLETED 3
-#define DWC3_REQUEST_STATUS_UNKNOWN -1
+#define DWC3_REQUEST_STATUS_QUEUED 0
+#define DWC3_REQUEST_STATUS_STARTED 1
+#define DWC3_REQUEST_STATUS_DISCONNECTED 2
+#define DWC3_REQUEST_STATUS_DEQUEUED 3
+#define DWC3_REQUEST_STATUS_STALLED 4
+#define DWC3_REQUEST_STATUS_COMPLETED 5
+#define DWC3_REQUEST_STATUS_UNKNOWN -1
u8 epnum;
struct dwc3_trb *trb;
@@ -946,6 +954,7 @@ struct dwc3_scratchpad_array {
* @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
+ * @mutex: for mode switching
* @dev: pointer to our struct device
* @sysdev: pointer to the DMA-capable device
* @xhci: pointer to our xHCI child
@@ -986,6 +995,7 @@ struct dwc3_scratchpad_array {
* @role_sw: usb_role_switch handle
* @role_switch_default_mode: default operation mode of controller while
* usb role is USB_ROLE_NONE.
+ * @usb_psy: pointer to power supply interface.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -1034,7 +1044,8 @@ struct dwc3_scratchpad_array {
* @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
* not needed for DWC_usb31 version 1.70a-ea06 and below
* @usb3_lpm_capable: set if hadrware supports Link Power Management
- * @usb2_lpm_disable: set to disable usb2 lpm
+ * @usb2_lpm_disable: set to disable usb2 lpm for host
+ * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -1085,6 +1096,9 @@ struct dwc3 {
/* device lock */
spinlock_t lock;
+ /* mode switching lock */
+ struct mutex mutex;
+
struct device *dev;
struct device *sysdev;
@@ -1125,6 +1139,8 @@ struct dwc3 {
struct usb_role_switch *role_sw;
enum usb_dr_mode role_switch_default_mode;
+ struct power_supply *usb_psy;
+
u32 fladj;
u32 irq_gadget;
u32 otg_irq;
@@ -1238,6 +1254,7 @@ struct dwc3 {
unsigned dis_start_transfer_quirk:1;
unsigned usb3_lpm_capable:1;
unsigned usb2_lpm_disable:1;
+ unsigned usb2_gadget_lpm_disable:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
@@ -1358,7 +1375,7 @@ struct dwc3_event_depevt {
* 3 - ULStChng
* 4 - WkUpEvt
* 5 - Reserved
- * 6 - EOPF
+ * 6 - Suspend (EOPF on revisions 2.10a and prior)
* 7 - SOF
* 8 - Reserved
* 9 - ErrticErr
@@ -1455,6 +1472,23 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
(!(_ip##_VERSIONTYPE_##_to) || \
dwc->version_type <= _ip##_VERSIONTYPE_##_to))
+/**
+ * dwc3_mdwidth - get MDWIDTH value in bits
+ * @dwc: pointer to our context structure
+ *
+ * Return MDWIDTH configuration value in bits.
+ */
+static inline u32 dwc3_mdwidth(struct dwc3 *dwc)
+{
+ u32 mdwidth;
+
+ mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+ return mdwidth;
+}
+
bool dwc3_has_imod(struct dwc3 *dwc);
int dwc3_event_buffers_setup(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 8ab394942360..d223c54115f4 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
-/**
+/*
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
@@ -221,8 +221,8 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
snprintf(str, size, "WakeUp [%s]",
dwc3_gadget_link_string(state));
break;
- case DWC3_DEVICE_EVENT_EOPF:
- snprintf(str, size, "End-Of-Frame [%s]",
+ case DWC3_DEVICE_EVENT_SUSPEND:
+ snprintf(str, size, "Suspend [%s]",
dwc3_gadget_link_string(state));
break;
case DWC3_DEVICE_EVENT_SOF:
@@ -353,8 +353,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
return "Wake-Up";
case DWC3_DEVICE_EVENT_HIBER_REQ:
return "Hibernation";
- case DWC3_DEVICE_EVENT_EOPF:
- return "End of Periodic Frame";
+ case DWC3_DEVICE_EVENT_SUSPEND:
+ return "Suspend";
case DWC3_DEVICE_EVENT_SOF:
return "Start of Frame";
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
@@ -413,9 +413,12 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
#ifdef CONFIG_DEBUG_FS
+extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep);
extern void dwc3_debugfs_init(struct dwc3 *d);
extern void dwc3_debugfs_exit(struct dwc3 *d);
#else
+static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
+{ }
static inline void dwc3_debugfs_init(struct dwc3 *d)
{ }
static inline void dwc3_debugfs_exit(struct dwc3 *d)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 8b668ef46f7f..3cd294264372 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -292,6 +292,9 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
epnum |= 1;
dep = dwc->eps[epnum];
+ if (dep == NULL)
+ return NULL;
+
if (dep->flags & DWC3_EP_ENABLED)
return dep;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c7ef218e7a8c..f14c2aa83759 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -308,13 +308,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- int needs_wakeup;
+ int link_state;
- needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
- dwc->link_state == DWC3_LINK_STATE_U2 ||
- dwc->link_state == DWC3_LINK_STATE_U3);
-
- if (unlikely(needs_wakeup)) {
+ link_state = dwc3_gadget_get_link_state(dwc);
+ if (link_state == DWC3_LINK_STATE_U1 ||
+ link_state == DWC3_LINK_STATE_U2 ||
+ link_state == DWC3_LINK_STATE_U3) {
ret = __dwc3_gadget_wakeup(dwc);
dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
ret);
@@ -608,12 +607,14 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
u8 bInterval_m1;
/*
- * Valid range for DEPCFG.bInterval_m1 is from 0 to 13, and it
- * must be set to 0 when the controller operates in full-speed.
+ * Valid range for DEPCFG.bInterval_m1 is from 0 to 13.
+ *
+ * NOTE: The programming guide incorrectly stated bInterval_m1
+ * must be set to 0 when operating in fullspeed. Internally the
+ * controller does not have this limitation. See DWC_usb3x
+ * programming guide section 3.2.2.1.
*/
bInterval_m1 = min_t(u8, desc->bInterval - 1, 13);
- if (dwc->gadget->speed == USB_SPEED_FULL)
- bInterval_m1 = 0;
if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
dwc->gadget->speed == USB_SPEED_FULL)
@@ -729,8 +730,16 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
* All stream eps will reinitiate stream on NoStream
* rejection until we can determine that the host can
* prime after the first transfer.
+ *
+ * However, if the controller is capable of
+ * TXF_FLUSH_BYPASS, then IN direction endpoints will
+ * automatically restart the stream without the driver
+ * initiation.
*/
- dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+ if (!dep->direction ||
+ !(dwc->hwparams.hwparams9 &
+ DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS))
+ dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
}
}
@@ -1235,6 +1244,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
req->start_sg = sg_next(s);
req->num_queued_sgs++;
+ req->num_pending_sgs--;
/*
* The number of pending SG entries may not correspond to the
@@ -1242,7 +1252,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
* don't include unused SG entries.
*/
if (length == 0) {
- req->num_pending_sgs -= req->request.num_mapped_sgs - req->num_queued_sgs;
+ req->num_pending_sgs = 0;
break;
}
@@ -1402,7 +1412,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
dwc3_stop_active_transfer(dep, true, true);
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
- dwc3_gadget_move_cancelled_request(req);
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
/* If ep isn't started, then there's no end transfer pending */
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
@@ -1675,7 +1685,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
}
}
- return __dwc3_gadget_kick_transfer(dep);
+ __dwc3_gadget_kick_transfer(dep);
+
+ return 0;
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
@@ -1729,10 +1741,25 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
{
struct dwc3_request *req;
struct dwc3_request *tmp;
+ struct dwc3 *dwc = dep->dwc;
list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
dwc3_gadget_ep_skip_trbs(dep, req);
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ switch (req->status) {
+ case DWC3_REQUEST_STATUS_DISCONNECTED:
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ break;
+ case DWC3_REQUEST_STATUS_DEQUEUED:
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ break;
+ case DWC3_REQUEST_STATUS_STALLED:
+ dwc3_gadget_giveback(dep, req, -EPIPE);
+ break;
+ default:
+ dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ break;
+ }
}
}
@@ -1776,7 +1803,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
* cancelled.
*/
list_for_each_entry_safe(r, t, &dep->started_list, list)
- dwc3_gadget_move_cancelled_request(r);
+ dwc3_gadget_move_cancelled_request(r,
+ DWC3_REQUEST_STATUS_DEQUEUED);
dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
@@ -1848,7 +1876,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
dwc3_stop_active_transfer(dep, true, true);
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
- dwc3_gadget_move_cancelled_request(req);
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
@@ -1973,6 +2001,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
case DWC3_LINK_STATE_RESET:
case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
+ case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */
+ case DWC3_LINK_STATE_U1:
case DWC3_LINK_STATE_RESUME:
break;
default:
@@ -2113,9 +2143,6 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
reg |= DWC3_DCFG_SUPERSPEED;
} else {
switch (speed) {
- case USB_SPEED_LOW:
- reg |= DWC3_DCFG_LOWSPEED;
- break;
case USB_SPEED_FULL:
reg |= DWC3_DCFG_FULLSPEED;
break;
@@ -2234,13 +2261,10 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
}
/*
- * Synchronize any pending event handling before executing the controller
- * halt routine.
+ * Synchronize and disable any further event handling while controller
+ * is being enabled/disabled.
*/
- if (!is_on) {
- dwc3_gadget_disable_irq(dwc);
- synchronize_irq(dwc->irq_gadget);
- }
+ disable_irq(dwc->irq_gadget);
spin_lock_irqsave(&dwc->lock, flags);
@@ -2278,6 +2302,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
+ enable_irq(dwc->irq_gadget);
+
pm_runtime_put(dwc->dev);
return ret;
@@ -2299,6 +2325,10 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
reg |= DWC3_DEVTEN_ULSTCNGEN;
+ /* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
+ reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
+
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}
@@ -2340,9 +2370,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
u32 reg;
ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
- mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
- if (DWC3_IP_IS(DWC32))
- mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+ mdwidth = dwc3_mdwidth(dwc);
nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
nump = min_t(u32, nump, 16);
@@ -2388,6 +2416,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_gadget_setup_nump(dwc);
+ /*
+ * Currently the controller handles single stream only. So, Ignore
+ * Packet Pending bit for stream selection and don't search for another
+ * stream if the host sends Data Packet with PP=0 (for OUT direction) or
+ * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
+ * the stream performance.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_IGNSTRMPP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -2531,11 +2570,19 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
{
struct dwc3 *dwc = gadget_to_dwc(g);
+ union power_supply_propval val = {0};
+ int ret;
if (dwc->usb2_phy)
return usb_phy_set_power(dwc->usb2_phy, mA);
- return 0;
+ if (!dwc->usb_psy)
+ return -EOPNOTSUPP;
+
+ val.intval = 1000 * mA;
+ ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+
+ return ret;
}
static const struct usb_gadget_ops dwc3_gadget_ops = {
@@ -2571,12 +2618,10 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
- int mdwidth;
+ u32 mdwidth;
int size;
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
- if (DWC3_IP_IS(DWC32))
- mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+ mdwidth = dwc3_mdwidth(dwc);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
@@ -2618,12 +2663,10 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
- int mdwidth;
+ u32 mdwidth;
int size;
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
- if (DWC3_IP_IS(DWC32))
- mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+ mdwidth = dwc3_mdwidth(dwc);
/* MDWIDTH is represented in bits, convert to bytes */
mdwidth /= 8;
@@ -2710,6 +2753,8 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->started_list);
INIT_LIST_HEAD(&dep->cancelled_list);
+ dwc3_debugfs_create_endpoint_dir(dep);
+
return 0;
}
@@ -2753,6 +2798,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
list_del(&dep->endpoint.ep_list);
}
+ debugfs_remove_recursive(debugfs_lookup(dep->name, dwc->root));
kfree(dep);
}
}
@@ -2830,15 +2876,15 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
struct scatterlist *sg = req->sg;
struct scatterlist *s;
- unsigned int pending = req->num_pending_sgs;
+ unsigned int num_queued = req->num_queued_sgs;
unsigned int i;
int ret = 0;
- for_each_sg(sg, s, pending, i) {
+ for_each_sg(sg, s, num_queued, i) {
trb = &dep->trb_pool[dep->trb_dequeue];
req->sg = sg_next(s);
- req->num_pending_sgs--;
+ req->num_queued_sgs--;
ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req,
trb, event, status, true);
@@ -2861,7 +2907,7 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
{
- return req->num_pending_sgs == 0;
+ return req->num_pending_sgs == 0 && req->num_queued_sgs == 0;
}
static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
@@ -2870,7 +2916,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
{
int ret;
- if (req->num_pending_sgs)
+ if (req->request.num_mapped_sgs)
ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event,
status);
else
@@ -2913,6 +2959,11 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
{
struct dwc3_request *req;
+ struct dwc3 *dwc = dep->dwc;
+
+ if (!dep->endpoint.desc || !dwc->pullups_connected ||
+ !dwc->connected)
+ return false;
if (!list_empty(&dep->pending_list))
return true;
@@ -3322,6 +3373,15 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
+ /*
+ * Ideally, dwc3_reset_gadget() would trigger the function
+ * drivers to stop any active transfers through ep disable.
+ * However, for functions which defer ep disable, such as mass
+ * storage, we will need to rely on the call to stop active
+ * transfers here, and avoid allowing of request queuing.
+ */
+ dwc->connected = false;
+
/*
* WORKAROUND: DWC3 revisions <1.88a have an issue which
* would cause a missing Disconnect Event if there's a
@@ -3448,11 +3508,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc->gadget->ep0->maxpacket = 64;
dwc->gadget->speed = USB_SPEED_FULL;
break;
- case DWC3_DSTS_LOWSPEED:
- dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
- dwc->gadget->ep0->maxpacket = 8;
- dwc->gadget->speed = USB_SPEED_LOW;
- break;
}
dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket;
@@ -3460,6 +3515,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
/* Enable USB2 LPM Capability */
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
+ !dwc->usb2_gadget_lpm_disable &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -3486,6 +3542,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
+ if (dwc->usb2_gadget_lpm_disable) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -3687,7 +3749,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
- case DWC3_DEVICE_EVENT_EOPF:
+ case DWC3_DEVICE_EVENT_SUSPEND:
/* It changed to be suspend event for version 2.30a and above */
if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
/*
@@ -3934,7 +3996,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
dwc->gadget->sg_supported = true;
dwc->gadget->name = "dwc3-gadget";
- dwc->gadget->lpm_capable = true;
+ dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable;
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -3986,6 +4048,7 @@ err5:
dwc3_gadget_free_endpoints(dwc);
err4:
usb_put_gadget(dwc->gadget);
+ dwc->gadget = NULL;
err3:
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
@@ -4005,8 +4068,12 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
- usb_del_gadget_udc(dwc->gadget);
+ if (!dwc->gadget)
+ return;
+
+ usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
+ usb_put_gadget(dwc->gadget);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
kfree(dwc->setup_buf);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 0cd281949970..77df4b6d6c13 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -90,15 +90,17 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
/**
* dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
* @req: the request to be moved
+ * @reason: cancelled reason for the dwc3 request
*
* Caller should take care of locking. This function will move @req from its
* current list to the endpoint's cancelled_list.
*/
-static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
+static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req,
+ unsigned int reason)
{
struct dwc3_ep *dep = req->dep;
- req->status = DWC3_REQUEST_STATUS_CANCELLED;
+ req->status = reason;
list_move_tail(&req->list, &dep->cancelled_list);
}
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 76b73b116862..1e96ea339d48 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
-/**
+/*
* io.h - DesignWare USB3 DRD IO Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 38/64] usb: dwc3: import from kernel v5.14
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (36 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 37/64] usb: dwc3: import from kernel v5.13 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 39/64] usb: dwc3: import from kernel v5.15 Jens Wiklander
` (29 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.13 to v5.14.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.13..v5.14
Commits imported:
4a1e25c0a029 usb: dwc3: gadget: Stop EP0 transfers during pullup disable
51f1954ad853 usb: dwc3: gadget: Fix dwc3_calc_trbs_left()
664cc971fb25 Revert "usb: dwc3: gadget: Use list_replace_init() before traversing lists"
cb10f68ad815 usb: dwc3: gadget: Avoid runtime resume if disabling pullup
d25d85061bd8 usb: dwc3: gadget: Use list_replace_init() before traversing lists
40edb52298df usb: dwc3: avoid NULL access of usb_gadget_driver
84524d1232ec usb: dwc3: Fix debugfs creation flow
37fdb7c90f8c Merge tag 'v5.13-rc6' into usb-next
8562d5bfc0fc USB: dwc3: remove debugfs root dentry storage
45d39448b4d0 usb: dwc3: support 64 bit DMA in platform driver
2a042767814b usb: dwc3: core: fix kernel panic when do reboot
5ff90af9da8f usb: dwc3: debugfs: Add and remove endpoint dirs dynamically
aa10fab0f859 Merge 5.13-rc4 into usb-next
ab00a41e73dc usb: dwc3: trace: Remove unused fields in dwc3_log_trb
9fbf82c76414 Merge 5.13-rc2 into usb-next
457d22850b27 usb: dwc3: pci: Fix DEFINE for Intel Elkhart Lake
374ac7448caa usb: dwc3: remove repeated setting of current_dr_role
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 7 ++++-
drivers/usb/dwc3/core.h | 3 +-
drivers/usb/dwc3/ep0.c | 10 ++++---
drivers/usb/dwc3/gadget.c | 59 +++++++++++++++++++++++++++------------
4 files changed, 54 insertions(+), 25 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 4ac397e43e19..ba74ad7f6995 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1545,6 +1545,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
+ ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
+ if (ret)
+ return ret;
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset))
return PTR_ERR(dwc->reset);
@@ -1616,17 +1620,18 @@ static int dwc3_probe(struct platform_device *pdev)
}
dwc3_check_params(dwc);
+ dwc3_debugfs_init(dwc);
ret = dwc3_core_init_mode(dwc);
if (ret)
goto err5;
- dwc3_debugfs_init(dwc);
pm_runtime_put(dev);
return 0;
err5:
+ dwc3_debugfs_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
usb_phy_shutdown(dwc->usb2_phy);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index c5d5760cdf53..5991766239ba 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1013,7 +1013,6 @@ struct dwc3_scratchpad_array {
* @link_state: link state
* @speed: device speed (super, high, full, low)
* @hwparams: copy of hwparams registers
- * @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
* @dbg_lsp_select: current debug lsp mux register selection
* @test_mode: true when we're entering a USB test mode
@@ -1222,7 +1221,6 @@ struct dwc3 {
u8 num_eps;
struct dwc3_hwparams hwparams;
- struct dentry *root;
struct debugfs_regset32 *regset;
u32 dbg_lsp_select;
@@ -1281,6 +1279,7 @@ struct dwc3 {
unsigned dis_metastability_quirk:1;
unsigned dis_split_quirk:1;
+ unsigned async_callbacks:1;
u16 imod_interval;
};
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 3cd294264372..2f9e45eed228 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -597,11 +597,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- int ret;
+ int ret = -EINVAL;
- spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
- spin_lock(&dwc->lock);
+ if (dwc->async_callbacks) {
+ spin_unlock(&dwc->lock);
+ ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
+ spin_lock(&dwc->lock);
+ }
return ret;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f14c2aa83759..ccb68fe6202e 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -940,19 +940,19 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{
- struct dwc3_trb *tmp;
u8 trbs_left;
/*
- * If enqueue & dequeue are equal than it is either full or empty.
- *
- * One way to know for sure is if the TRB right before us has HWO bit
- * set or not. If it has, then we're definitely full and can't fit any
- * more transfers in our ring.
+ * If the enqueue & dequeue are equal then the TRB ring is either full
+ * or empty. It's considered full when there are DWC3_TRB_NUM-1 of TRBs
+ * pending to be processed by the driver.
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
- tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ /*
+ * If there is any request remained in the started_list at
+ * this point, that means there is no TRB available.
+ */
+ if (!list_empty(&dep->started_list))
return 0;
return DWC3_TRB_NUM - 1;
@@ -2243,10 +2243,19 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
- if (ret == 0) {
- dev_err(dwc->dev, "timed out waiting for SETUP phase\n");
- return -ETIMEDOUT;
- }
+ if (ret == 0)
+ dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
+ }
+
+ /*
+ * Avoid issuing a runtime resume if the device is already in the
+ * suspended state during gadget disconnect. DWC3 gadget was already
+ * halted/stopped during runtime suspend.
+ */
+ if (!is_on) {
+ pm_runtime_barrier(dwc->dev);
+ if (pm_runtime_suspended(dwc->dev))
+ return 0;
}
/*
@@ -2447,6 +2456,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
dwc->link_state = DWC3_LINK_STATE_SS_DIS;
+ dwc->delayed_status = false;
dwc3_ep0_out_start(dwc);
dwc3_gadget_enable_irq(dwc);
@@ -2585,6 +2595,16 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
return ret;
}
+static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->async_callbacks = enable;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
@@ -2596,6 +2616,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.udc_set_ssp_rate = dwc3_gadget_set_ssp_rate,
.get_config_params = dwc3_gadget_config_params,
.vbus_draw = dwc3_gadget_vbus_draw,
+ .udc_async_callbacks = dwc3_gadget_async_callbacks,
};
/* -------------------------------------------------------------------------- */
@@ -2798,7 +2819,9 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
list_del(&dep->endpoint.ep_list);
}
- debugfs_remove_recursive(debugfs_lookup(dep->name, dwc->root));
+ debugfs_remove_recursive(debugfs_lookup(dep->name,
+ debugfs_lookup(dev_name(dep->dwc->dev),
+ usb_debug_root)));
kfree(dep);
}
}
@@ -3229,7 +3252,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
- if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
+ if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->disconnect(dwc->gadget);
spin_lock(&dwc->lock);
@@ -3238,7 +3261,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
- if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+ if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->suspend(dwc->gadget);
spin_lock(&dwc->lock);
@@ -3247,7 +3270,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc)
static void dwc3_resume_gadget(struct dwc3 *dwc)
{
- if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+ if (dwc->async_callbacks && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
@@ -3259,7 +3282,7 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
if (!dwc->gadget_driver)
return;
- if (dwc->gadget->speed != USB_SPEED_UNKNOWN) {
+ if (dwc->async_callbacks && dwc->gadget->speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
@@ -3583,7 +3606,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
* implemented.
*/
- if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+ if (dwc->async_callbacks && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 39/64] usb: dwc3: import from kernel v5.15
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (37 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 38/64] usb: dwc3: import from kernel v5.14 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 40/64] usb: dwc3: import from kernel v5.16 Jens Wiklander
` (28 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.14 to v5.15.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.14..v5.15
Commits imported:
268bbde716e3 usb: dwc3: gadget: Revert "set gadgets parent to the right controller"
8cfac9a6744f usb: dwc3: core: balance phy init and exit
07281a257a68 Merge tag 'usb-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
1abade64563e usb: dwc3: pci: add support for AMD's newer generation platform.
6a48d0ae01a6 usb: dwc3: imx8mp: request irq after initializing dwc3
843714bb37d9 usb: dwc3: Decouple USB 2.0 L1 & L2 events
2dec48c32a34 Merge 5.14-rc6 into usb-next
175006956740 usb: dwc3: qcom: add IRQ check
baa2986bda3f usb: dwc3: meson-g12a: add IRQ check
73e19de7b79a Merge 5.14-rc5 into usb-next
0132bf6f3958 drivers: usb: dwc3-qcom: Add sdm660 compatible
c6e23b89a95d usb: dwc3: gadget: set gadgets parent to the right controller
26f94fe8e739 usb: dwc3: drd: use helper to get role-switch-default-mode
d842bc6c0579 Merge v5.14-rc3 into usb-next
8e6cb5d27e82 usb: dwc3: dwc3-qcom: Fix typo in the dwc3 vbus override API
cefdd52fa045 usb: dwc3: dwc3-qcom: Enable tx-fifo-resize property by default
9f607a309fbe usb: dwc3: Resize TX FIFOs to meet EP bursting requirements
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 54 ++++++---
drivers/usb/dwc3/core.h | 21 +++-
drivers/usb/dwc3/ep0.c | 2 +
drivers/usb/dwc3/gadget.c | 232 ++++++++++++++++++++++++++++++++++++++
4 files changed, 290 insertions(+), 19 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index ba74ad7f6995..0104a80b185e 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -264,19 +264,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
int retries = 1000;
- int ret;
-
- usb_phy_init(dwc->usb2_phy);
- usb_phy_init(dwc->usb3_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- return ret;
-
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0) {
- phy_exit(dwc->usb2_generic_phy);
- return ret;
- }
/*
* We're resetting only the device side because, if we're in host mode,
@@ -310,9 +297,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
udelay(1);
} while (--retries);
- phy_exit(dwc->usb3_generic_phy);
- phy_exit(dwc->usb2_generic_phy);
-
return -ETIMEDOUT;
done:
@@ -982,9 +966,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->phys_ready = true;
}
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err0a;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0) {
+ phy_exit(dwc->usb2_generic_phy);
+ goto err0a;
+ }
+
ret = dwc3_core_soft_reset(dwc);
if (ret)
- goto err0a;
+ goto err1;
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
@@ -1050,6 +1046,15 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ /*
+ * Decouple USB 2.0 L1 & L2 events which will allow for
+ * gadget driver to only receive U3/L2 suspend & wakeup
+ * events and prevent the more frequent L1 LPM transitions
+ * from interrupting the driver.
+ */
+ if (!DWC3_VER_IS_PRIOR(DWC3, 300A))
+ reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT;
+
if (dwc->dis_tx_ipgap_linecheck_quirk)
reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
@@ -1267,6 +1272,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
+ u8 tx_fifo_resize_max_num;
const char *usb_psy_name;
int ret;
@@ -1282,6 +1288,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
*/
hird_threshold = 12;
+ /*
+ * default to a TXFIFO size large enough to fit 6 max packets. This
+ * allows for systems with larger bus latencies to have some headroom
+ * for endpoints that have a large bMaxBurst value.
+ */
+ tx_fifo_resize_max_num = 6;
+
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
@@ -1325,6 +1338,11 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&tx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,tx-max-burst-prd",
&tx_max_burst_prd);
+ dwc->do_fifo_resize = device_property_read_bool(dev,
+ "tx-fifo-resize");
+ if (dwc->do_fifo_resize)
+ device_property_read_u8(dev, "tx-fifo-max-num",
+ &tx_fifo_resize_max_num);
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
@@ -1390,6 +1408,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_max_burst_prd = tx_max_burst_prd;
dwc->imod_interval = 0;
+
+ dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
}
/* check whether the core supports IMOD */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5991766239ba..5612bfdf37da 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -256,9 +256,10 @@
#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
/* Global User Control 1 Register */
-#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
+#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
-#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
/* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10)
@@ -1023,6 +1024,7 @@ struct dwc3_scratchpad_array {
* @rx_max_burst_prd: max periodic ESS receive burst size
* @tx_thr_num_pkt_prd: periodic ESS transmit packet count
* @tx_max_burst_prd: max periodic ESS transmit burst size
+ * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @delayed_status: true when gadget driver asks for delayed status
@@ -1037,6 +1039,7 @@ struct dwc3_scratchpad_array {
* 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
* @pending_events: true when we have pending IRQs to be handled
+ * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints
* @pullups_connected: true when Run/Stop bit is set
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @three_stage_setup: set if we perform a three phase setup
@@ -1079,6 +1082,11 @@ struct dwc3_scratchpad_array {
* @dis_split_quirk: set to disable split boundary.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
+ * @max_cfg_eps: current max number of IN eps used across all USB configs.
+ * @last_fifo_depth: last fifo depth used to determine next fifo ram start
+ * address.
+ * @num_ep_resized: carries the current number endpoints which have had its tx
+ * fifo resized.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1233,6 +1241,7 @@ struct dwc3 {
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
+ u8 tx_fifo_resize_max_num;
const char *hsphy_interface;
@@ -1246,6 +1255,7 @@ struct dwc3 {
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
unsigned pending_events:1;
+ unsigned do_fifo_resize:1;
unsigned pullups_connected:1;
unsigned setup_packet_pending:1;
unsigned three_stage_setup:1;
@@ -1282,6 +1292,10 @@ struct dwc3 {
unsigned async_callbacks:1;
u16 imod_interval;
+
+ int max_cfg_eps;
+ int last_fifo_depth;
+ int num_ep_resized;
};
#define INCRX_BURST_MODE 0
@@ -1513,6 +1527,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params);
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
u32 param);
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1532,6 +1547,8 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
int cmd, u32 param)
{ return 0; }
+static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{ }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 2f9e45eed228..658739410992 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -621,6 +621,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
case USB_STATE_ADDRESS:
+ dwc3_gadget_clear_tx_fifos(dwc);
+
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index ccb68fe6202e..4519d06c9ca2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -631,6 +631,187 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);
+/**
+ * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
+ * @dwc: pointer to the DWC3 context
+ * @nfifos: number of fifos to calculate for
+ *
+ * Calculates the size value based on the equation below:
+ *
+ * DWC3 revision 280A and prior:
+ * fifo_size = mult * (max_packet / mdwidth) + 1;
+ *
+ * DWC3 revision 290A and onwards:
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * The max packet size is set to 1024, as the txfifo requirements mainly apply
+ * to super speed USB use cases. However, it is safe to overestimate the fifo
+ * allocations for other scenarios, i.e. high speed USB.
+ */
+static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
+{
+ int max_packet = 1024;
+ int fifo_size;
+ int mdwidth;
+
+ mdwidth = dwc3_mdwidth(dwc);
+
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth >>= 3;
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 290A))
+ fifo_size = mult * (max_packet / mdwidth) + 1;
+ else
+ fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
+ return fifo_size;
+}
+
+/**
+ * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation
+ * @dwc: pointer to the DWC3 context
+ *
+ * Iterates through all the endpoint registers and clears the previous txfifo
+ * allocations.
+ */
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int fifo_depth;
+ int size;
+ int num;
+
+ if (!dwc->do_fifo_resize)
+ return;
+
+ /* Read ep0IN related TXFIFO size */
+ dep = dwc->eps[1];
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ if (DWC3_IP_IS(DWC3))
+ fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
+ else
+ fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size);
+
+ dwc->last_fifo_depth = fifo_depth;
+ /* Clear existing TXFIFO for all IN eps except ep0 */
+ for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
+ num += 2) {
+ dep = dwc->eps[num];
+ /* Don't change TXFRAMNUM on usb31 version */
+ size = DWC3_IP_IS(DWC3) ? 0 :
+ dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
+ DWC31_GTXFIFOSIZ_TXFRAMNUM;
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+ }
+ dwc->num_ep_resized = 0;
+}
+
+/*
+ * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
+ * @dwc: pointer to our context structure
+ *
+ * This function will a best effort FIFO allocation in order
+ * to improve FIFO usage and throughput, while still allowing
+ * us to enable as many endpoints as possible.
+ *
+ * Keep in mind that this operation will be highly dependent
+ * on the configured size for RAM1 - which contains TxFifo -,
+ * the amount of endpoints enabled on coreConsultant tool, and
+ * the width of the Master Bus.
+ *
+ * In general, FIFO depths are represented with the following equation:
+ *
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * In conjunction with dwc3_gadget_check_config(), this resizing logic will
+ * ensure that all endpoints will have enough internal memory for one max
+ * packet per endpoint.
+ */
+static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int fifo_0_start;
+ int ram1_depth;
+ int fifo_size;
+ int min_depth;
+ int num_in_ep;
+ int remaining;
+ int num_fifos = 1;
+ int fifo;
+ int tmp;
+
+ if (!dwc->do_fifo_resize)
+ return 0;
+
+ /* resize IN endpoints except ep0 */
+ if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
+ return 0;
+
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+
+ if ((dep->endpoint.maxburst > 1 &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ num_fifos = 3;
+
+ if (dep->endpoint.maxburst > 6 &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
+ num_fifos = dwc->tx_fifo_resize_max_num;
+
+ /* FIFO size for a single buffer */
+ fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
+
+ /* Calculate the number of remaining EPs w/o any FIFO */
+ num_in_ep = dwc->max_cfg_eps;
+ num_in_ep -= dwc->num_ep_resized;
+
+ /* Reserve at least one FIFO for the number of IN EPs */
+ min_depth = num_in_ep * (fifo + 1);
+ remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
+ remaining = max_t(int, 0, remaining);
+ /*
+ * We've already reserved 1 FIFO per EP, so check what we can fit in
+ * addition to it. If there is not enough remaining space, allocate
+ * all the remaining space to the EP.
+ */
+ fifo_size = (num_fifos - 1) * fifo;
+ if (remaining < fifo_size)
+ fifo_size = remaining;
+
+ fifo_size += fifo;
+ /* Last increment according to the TX FIFO size equation */
+ fifo_size++;
+
+ /* Check if TXFIFOs start at non-zero addr */
+ tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
+
+ fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
+ if (DWC3_IP_IS(DWC3))
+ dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+ else
+ dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+ /* Check fifo size allocation doesn't exceed available RAM size. */
+ if (dwc->last_fifo_depth >= ram1_depth) {
+ dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
+ dwc->last_fifo_depth, ram1_depth,
+ dep->endpoint.name, fifo_size);
+ if (DWC3_IP_IS(DWC3))
+ fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+ else
+ fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+ dwc->last_fifo_depth -= fifo_size;
+ return -ENOMEM;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+ dwc->num_ep_resized++;
+
+ return 0;
+}
+
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
@@ -648,6 +829,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
int ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
+ ret = dwc3_gadget_resize_tx_fifos(dep);
+ if (ret)
+ return ret;
+
ret = dwc3_gadget_start_config(dep);
if (ret)
return ret;
@@ -2508,6 +2693,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = NULL;
+ dwc->max_cfg_eps = 0;
spin_unlock_irqrestore(&dwc->lock, flags);
free_irq(dwc->irq_gadget, dwc->ev_buf);
@@ -2595,6 +2781,51 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
return ret;
}
+/**
+ * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration
+ * @g: pointer to the USB gadget
+ *
+ * Used to record the maximum number of endpoints being used in a USB composite
+ * device. (across all configurations) This is to be used in the calculation
+ * of the TXFIFO sizes when resizing internal memory for individual endpoints.
+ * It will help ensured that the resizing logic reserves enough space for at
+ * least one max packet.
+ */
+static int dwc3_gadget_check_config(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct usb_ep *ep;
+ int fifo_size = 0;
+ int ram1_depth;
+ int ep_num = 0;
+
+ if (!dwc->do_fifo_resize)
+ return 0;
+
+ list_for_each_entry(ep, &g->ep_list, ep_list) {
+ /* Only interested in the IN endpoints */
+ if (ep->claimed && (ep->address & USB_DIR_IN))
+ ep_num++;
+ }
+
+ if (ep_num <= dwc->max_cfg_eps)
+ return 0;
+
+ /* Update the max number of eps in the composition */
+ dwc->max_cfg_eps = ep_num;
+
+ fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps);
+ /* Based on the equation, increment by one for every ep */
+ fifo_size += dwc->max_cfg_eps;
+
+ /* Check if we can fit a single fifo per endpoint */
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+ if (fifo_size > ram1_depth)
+ return -ENOMEM;
+
+ return 0;
+}
+
static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
{
struct dwc3 *dwc = gadget_to_dwc(g);
@@ -2616,6 +2847,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.udc_set_ssp_rate = dwc3_gadget_set_ssp_rate,
.get_config_params = dwc3_gadget_config_params,
.vbus_draw = dwc3_gadget_vbus_draw,
+ .check_config = dwc3_gadget_check_config,
.udc_async_callbacks = dwc3_gadget_async_callbacks,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 40/64] usb: dwc3: import from kernel v5.16
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (38 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 39/64] usb: dwc3: import from kernel v5.15 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 41/64] usb: dwc3: import from kernel v5.17 Jens Wiklander
` (27 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.15 to v5.16.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.15..v5.16
Commits imported:
6a97cee39d8f Revert "usb: dwc3: dwc3-qcom: Enable tx-fifo-resize property by default"
47ce45906ca9 usb: dwc3: leave default DMA for PCI devices
26288448120b usb: dwc3: gadget: Fix null pointer exception
63c4c320ccf7 usb: dwc3: gadget: Check for L1/L2/U3 for Start Transfer
d74dc3e9f58c usb: dwc3: gadget: Ignore NoStream after End Transfer
250fdabec6ff usb: dwc3: core: Revise GHWPARAMS9 offset
d1a4683747fe usb: dwc3: Align DWC3_EP_* flag macros
876a75cb520f usb: dwc3: gadget: Skip resizing EP's TX FIFO if already resized
b851f7c7b8fd usb: dwc3: gadget: Change to dev_dbg() when queuing to inactive gadget/ep
620b74d01b9d Merge 5.15-rc5 into usb-next
2abc865706c9 usb: exynos: describe driver in KConfig
ae9a6149884e Merge 5.15-rc3 into usb-next
8217f07a5023 usb: dwc3: gadget: Avoid starting DWC3 gadget during UDC unbind
7bee31883889 usb: dwc3: reference clock period configuration
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 37 ++++++++++++++++++++++++---
drivers/usb/dwc3/core.h | 27 +++++++++++++-------
drivers/usb/dwc3/gadget.c | 53 ++++++++++++++++++++++++++++-----------
3 files changed, 90 insertions(+), 27 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 0104a80b185e..f4c09951b517 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -26,6 +26,7 @@
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
+#include <linux/bitfield.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -335,6 +336,29 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
}
}
+/**
+ * dwc3_ref_clk_period - Reference clock period configuration
+ * Default reference clock period depends on hardware
+ * configuration. For systems with reference clock that differs
+ * from the default, this will set clock period in DWC3_GUCTL
+ * register.
+ * @dwc: Pointer to our controller context structure
+ * @ref_clk_per: reference clock period in ns
+ */
+static void dwc3_ref_clk_period(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ if (dwc->ref_clk_per == 0)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+ reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
+ reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per);
+ dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+}
+
+
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
@@ -1007,6 +1031,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc);
+ /* Adjust Reference Clock Period */
+ dwc3_ref_clk_period(dwc);
+
dwc3_set_incr_burst_type(dwc);
usb_phy_set_suspend(dwc->usb2_phy, 0);
@@ -1389,6 +1416,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&dwc->hsphy_interface);
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
&dwc->fladj);
+ device_property_read_u32(dev, "snps,ref-clock-period-ns",
+ &dwc->ref_clk_per);
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");
@@ -1565,9 +1594,11 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
- ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
- if (ret)
- return ret;
+ if (!dwc->sysdev_is_parent) {
+ ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
+ if (ret)
+ return ret;
+ }
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset))
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5612bfdf37da..5c491d0a19d7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -143,7 +143,7 @@
#define DWC3_GHWPARAMS8 0xc600
#define DWC3_GUCTL3 0xc60c
#define DWC3_GFLADJ 0xc630
-#define DWC3_GHWPARAMS9 0xc680
+#define DWC3_GHWPARAMS9 0xc6e0
/* Device Registers */
#define DWC3_DCFG 0xc700
@@ -387,6 +387,10 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+/* Global User Control Register*/
+#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
+#define DWC3_GUCTL_REFCLKPER_SEL 22
+
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
@@ -711,21 +715,22 @@ struct dwc3_ep {
u32 saved_state;
unsigned int flags;
-#define DWC3_EP_ENABLED BIT(0)
-#define DWC3_EP_STALL BIT(1)
-#define DWC3_EP_WEDGE BIT(2)
-#define DWC3_EP_TRANSFER_STARTED BIT(3)
-#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
-#define DWC3_EP_PENDING_REQUEST BIT(5)
-#define DWC3_EP_DELAY_START BIT(6)
+#define DWC3_EP_ENABLED BIT(0)
+#define DWC3_EP_STALL BIT(1)
+#define DWC3_EP_WEDGE BIT(2)
+#define DWC3_EP_TRANSFER_STARTED BIT(3)
+#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
+#define DWC3_EP_PENDING_REQUEST BIT(5)
+#define DWC3_EP_DELAY_START BIT(6)
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
+#define DWC3_EP_TXFIFO_RESIZED BIT(12)
/* This last one is specific to EP0 */
-#define DWC3_EP0_DIR_IN BIT(31)
+#define DWC3_EP0_DIR_IN BIT(31)
/*
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
@@ -970,6 +975,7 @@ struct dwc3_scratchpad_array {
* @regs: base address for our registers
* @regs_size: address space size
* @fladj: frame length adjustment
+ * @ref_clk_per: reference clock period configuration
* @irq_gadget: peripheral controller's IRQ number
* @otg_irq: IRQ number for OTG IRQs
* @current_otg_role: current role of operation while using the OTG block
@@ -1027,6 +1033,7 @@ struct dwc3_scratchpad_array {
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
+ * @softconnect: true when gadget connect is called, false when disconnect runs
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
@@ -1149,6 +1156,7 @@ struct dwc3 {
struct power_supply *usb_psy;
u32 fladj;
+ u32 ref_clk_per;
u32 irq_gadget;
u32 otg_irq;
u32 current_otg_role;
@@ -1246,6 +1254,7 @@ struct dwc3 {
const char *hsphy_interface;
unsigned connected:1;
+ unsigned softconnect:1;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 4519d06c9ca2..7e3db00e9759 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -310,13 +310,24 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
int link_state;
+ /*
+ * Initiate remote wakeup if the link state is in U3 when
+ * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
+ * link state is in U1/U2, no remote wakeup is needed. The Start
+ * Transfer command will initiate the link recovery.
+ */
link_state = dwc3_gadget_get_link_state(dwc);
- if (link_state == DWC3_LINK_STATE_U1 ||
- link_state == DWC3_LINK_STATE_U2 ||
- link_state == DWC3_LINK_STATE_U3) {
+ switch (link_state) {
+ case DWC3_LINK_STATE_U2:
+ if (dwc->gadget->speed >= USB_SPEED_SUPER)
+ break;
+
+ fallthrough;
+ case DWC3_LINK_STATE_U3:
ret = __dwc3_gadget_wakeup(dwc);
dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
ret);
+ break;
}
}
@@ -702,6 +713,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
DWC31_GTXFIFOSIZ_TXFRAMNUM;
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+ dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
}
dwc->num_ep_resized = 0;
}
@@ -747,6 +759,10 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
return 0;
+ /* bail if already resized */
+ if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
+ return 0;
+
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
if ((dep->endpoint.maxburst > 1 &&
@@ -807,6 +823,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
}
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+ dep->flags |= DWC3_EP_TXFIFO_RESIZED;
dwc->num_ep_resized++;
return 0;
@@ -995,7 +1012,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false;
dep->type = 0;
- dep->flags = 0;
+ dep->flags &= DWC3_EP_TXFIFO_RESIZED;
return 0;
}
@@ -1813,7 +1830,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
struct dwc3 *dwc = dep->dwc;
if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
- dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
return -ESHUTDOWN;
}
@@ -2418,7 +2435,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
int ret;
is_on = !!is_on;
-
+ dwc->softconnect = is_on;
/*
* Per databook, when we want to stop the gadget, if a control transfer
* is still in process, complete it and get the core into setup phase.
@@ -3246,6 +3263,9 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
bool no_started_trb = true;
+ if (!dep->endpoint.desc)
+ return no_started_trb;
+
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
@@ -3293,6 +3313,9 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
{
int status = 0;
+ if (!dep->endpoint.desc)
+ return;
+
if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
dwc3_gadget_endpoint_frame_from_event(dep, event);
@@ -3346,6 +3369,14 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
if (cmd != DWC3_DEPCMD_ENDTRANSFER)
return;
+ /*
+ * The END_TRANSFER command will cause the controller to generate a
+ * NoStream Event, and it's not due to the host DP NoStream rejection.
+ * Ignore the next NoStream event.
+ */
+ if (dep->stream_capable)
+ dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
@@ -3568,14 +3599,6 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
WARN_ON_ONCE(ret);
dep->resource_index = 0;
- /*
- * The END_TRANSFER command will cause the controller to generate a
- * NoStream Event, and it's not due to the host DP NoStream rejection.
- * Ignore the next NoStream event.
- */
- if (dep->stream_capable)
- dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
-
if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else
@@ -4352,7 +4375,7 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
{
int ret;
- if (!dwc->gadget_driver)
+ if (!dwc->gadget_driver || !dwc->softconnect)
return 0;
ret = __dwc3_gadget_start(dwc);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 41/64] usb: dwc3: import from kernel v5.17
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (39 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 40/64] usb: dwc3: import from kernel v5.16 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 42/64] usb: dwc3: import from kernel v5.18 Jens Wiklander
` (26 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.16 to v5.17.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.16..v5.17
Commits imported:
84918a89d6ef usb: dwc3: gadget: Let the interrupt handler disable bottom halves.
62e3f0afe246 usb: dwc3: pci: Fix Bay Trail phy GPIO mappings
d7c93a903f33 usb: dwc3: pci: Add "snps,dis_u2_susphy_quirk" for Intel Bay Trail
038438a25c45 usb: dwc3: pci: add support for the Intel Raptor Lake-S
117b4e96c7f3 usb: dwc3: gadget: Prevent core from processing stale TRBs
b470947c3672 usb: dwc3: xilinx: fix uninitialized return value
2cc9b1c93b1c usb: dwc3: xilinx: Fix error handling when getting USB3 PHY
9678f3361afc usb: dwc3: xilinx: Skip resets and USB3 register settings for USB2.0 mode
b52fe2dbb3e6 usb: dwc3: qcom: Fix NULL vs IS_ERR checking in dwc3_qcom_probe
fa0ef93868a6 usb: dwc3: dwc3-qcom: Add missing platform_device_put() in dwc3_qcom_acpi_register_core
74b39dfabd76 usb: dwc3: Drop unneeded calls to platform_get_resource_byname()
666f3de741f7 usb: dwc3: gadget: Support Multi-Stream Transfer
c45479ecd0c2 Merge 5.16-rc5 into usb-next
76c4c95dac0d usb: dwc3: gadget: Skip reading GEVNTSIZn
a02a26eb0aea usb: dwc3: gadget: Ignore Update Transfer cmd params
bc27117c7cdd usb: dwc3: gadget: Skip checking Update Transfer status
4d012040161c Merge 5.16-rc3 into usb-next
4ce3b45704d5 usb: dwc3: meson-g12a: fix shared reset control use
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.h | 9 +++++
drivers/usb/dwc3/gadget.c | 74 ++++++++++++++++++++++++++++++---------
2 files changed, 67 insertions(+), 16 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5c491d0a19d7..e1cc3f7398fb 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -153,6 +153,7 @@
#define DWC3_DGCMDPAR 0xc710
#define DWC3_DGCMD 0xc714
#define DWC3_DALEPENA 0xc720
+#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
#define DWC3_DEPCMDPAR2 0x00
@@ -382,6 +383,7 @@
/* Global HWPARAMS9 Register */
#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS BIT(0)
+#define DWC3_GHWPARAMS9_DEV_MST BIT(1)
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
@@ -558,6 +560,9 @@
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
#define DWC3_DALEPENA_EP(n) BIT(n)
+/* DWC_usb32 DCFG1 config */
+#define DWC3_DCFG1_DIS_MST_ENH BIT(1)
+
#define DWC3_DEPCMD_TYPE_CONTROL 0
#define DWC3_DEPCMD_TYPE_ISOC 1
#define DWC3_DEPCMD_TYPE_BULK 2
@@ -888,6 +893,10 @@ struct dwc3_hwparams {
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
+/* HWPARAMS9 */
+#define DWC3_MST_CAPABLE(p) (!!((p)->hwparams9 & \
+ DWC3_GHWPARAMS9_DEV_MST))
+
/**
* struct dwc3_request - representation of a transfer request
* @request: struct usb_request to be transferred
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 7e3db00e9759..a0c883f19a41 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -331,9 +331,17 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
}
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+ /*
+ * For some commands such as Update Transfer command, DEPCMDPARn
+ * registers are reserved. Since the driver often sends Update Transfer
+ * command, don't write to DEPCMDPARn to avoid register write delays and
+ * improve performance.
+ */
+ if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+ }
/*
* Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're
@@ -357,6 +365,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
cmd |= DWC3_DEPCMD_CMDACT;
dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
+
+ if (!(cmd & DWC3_DEPCMD_CMDACT)) {
+ ret = 0;
+ goto skip_status;
+ }
+
do {
reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
if (!(reg & DWC3_DEPCMD_CMDACT)) {
@@ -398,6 +412,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
cmd_status = -ETIMEDOUT;
}
+skip_status:
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
@@ -1260,17 +1275,35 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
+ /* All TRBs setup for MST must set CSP=1 when LST=0 */
+ if (dep->stream_capable && DWC3_MST_CAPABLE(&dwc->hwparams))
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
+
if ((!no_interrupt && !chain) || must_interrupt)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
- else if (dep->stream_capable && is_last)
+ else if (dep->stream_capable && is_last &&
+ !DWC3_MST_CAPABLE(&dwc->hwparams))
trb->ctrl |= DWC3_TRB_CTRL_LST;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
+ /*
+ * As per data book 4.2.3.2TRB Control Bit Rules section
+ *
+ * The controller autonomously checks the HWO field of a TRB to determine if the
+ * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
+ * is valid before setting the HWO field to '1'. In most systems, this means that
+ * software must update the fourth DWORD of a TRB last.
+ *
+ * However there is a possibility of CPU re-ordering here which can cause
+ * controller to observe the HWO bit set prematurely.
+ * Add a write memory barrier to prevent CPU re-ordering.
+ */
+ wmb();
trb->ctrl |= DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_enq(dep);
@@ -1513,7 +1546,8 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep)
* burst capability may try to read and use TRBs beyond the
* active transfer instead of stopping.
*/
- if (dep->stream_capable && req->request.is_last)
+ if (dep->stream_capable && req->request.is_last &&
+ !DWC3_MST_CAPABLE(&dep->dwc->hwparams))
return ret;
}
@@ -1546,7 +1580,8 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep)
* burst capability may try to read and use TRBs beyond the
* active transfer instead of stopping.
*/
- if (dep->stream_capable && req->request.is_last)
+ if (dep->stream_capable && req->request.is_last &&
+ !DWC3_MST_CAPABLE(&dwc->hwparams))
return ret;
}
@@ -1623,7 +1658,8 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
return ret;
}
- if (dep->stream_capable && req->request.is_last)
+ if (dep->stream_capable && req->request.is_last &&
+ !DWC3_MST_CAPABLE(&dep->dwc->hwparams))
dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
return 0;
@@ -2638,6 +2674,13 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
reg |= DWC3_DCFG_IGNSTRMPP;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ /* Enable MST by default if the device is capable of MST */
+ if (DWC3_MST_CAPABLE(&dwc->hwparams)) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG1);
+ reg &= ~DWC3_DCFG1_DIS_MST_ENH;
+ dwc3_writel(dwc->regs, DWC3_DCFG1, reg);
+ }
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -3437,7 +3480,8 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
case DEPEVT_STREAM_NOSTREAM:
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
!(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
- !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))
+ (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
+ !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
break;
/*
@@ -4067,7 +4111,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
struct dwc3 *dwc = evt->dwc;
irqreturn_t ret = IRQ_NONE;
int left;
- u32 reg;
left = evt->count;
@@ -4099,9 +4142,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
ret = IRQ_HANDLED;
/* Unmask interrupt */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
- reg &= ~DWC3_GEVNTSIZ_INTMASK;
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
@@ -4118,9 +4160,11 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
+ local_bh_disable();
spin_lock_irqsave(&dwc->lock, flags);
ret = dwc3_process_event_buf(evt);
spin_unlock_irqrestore(&dwc->lock, flags);
+ local_bh_enable();
return ret;
}
@@ -4130,7 +4174,6 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
struct dwc3 *dwc = evt->dwc;
u32 amount;
u32 count;
- u32 reg;
if (pm_runtime_suspended(dwc->dev)) {
pm_runtime_get(dwc->dev);
@@ -4157,9 +4200,8 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
evt->flags |= DWC3_EVENT_PENDING;
/* Mask interrupt */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
- reg |= DWC3_GEVNTSIZ_INTMASK;
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
amount = min(count, evt->length - evt->lpos);
memcpy(evt->cache + evt->lpos, evt->buf + evt->lpos, amount);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 42/64] usb: dwc3: import from kernel v5.18
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (40 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 41/64] usb: dwc3: import from kernel v5.17 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 43/64] usb: dwc3: import from kernel v5.19 Jens Wiklander
` (25 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.17 to v5.18.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.17..v5.18
Commits imported:
c7428dbddcf4 usb: dwc3: gadget: Return proper request status
973e0f7a847e usb: dwc3: pci: add support for the Intel Meteor Lake-P
f4fd84ae0765 usb: dwc3: core: Only handle soft-reset in DCTL
ab7aa2866d29 usb: dwc3: Try usb-role-switch first in dwc3_drd_init
f28ad9069363 usb: dwc3: core: Fix tx/rx threshold settings
4e64cd7763ca usb: dwc3: fix backwards compat with rockchip devices
0066472de157 usb: dwc3: Issue core soft reset before enabling run/stop
e4cf6580ac74 usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue
e192cc7b5239 usb: dwc3: gadget: move cmd_endtransfer to extra function
26d27a1080a7 usb: dwc3: gadget: ep_queue simplify isoc start condition
ecb0a2f1e9e2 usb: dwc3: pci: Add support for Intel Alder Lake
ac01df343e5a usb: dwc3: omap: fix "unbalanced disables for smps10_out1" on omap5evm
aa6812be1feb usb: dwc3: gadget: Give some time to schedule isoc
62b20e6e0dde usb: dwc3: core: do not use 3.0 clock when operating in 2.0 mode
9d52107185b6 usb: dwc3: imx8mp: Add support for setting SOC specific flags
81915384b5d1 usb: dwc3: imx8mp: rename iomem base pointer
850ebb27aed8 usb: dwc3-meson-g12a: constify drvdata structs
ca9400ef7f67 Merge 5.17-rc6 into usb-next
a5d847b0afd3 usb: dwc3: pci: Also apply Bay Trail GPIO mappings to ulpi-device
582ab24e096f usb: dwc3: pci: Set "linux,phy_charger_detect" property on some Bay Trail boards
e285cb403994 usb: dwc3: pci: Set the swnode from inside dwc3_pci_quirks()
e51879d85a4d usb: dwc3: drd: Don't check against CONFIG_OF
fbd533e90d23 Merge 5.17-rc4 into usb-next
596c87856e08 usb: dwc3: Program GFLADJ
5114c3ee2487 usb: dwc3: Calculate REFCLKPER based on reference clock
33fb697ec7e5 usb: dwc3: Get clocks individually
60c250a98d4c Merge tag 'v5.17-rc2' into usb-next
a102f07e4edf usb: dwc3: drd: Add support for usb-conn-gpio based usb-role-switch
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 158 ++++++++++++++++++++++++++++++++------
drivers/usb/dwc3/core.h | 17 +++-
drivers/usb/dwc3/ep0.c | 14 ++++
drivers/usb/dwc3/gadget.c | 149 ++++++++++++++++++++++++-----------
drivers/usb/dwc3/gadget.h | 1 +
5 files changed, 267 insertions(+), 72 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f4c09951b517..d28cd1a6709b 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -115,8 +115,6 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
dwc->current_dr_role = mode;
}
-static int dwc3_core_soft_reset(struct dwc3 *dwc);
-
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
@@ -261,7 +259,7 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
-static int dwc3_core_soft_reset(struct dwc3 *dwc)
+int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
int retries = 1000;
@@ -276,7 +274,8 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ reg &= ~DWC3_DCTL_RUN_STOP;
+ dwc3_gadget_dctl_write_safe(dwc, reg);
/*
* For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
@@ -347,17 +346,64 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
*/
static void dwc3_ref_clk_period(struct dwc3 *dwc)
{
+ unsigned long period;
+ unsigned long fladj;
+ unsigned long decr;
+ unsigned long rate;
u32 reg;
- if (dwc->ref_clk_per == 0)
+ if (dwc->ref_clk) {
+ rate = clk_get_rate(dwc->ref_clk);
+ if (!rate)
+ return;
+ period = NSEC_PER_SEC / rate;
+ } else if (dwc->ref_clk_per) {
+ period = dwc->ref_clk_per;
+ rate = NSEC_PER_SEC / period;
+ } else {
return;
+ }
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
- reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per);
+ reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
-}
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
+ return;
+
+ /*
+ * The calculation below is
+ *
+ * 125000 * (NSEC_PER_SEC / (rate * period) - 1)
+ *
+ * but rearranged for fixed-point arithmetic. The division must be
+ * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and
+ * neither does rate * period).
+ *
+ * Note that rate * period ~= NSEC_PER_SECOND, minus the number of
+ * nanoseconds of error caused by the truncation which happened during
+ * the division when calculating rate or period (whichever one was
+ * derived from the other). We first calculate the relative error, then
+ * scale it to units of 8 ppm.
+ */
+ fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period);
+ fladj -= 125000;
+
+ /*
+ * The documented 240MHz constant is scaled by 2 to get PLS1 as well.
+ */
+ decr = 480000000 / rate;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
+ & ~DWC3_GFLADJ_240MHZDECR
+ & ~DWC3_GFLADJ_240MHZDECR_PLS1;
+ reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
+ | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
+ | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+}
/**
* dwc3_free_one_event_buffer - Frees one event buffer
@@ -745,6 +791,38 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
return 0;
}
+static int dwc3_clk_enable(struct dwc3 *dwc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(dwc->bus_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dwc->ref_clk);
+ if (ret)
+ goto disable_bus_clk;
+
+ ret = clk_prepare_enable(dwc->susp_clk);
+ if (ret)
+ goto disable_ref_clk;
+
+ return 0;
+
+disable_ref_clk:
+ clk_disable_unprepare(dwc->ref_clk);
+disable_bus_clk:
+ clk_disable_unprepare(dwc->bus_clk);
+ return ret;
+}
+
+static void dwc3_clk_disable(struct dwc3 *dwc)
+{
+ clk_disable_unprepare(dwc->susp_clk);
+ clk_disable_unprepare(dwc->ref_clk);
+ clk_disable_unprepare(dwc->bus_clk);
+}
+
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
@@ -758,7 +836,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
+ dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset);
}
@@ -1088,6 +1166,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->parkmode_disable_ss_quirk)
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+ if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
+ (dwc->maximum_speed == USB_SPEED_HIGH ||
+ dwc->maximum_speed == USB_SPEED_FULL))
+ reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@@ -1295,10 +1378,10 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
- u8 rx_thr_num_pkt_prd;
- u8 rx_max_burst_prd;
- u8 tx_thr_num_pkt_prd;
- u8 tx_max_burst_prd;
+ u8 rx_thr_num_pkt_prd = 0;
+ u8 rx_max_burst_prd = 0;
+ u8 tx_thr_num_pkt_prd = 0;
+ u8 tx_max_burst_prd = 0;
u8 tx_fifo_resize_max_num;
const char *usb_psy_name;
int ret;
@@ -1605,25 +1688,54 @@ static int dwc3_probe(struct platform_device *pdev)
return PTR_ERR(dwc->reset);
if (dev->of_node) {
- ret = devm_clk_bulk_get_all(dev, &dwc->clks);
- if (ret == -EPROBE_DEFER)
- return ret;
/*
* Clocks are optional, but new DT platforms should support all
* clocks as required by the DT-binding.
+ * Some devices have different clock names in legacy device trees,
+ * check for them to retain backwards compatibility.
*/
- if (ret < 0)
- dwc->num_clks = 0;
- else
- dwc->num_clks = ret;
+ dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
+ if (IS_ERR(dwc->bus_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+
+ if (dwc->bus_clk == NULL) {
+ dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
+ if (IS_ERR(dwc->bus_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+ }
+ dwc->ref_clk = devm_clk_get_optional(dev, "ref");
+ if (IS_ERR(dwc->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+
+ if (dwc->ref_clk == NULL) {
+ dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
+ if (IS_ERR(dwc->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+ }
+
+ dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
+ if (IS_ERR(dwc->susp_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
+
+ if (dwc->susp_clk == NULL) {
+ dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
+ if (IS_ERR(dwc->susp_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
+ }
}
ret = reset_control_deassert(dwc->reset);
if (ret)
return ret;
- ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
+ ret = dwc3_clk_enable(dwc);
if (ret)
goto assert_reset;
@@ -1711,7 +1823,7 @@ err1:
pm_runtime_disable(&pdev->dev);
disable_clks:
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
+ dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);
@@ -1755,7 +1867,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
if (ret)
return ret;
- ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
+ ret = dwc3_clk_enable(dwc);
if (ret)
goto assert_reset;
@@ -1766,7 +1878,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
return 0;
disable_clks:
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
+ dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e1cc3f7398fb..5c9d467195a6 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -259,6 +259,7 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
+#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
@@ -388,6 +389,9 @@
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
+#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
+#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
/* Global User Control Register*/
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
@@ -733,6 +737,7 @@ struct dwc3_ep {
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
+#define DWC3_EP_DELAY_STOP BIT(13)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -978,8 +983,9 @@ struct dwc3_scratchpad_array {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
- * @clks: array of clocks
- * @num_clks: number of clocks
+ * @bus_clk: clock for accessing the registers
+ * @ref_clk: reference clock
+ * @susp_clk: clock used when the SS phy is in low power (S3) state
* @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
@@ -1134,8 +1140,9 @@ struct dwc3 {
struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver;
- struct clk_bulk_data *clks;
- int num_clks;
+ struct clk *bus_clk;
+ struct clk *ref_clk;
+ struct clk *susp_clk;
struct reset_control *reset;
@@ -1525,6 +1532,8 @@ bool dwc3_has_imod(struct dwc3 *dwc);
int dwc3_event_buffers_setup(struct dwc3 *dwc);
void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+int dwc3_core_soft_reset(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 658739410992..1064be5518f6 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -271,6 +271,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
int ret;
+ int i;
complete(&dwc->ep0_in_setup);
@@ -279,6 +280,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
DWC3_TRBCTL_CONTROL_SETUP, false);
ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0);
+ for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
+ struct dwc3_ep *dwc3_ep;
+
+ dwc3_ep = dwc->eps[i];
+ if (!dwc3_ep)
+ continue;
+
+ if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
+ continue;
+
+ dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
+ dwc3_stop_active_transfer(dwc3_ep, true, true);
+ }
}
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a0c883f19a41..0b9c2493844a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -654,9 +654,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
- bool interrupt);
-
/**
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
* @dwc: pointer to the DWC3 context
@@ -1673,6 +1670,40 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
return DWC3_DSTS_SOFFN(reg);
}
+/**
+ * __dwc3_stop_active_transfer - stop the current active transfer
+ * @dep: isoc endpoint
+ * @force: set forcerm bit in the command
+ * @interrupt: command complete interrupt after End Transfer command
+ *
+ * When setting force, the ForceRM bit will be set. In that case
+ * the controller won't update the TRB progress on command
+ * completion. It also won't clear the HWO bit in the TRB.
+ * The command will also not complete immediately in that case.
+ */
+static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
+ cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(¶ms, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ WARN_ON_ONCE(ret);
+ dep->resource_index = 0;
+
+ if (!interrupt)
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ else if (!ret)
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+
+ return ret;
+}
+
/**
* dwc3_gadget_start_isoc_quirk - workaround invalid frame number
* @dep: isoc endpoint
@@ -1830,7 +1861,13 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
}
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
- dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
+ int future_interval = i + 1;
+
+ /* Give the controller at least 500us to schedule transfers */
+ if (desc->bInterval < 3)
+ future_interval += 3 - desc->bInterval;
+
+ dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval);
ret = __dwc3_gadget_kick_transfer(dep);
if (ret != -EAGAIN)
@@ -1842,21 +1879,8 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
* status, issue END_TRANSFER command and retry on the next XferNotReady
* event.
*/
- if (ret == -EAGAIN) {
- struct dwc3_gadget_ep_cmd_params params;
- u32 cmd;
-
- cmd = DWC3_DEPCMD_ENDTRANSFER |
- DWC3_DEPCMD_CMDIOC |
- DWC3_DEPCMD_PARAM(dep->resource_index);
-
- dep->resource_index = 0;
- memset(¶ms, 0, sizeof(params));
-
- ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
- if (!ret)
- dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
- }
+ if (ret == -EAGAIN)
+ ret = __dwc3_stop_active_transfer(dep, false, true);
return ret;
}
@@ -1899,6 +1923,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
*/
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
(dep->flags & DWC3_EP_WEDGE) ||
+ (dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_STALL)) {
dep->flags |= DWC3_EP_DELAY_START;
return 0;
@@ -1913,13 +1938,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* errors which will force us issue EndTransfer command.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
- !(dep->flags & DWC3_EP_TRANSFER_STARTED))
- return 0;
-
- if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST))
return __dwc3_gadget_start_isoc(dep);
+
+ return 0;
}
}
@@ -2033,6 +2056,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
if (r == req) {
struct dwc3_request *t;
+ /*
+ * If a Setup packet is received but yet to DMA out, the controller will
+ * not process the End Transfer command of any endpoint. Polling of its
+ * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+ * timeout. Delay issuing the End Transfer command until the Setup TRB is
+ * prepared.
+ */
+ if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
+ dep->flags |= DWC3_EP_DELAY_STOP;
+
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
@@ -2116,7 +2149,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
- if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
+ (dep->flags & DWC3_EP_DELAY_STOP)) {
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
return 0;
}
@@ -2544,6 +2578,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
dwc->ev_buf->length;
}
} else {
+ /*
+ * In the Synopsys DWC_usb31 1.90a programming guide section
+ * 4.1.9, it specifies that for a reconnect after a
+ * device-initiated disconnect requires a core soft reset
+ * (DCTL.CSftRst) before enabling the run/stop bit.
+ */
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_core_soft_reset(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc);
}
@@ -3229,6 +3274,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event,
struct dwc3_request *req, int status)
{
+ int request_status;
int ret;
if (req->request.num_mapped_sgs)
@@ -3249,7 +3295,35 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->needs_extra_trb = false;
}
- dwc3_gadget_giveback(dep, req, status);
+ /*
+ * The event status only reflects the status of the TRB with IOC set.
+ * For the requests that don't set interrupt on completion, the driver
+ * needs to check and return the status of the completed TRBs associated
+ * with the request. Use the status of the last TRB of the request.
+ */
+ if (req->request.no_interrupt) {
+ struct dwc3_trb *trb;
+
+ trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue);
+ switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) {
+ case DWC3_TRBSTS_MISSED_ISOC:
+ /* Isoc endpoint only */
+ request_status = -EXDEV;
+ break;
+ case DWC3_TRB_STS_XFER_IN_PROG:
+ /* Applicable when End Transfer with ForceRM=0 */
+ case DWC3_TRBSTS_SETUP_PENDING:
+ /* Control endpoint only */
+ case DWC3_TRBSTS_OK:
+ default:
+ request_status = 0;
+ break;
+ }
+ } else {
+ request_status = status;
+ }
+
+ dwc3_gadget_giveback(dep, req, request_status);
out:
return ret;
@@ -3596,14 +3670,11 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
- struct dwc3_gadget_ep_cmd_params params;
- u32 cmd;
- int ret;
-
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
+ (dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
@@ -3634,19 +3705,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
* This mode is NOT available on the DWC_usb31 IP.
*/
- cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
- cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
- cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
- memset(¶ms, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
- WARN_ON_ONCE(ret);
- dep->resource_index = 0;
-
- if (!interrupt)
- dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- else
- dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ __dwc3_stop_active_transfer(dep, force, interrupt);
}
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 77df4b6d6c13..f763380e672e 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
+void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 43/64] usb: dwc3: import from kernel v5.19
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (41 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 42/64] usb: dwc3: import from kernel v5.18 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 44/64] usb: dwc3: import from kernel v6.0 Jens Wiklander
` (24 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.18 to v5.19.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.18..v5.19
Commits imported:
5812175389e2 usb: dwc3-am62: remove unnecesary clk_put()
7441b273388b usb: dwc3: gadget: Fix event pending check
8bd6b8c4b100 USB: fixup for merge issue with "usb: dwc3: Don't switch OTG -> peripheral if extcon is present"
7ddda2614d62 usb: dwc3: pci: Restore line lost in merge conflict resolution
9c1e916960c1 usb: dwc3: gadget: Fix IN endpoint max packet size allocation
859bdc359567 usb: dwc3: core: Add error log when core soft reset failed
3c5880745b44 usb: dwc3: gadget: Move null pinter check to proper place
9d778f0c5f95 usb: dwc3: Fix ep0 handling when getting reset while doing control transfer
d090c7a2ab84 USB / dwc3: Fix a checkpatch warning in core.c
f66eef8fb898 usb: dwc3: gadget: Delay issuing End Transfer
ace17b6ee4f9 usb: dwc3: gadget: Only End Transfer for ep0 data phase
c96683798e27 usb: dwc3: ep0: Don't prepare beyond Setup stage
8f8034f493b5 usb: dwc3: gadget: Don't modify GEVNTCOUNT in pullup()
861c010a2ee1 usb: dwc3: gadget: Refactor pullup()
69e131d1ac4e usb: dwc3: gadget: Prevent repeat pullup()
ca05b38252d7 usb: dwc3: xilinx: Add gpio-reset support
4ea150889ad7 usb: dwc3: remove a possible unnecessary 'out of memory' message
7fd069d65da2 usb: dwc3: host: Stop setting the ACPI companion
b9c92fb4aabb Merge 5.18-rc5 into usb-next
a03e2ddab8e7 usb: dwc3: pci: Fix pm_runtime_get_sync() error checking
bf594d1d0c1d usb: dwc3: gadget: Replace list_for_each_entry_safe() if using giveback
2840d6dfcf43 usb: dwc3: EP clear halt leading to clearing of delayed_status
0f0101719138 usb: dwc3: Don't switch OTG -> peripheral if extcon is present
6a7c7df993bd usb: dwc3: gadget: increase tx fifo size for ss isoc endpoints
0fee30ab6351 USB / dwc3: Fix three doc-build warnings
e8784c0aec03 drivers: usb: dwc3: Add AM62 USB wrapper driver
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 76 ++++++--
drivers/usb/dwc3/core.h | 2 +
drivers/usb/dwc3/dwc3-am62.c | 328 +++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/ep0.c | 14 +-
drivers/usb/dwc3/gadget.c | 220 ++++++++++++++---------
drivers/usb/dwc3/gadget.h | 2 +
6 files changed, 538 insertions(+), 104 deletions(-)
create mode 100644 drivers/usb/dwc3/dwc3-am62.c
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index d28cd1a6709b..573421984948 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
@@ -85,7 +86,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
* mode. If the controller supports DRD but the dr_mode is not
* specified or set to OTG, then set the mode to peripheral.
*/
- if (mode == USB_DR_MODE_OTG &&
+ if (mode == USB_DR_MODE_OTG && !dwc->edev &&
(!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
!device_property_read_bool(dwc->dev, "usb-role-switch")) &&
!DWC3_VER_IS_PRIOR(DWC3, 330A))
@@ -297,6 +298,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
udelay(1);
} while (--retries);
+ dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n");
return -ETIMEDOUT;
done:
@@ -342,7 +344,6 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
* from the default, this will set clock period in DWC3_GUCTL
* register.
* @dwc: Pointer to our controller context structure
- * @ref_clk_per: reference clock period in ns
*/
static void dwc3_ref_clk_period(struct dwc3 *dwc)
{
@@ -964,10 +965,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
return;
vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL);
- if (!vals) {
- dev_err(dev, "Error to get memory\n");
+ if (!vals)
return;
- }
/* Get INCR burst type, and parse it */
ret = device_property_read_u32_array(dev,
@@ -1268,40 +1267,36 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
if (IS_ERR(dwc->usb2_phy)) {
ret = PTR_ERR(dwc->usb2_phy);
- if (ret == -ENXIO || ret == -ENODEV) {
+ if (ret == -ENXIO || ret == -ENODEV)
dwc->usb2_phy = NULL;
- } else {
+ else
return dev_err_probe(dev, ret, "no usb2 phy configured\n");
- }
}
if (IS_ERR(dwc->usb3_phy)) {
ret = PTR_ERR(dwc->usb3_phy);
- if (ret == -ENXIO || ret == -ENODEV) {
+ if (ret == -ENXIO || ret == -ENODEV)
dwc->usb3_phy = NULL;
- } else {
+ else
return dev_err_probe(dev, ret, "no usb3 phy configured\n");
- }
}
dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
if (IS_ERR(dwc->usb2_generic_phy)) {
ret = PTR_ERR(dwc->usb2_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV) {
+ if (ret == -ENOSYS || ret == -ENODEV)
dwc->usb2_generic_phy = NULL;
- } else {
+ else
return dev_err_probe(dev, ret, "no usb2 phy configured\n");
- }
}
dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
if (IS_ERR(dwc->usb3_generic_phy)) {
ret = PTR_ERR(dwc->usb3_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV) {
+ if (ret == -ENOSYS || ret == -ENODEV)
dwc->usb3_generic_phy = NULL;
- } else {
+ else
return dev_err_probe(dev, ret, "no usb3 phy configured\n");
- }
}
return 0;
@@ -1633,6 +1628,46 @@ static void dwc3_check_params(struct dwc3 *dwc)
}
}
+static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+ struct device_node *np_phy;
+ struct extcon_dev *edev = NULL;
+ const char *name;
+
+ if (device_property_read_bool(dev, "extcon"))
+ return extcon_get_edev_by_phandle(dev, 0);
+
+ /*
+ * Device tree platforms should get extcon via phandle.
+ * On ACPI platforms, we get the name from a device property.
+ * This device property is for kernel internal use only and
+ * is expected to be set by the glue code.
+ */
+ if (device_property_read_string(dev, "linux,extcon-name", &name) == 0)
+ return extcon_get_extcon_dev(name);
+
+ /*
+ * Try to get an extcon device from the USB PHY controller's "port"
+ * node. Check if it has the "port" node first, to avoid printing the
+ * error message from underlying code, as it's a valid case: extcon
+ * device (and "port" node) may be missing in case of "usb-role-switch"
+ * or OTG mode.
+ */
+ np_phy = of_parse_phandle(dev->of_node, "phys", 0);
+ if (of_graph_is_present(np_phy)) {
+ struct device_node *np_conn;
+
+ np_conn = of_graph_get_remote_node(np_phy, -1, -1);
+ if (np_conn)
+ edev = extcon_find_edev_by_node(np_conn);
+ of_node_put(np_conn);
+ }
+ of_node_put(np_phy);
+
+ return edev;
+}
+
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1768,6 +1803,13 @@ static int dwc3_probe(struct platform_device *pdev)
goto err2;
}
+ dwc->edev = dwc3_get_extcon(dwc);
+ if (IS_ERR(dwc->edev)) {
+ ret = PTR_ERR(dwc->edev);
+ dev_err_probe(dwc->dev, ret, "failed to get extcon\n");
+ goto err3;
+ }
+
ret = dwc3_get_dr_mode(dwc);
if (ret)
goto err3;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5c9d467195a6..81c486b3941c 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1046,6 +1046,7 @@ struct dwc3_scratchpad_array {
* @tx_thr_num_pkt_prd: periodic ESS transmit packet count
* @tx_max_burst_prd: max periodic ESS transmit burst size
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
+ * @clear_stall_protocol: endpoint number that requires a delayed status phase
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @softconnect: true when gadget connect is called, false when disconnect runs
@@ -1266,6 +1267,7 @@ struct dwc3 {
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
u8 tx_fifo_resize_max_num;
+ u8 clear_stall_protocol;
const char *hsphy_interface;
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
new file mode 100644
index 000000000000..173cf3579c55
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwc3-am62.c - TI specific Glue layer for AM62 DWC3 USB Controller
+ *
+ * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/pinctrl/consumer.h>
+
+/* USB WRAPPER register offsets */
+#define USBSS_PID 0x0
+#define USBSS_OVERCURRENT_CTRL 0x4
+#define USBSS_PHY_CONFIG 0x8
+#define USBSS_PHY_TEST 0xc
+#define USBSS_CORE_STAT 0x14
+#define USBSS_HOST_VBUS_CTRL 0x18
+#define USBSS_MODE_CONTROL 0x1c
+#define USBSS_WAKEUP_CONFIG 0x30
+#define USBSS_WAKEUP_STAT 0x34
+#define USBSS_OVERRIDE_CONFIG 0x38
+#define USBSS_IRQ_MISC_STATUS_RAW 0x430
+#define USBSS_IRQ_MISC_STATUS 0x434
+#define USBSS_IRQ_MISC_ENABLE_SET 0x438
+#define USBSS_IRQ_MISC_ENABLE_CLR 0x43c
+#define USBSS_IRQ_MISC_EOI 0x440
+#define USBSS_INTR_TEST 0x490
+#define USBSS_VBUS_FILTER 0x614
+#define USBSS_VBUS_STAT 0x618
+#define USBSS_DEBUG_CFG 0x708
+#define USBSS_DEBUG_DATA 0x70c
+#define USBSS_HOST_HUB_CTRL 0x714
+
+/* PHY CONFIG register bits */
+#define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1)
+#define USBSS_PHY_VBUS_SEL_SHIFT 1
+#define USBSS_PHY_LANE_REVERSE BIT(0)
+
+/* MODE CONTROL register bits */
+#define USBSS_MODE_VALID BIT(0)
+
+/* WAKEUP CONFIG register bits */
+#define USBSS_WAKEUP_CFG_OVERCURRENT_EN BIT(3)
+#define USBSS_WAKEUP_CFG_LINESTATE_EN BIT(2)
+#define USBSS_WAKEUP_CFG_SESSVALID_EN BIT(1)
+#define USBSS_WAKEUP_CFG_VBUSVALID_EN BIT(0)
+
+/* WAKEUP STAT register bits */
+#define USBSS_WAKEUP_STAT_OVERCURRENT BIT(4)
+#define USBSS_WAKEUP_STAT_LINESTATE BIT(3)
+#define USBSS_WAKEUP_STAT_SESSVALID BIT(2)
+#define USBSS_WAKEUP_STAT_VBUSVALID BIT(1)
+#define USBSS_WAKEUP_STAT_CLR BIT(0)
+
+/* IRQ_MISC_STATUS_RAW register bits */
+#define USBSS_IRQ_MISC_RAW_VBUSVALID BIT(22)
+#define USBSS_IRQ_MISC_RAW_SESSVALID BIT(20)
+
+/* IRQ_MISC_STATUS register bits */
+#define USBSS_IRQ_MISC_VBUSVALID BIT(22)
+#define USBSS_IRQ_MISC_SESSVALID BIT(20)
+
+/* IRQ_MISC_ENABLE_SET register bits */
+#define USBSS_IRQ_MISC_ENABLE_SET_VBUSVALID BIT(22)
+#define USBSS_IRQ_MISC_ENABLE_SET_SESSVALID BIT(20)
+
+/* IRQ_MISC_ENABLE_CLR register bits */
+#define USBSS_IRQ_MISC_ENABLE_CLR_VBUSVALID BIT(22)
+#define USBSS_IRQ_MISC_ENABLE_CLR_SESSVALID BIT(20)
+
+/* IRQ_MISC_EOI register bits */
+#define USBSS_IRQ_MISC_EOI_VECTOR BIT(0)
+
+/* VBUS_STAT register bits */
+#define USBSS_VBUS_STAT_SESSVALID BIT(2)
+#define USBSS_VBUS_STAT_VBUSVALID BIT(0)
+
+/* Mask for PHY PLL REFCLK */
+#define PHY_PLL_REFCLK_MASK GENMASK(3, 0)
+
+#define DWC3_AM62_AUTOSUSPEND_DELAY 100
+
+struct dwc3_data {
+ struct device *dev;
+ void __iomem *usbss;
+ struct clk *usb2_refclk;
+ int rate_code;
+ struct regmap *syscon;
+ unsigned int offset;
+ unsigned int vbus_divider;
+};
+
+static const int dwc3_ti_rate_table[] = { /* in KHZ */
+ 9600,
+ 10000,
+ 12000,
+ 19200,
+ 20000,
+ 24000,
+ 25000,
+ 26000,
+ 38400,
+ 40000,
+ 58000,
+ 50000,
+ 52000,
+};
+
+static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
+{
+ return readl((data->usbss) + offset);
+}
+
+static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
+{
+ writel(value, (data->usbss) + offset);
+}
+
+static int phy_syscon_pll_refclk(struct dwc3_data *data)
+{
+ struct device *dev = data->dev;
+ struct device_node *node = dev->of_node;
+ struct of_phandle_args args;
+ struct regmap *syscon;
+ int ret;
+
+ syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
+ if (IS_ERR(syscon)) {
+ dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
+ return PTR_ERR(syscon);
+ }
+
+ data->syscon = syscon;
+
+ ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
+ 0, &args);
+ if (ret)
+ return ret;
+
+ data->offset = args.args[0];
+
+ ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
+ if (ret) {
+ dev_err(dev, "failed to set phy pll reference clock rate\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_ti_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct dwc3_data *data;
+ int i, ret;
+ unsigned long rate;
+ u32 reg;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev;
+ platform_set_drvdata(pdev, data);
+
+ data->usbss = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->usbss)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->usbss);
+ }
+
+ data->usb2_refclk = devm_clk_get(dev, "ref");
+ if (IS_ERR(data->usb2_refclk)) {
+ dev_err(dev, "can't get usb2_refclk\n");
+ return PTR_ERR(data->usb2_refclk);
+ }
+
+ /* Calculate the rate code */
+ rate = clk_get_rate(data->usb2_refclk);
+ rate /= 1000; // To KHz
+ for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
+ if (dwc3_ti_rate_table[i] == rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(dwc3_ti_rate_table)) {
+ dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
+ return -EINVAL;
+ }
+
+ data->rate_code = i;
+
+ /* Read the syscon property and set the rate code */
+ ret = phy_syscon_pll_refclk(data);
+ if (ret)
+ return ret;
+
+ /* VBUS divider select */
+ data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+ reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG);
+ if (data->vbus_divider)
+ reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
+
+ dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Don't ignore its dependencies with its children
+ */
+ pm_suspend_ignore_children(dev, false);
+ clk_prepare_enable(data->usb2_refclk);
+ pm_runtime_get_noresume(dev);
+
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to create dwc3 core: %d\n", ret);
+ goto err_pm_disable;
+ }
+
+ /* Set mode valid bit to indicate role is valid */
+ reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+ reg |= USBSS_MODE_VALID;
+ dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+
+ /* Setting up autosuspend */
+ pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return 0;
+
+err_pm_disable:
+ clk_disable_unprepare(data->usb2_refclk);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ return ret;
+}
+
+static int dwc3_ti_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static int dwc3_ti_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dwc3_data *data = platform_get_drvdata(pdev);
+ u32 reg;
+
+ device_for_each_child(dev, NULL, dwc3_ti_remove_core);
+
+ /* Clear mode valid bit */
+ reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+ reg &= ~USBSS_MODE_VALID;
+ dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+
+ pm_runtime_put_sync(dev);
+ clk_disable_unprepare(data->usb2_refclk);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int dwc3_ti_suspend_common(struct device *dev)
+{
+ struct dwc3_data *data = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(data->usb2_refclk);
+
+ return 0;
+}
+
+static int dwc3_ti_resume_common(struct device *dev)
+{
+ struct dwc3_data *data = dev_get_drvdata(dev);
+
+ clk_prepare_enable(data->usb2_refclk);
+
+ return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(dwc3_ti_pm_ops, dwc3_ti_suspend_common,
+ dwc3_ti_resume_common, NULL);
+
+#define DEV_PM_OPS (&dwc3_ti_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static const struct of_device_id dwc3_ti_of_match[] = {
+ { .compatible = "ti,am62-usb"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
+
+static struct platform_driver dwc3_ti_driver = {
+ .probe = dwc3_ti_probe,
+ .remove = dwc3_ti_remove,
+ .driver = {
+ .name = "dwc3-am62",
+ .pm = DEV_PM_OPS,
+ .of_match_table = dwc3_ti_of_match,
+ },
+};
+
+module_platform_driver(dwc3_ti_driver);
+
+MODULE_ALIAS("platform:dwc3-am62");
+MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare USB3 TI Glue Layer");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 1064be5518f6..5d642660fd15 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -218,7 +218,7 @@ out:
return ret;
}
-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@@ -813,7 +813,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
int ret = -EINVAL;
u32 len;
- if (!dwc->gadget_driver)
+ if (!dwc->gadget_driver || !dwc->connected)
goto out;
trace_dwc3_ctrl_req(ctrl);
@@ -1080,6 +1080,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
unsigned int direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
+ dwc->clear_stall_protocol = 0;
if (dwc->ep0state != EP0_STATUS_PHASE)
return;
@@ -1087,13 +1088,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
}
-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
- if (!dep->resource_index)
+ /*
+ * For status/DATA OUT stage, TRB will be queued on ep0 out
+ * endpoint for which resource index is zero. Hence allow
+ * queuing ENDXFER command for ep0 out endpoint.
+ */
+ if (!dep->resource_index && dep->number)
return;
cmd = DWC3_DEPCMD_ENDTRANSFER;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 0b9c2493844a..0d89dfa6eef5 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -657,7 +657,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
/**
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
* @dwc: pointer to the DWC3 context
- * @nfifos: number of fifos to calculate for
*
* Calculates the size value based on the equation below:
*
@@ -690,7 +689,7 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
}
/**
- * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation
+ * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation
* @dwc: pointer to the DWC3 context
*
* Iterates through all the endpoint registers and clears the previous txfifo
@@ -783,7 +782,8 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
num_fifos = 3;
if (dep->endpoint.maxburst > 6 &&
- usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
+ (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31))
num_fifos = dwc->tx_fifo_resize_max_num;
/* FIFO size for a single buffer */
@@ -882,12 +882,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dep->trb_dequeue = 0;
+ dep->trb_enqueue = 0;
+
if (usb_endpoint_xfer_control(desc))
goto out;
/* Initialize the TRB ring */
- dep->trb_dequeue = 0;
- dep->trb_enqueue = 0;
memset(dep->trb_pool, 0,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
@@ -2001,10 +2002,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
{
struct dwc3_request *req;
- struct dwc3_request *tmp;
struct dwc3 *dwc = dep->dwc;
- list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+ while (!list_empty(&dep->cancelled_list)) {
+ req = next_request(&dep->cancelled_list);
dwc3_gadget_ep_skip_trbs(dep, req);
switch (req->status) {
case DWC3_REQUEST_STATUS_DISCONNECTED:
@@ -2021,6 +2022,12 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
dwc3_gadget_giveback(dep, req, -ECONNRESET);
break;
}
+ /*
+ * The endpoint is disabled, let the dwc3_remove_requests()
+ * handle the cleanup.
+ */
+ if (!dep->endpoint.desc)
+ break;
}
}
@@ -2056,16 +2063,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
if (r == req) {
struct dwc3_request *t;
- /*
- * If a Setup packet is received but yet to DMA out, the controller will
- * not process the End Transfer command of any endpoint. Polling of its
- * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
- * timeout. Delay issuing the End Transfer command until the Setup TRB is
- * prepared.
- */
- if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
- dep->flags |= DWC3_EP_DELAY_STOP;
-
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
@@ -2152,6 +2149,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
(dep->flags & DWC3_EP_DELAY_STOP)) {
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+ if (protocol)
+ dwc->clear_stall_protocol = dep->number;
+
return 0;
}
@@ -2498,27 +2498,63 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
static void __dwc3_gadget_stop(struct dwc3 *dwc);
static int __dwc3_gadget_start(struct dwc3 *dwc);
-static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
- int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->connected = false;
- is_on = !!is_on;
- dwc->softconnect = is_on;
/*
* Per databook, when we want to stop the gadget, if a control transfer
* is still in process, complete it and get the core into setup phase.
*/
- if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ int ret;
+
reinit_completion(&dwc->ep0_in_setup);
+ spin_unlock_irqrestore(&dwc->lock, flags);
ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+ spin_lock_irqsave(&dwc->lock, flags);
if (ret == 0)
dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
}
+ /*
+ * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.8 Table 4-7, it states that for a device-initiated
+ * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
+ * command for any active transfers" before clearing the RunStop
+ * bit.
+ */
+ dwc3_stop_active_transfers(dwc);
+ __dwc3_gadget_stop(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ /*
+ * Note: if the GEVNTCOUNT indicates events in the event buffer, the
+ * driver needs to acknowledge them before the controller can halt.
+ * Simply let the interrupt handler acknowledges and handle the
+ * remaining event generated by the controller while polling for
+ * DSTS.DEVCTLHLT.
+ */
+ return dwc3_gadget_run_stop(dwc, false, false);
+}
+
+static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ int ret;
+
+ is_on = !!is_on;
+
+ if (dwc->pullups_connected == is_on)
+ return 0;
+
+ dwc->softconnect = is_on;
+
/*
* Avoid issuing a runtime resume if the device is already in the
* suspended state during gadget disconnect. DWC3 gadget was already
@@ -2541,42 +2577,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
return 0;
}
- /*
- * Synchronize and disable any further event handling while controller
- * is being enabled/disabled.
- */
- disable_irq(dwc->irq_gadget);
-
- spin_lock_irqsave(&dwc->lock, flags);
-
if (!is_on) {
- u32 count;
-
- dwc->connected = false;
- /*
- * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
- * Section 4.1.8 Table 4-7, it states that for a device-initiated
- * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
- * command for any active transfers" before clearing the RunStop
- * bit.
- */
- dwc3_stop_active_transfers(dwc);
- __dwc3_gadget_stop(dwc);
-
- /*
- * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
- * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the
- * "software needs to acknowledge the events that are generated
- * (by writing to GEVNTCOUNTn) while it is waiting for this bit
- * to be set to '1'."
- */
- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
- count &= DWC3_GEVNTCOUNT_MASK;
- if (count > 0) {
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
- dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) %
- dwc->ev_buf->length;
- }
+ ret = dwc3_gadget_soft_disconnect(dwc);
} else {
/*
* In the Synopsys DWC_usb31 1.90a programming guide section
@@ -2584,18 +2586,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
* device-initiated disconnect requires a core soft reset
* (DCTL.CSftRst) before enabling the run/stop bit.
*/
- spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_core_soft_reset(dwc);
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc);
+ ret = dwc3_gadget_run_stop(dwc, true, false);
}
- ret = dwc3_gadget_run_stop(dwc, is_on, false);
- spin_unlock_irqrestore(&dwc->lock, flags);
- enable_irq(dwc->irq_gadget);
-
pm_runtime_put(dwc->dev);
return ret;
@@ -2745,6 +2742,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
+ dwc->ep0_bounced = false;
dwc->link_state = DWC3_LINK_STATE_SS_DIS;
dwc->delayed_status = false;
dwc3_ep0_out_start(dwc);
@@ -2978,6 +2976,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
struct dwc3 *dwc = dep->dwc;
u32 mdwidth;
int size;
+ int maxpacket;
mdwidth = dwc3_mdwidth(dwc);
@@ -2990,21 +2989,24 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
else
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
- /* FIFO Depth is in MDWDITH bytes. Multiply */
- size *= mdwidth;
-
/*
- * To meet performance requirement, a minimum TxFIFO size of 3x
- * MaxPacketSize is recommended for endpoints that support burst and a
- * minimum TxFIFO size of 2x MaxPacketSize for endpoints that don't
- * support burst. Use those numbers and we can calculate the max packet
- * limit as below.
+ * maxpacket size is determined as part of the following, after assuming
+ * a mult value of one maxpacket:
+ * DWC3 revision 280A and prior:
+ * fifo_size = mult * (max_packet / mdwidth) + 1;
+ * maxpacket = mdwidth * (fifo_size - 1);
+ *
+ * DWC3 revision 290A and onwards:
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ * maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth;
*/
- if (dwc->maximum_speed >= USB_SPEED_SUPER)
- size /= 3;
+ if (DWC3_VER_IS_PRIOR(DWC3, 290A))
+ maxpacket = mdwidth * (size - 1);
else
- size /= 2;
+ maxpacket = mdwidth * ((size - 1) - 1) - mdwidth;
+ /* Functionally, space for one max packet is sufficient */
+ size = min_t(int, maxpacket, 1024);
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
dep->endpoint.max_streams = 16;
@@ -3333,15 +3335,21 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event, int status)
{
struct dwc3_request *req;
- struct dwc3_request *tmp;
- list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+ while (!list_empty(&dep->started_list)) {
int ret;
+ req = next_request(&dep->started_list);
ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
req, status);
if (ret)
break;
+ /*
+ * The endpoint is disabled, let the dwc3_remove_requests()
+ * handle the cleanup.
+ */
+ if (!dep->endpoint.desc)
+ break;
}
}
@@ -3380,14 +3388,14 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
struct dwc3 *dwc = dep->dwc;
bool no_started_trb = true;
- if (!dep->endpoint.desc)
- return no_started_trb;
-
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
goto out;
+ if (!dep->endpoint.desc)
+ return no_started_trb;
+
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->started_list) &&
(list_empty(&dep->pending_list) || status == -EXDEV))
@@ -3512,7 +3520,7 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
}
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
- if (dwc->delayed_status)
+ if (dwc->clear_stall_protocol == dep->number)
dwc3_ep0_send_delayed_status(dwc);
}
@@ -3673,11 +3681,34 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
+ struct dwc3 *dwc = dep->dwc;
+
+ /*
+ * Only issue End Transfer command to the control endpoint of a started
+ * Data Phase. Typically we should only do so in error cases such as
+ * invalid/unexpected direction as described in the control transfer
+ * flow of the programming guide.
+ */
+ if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
+ return;
+
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
(dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
+ /*
+ * If a Setup packet is received but yet to DMA out, the controller will
+ * not process the End Transfer command of any endpoint. Polling of its
+ * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+ * timeout. Delay issuing the End Transfer command until the Setup TRB is
+ * prepared.
+ */
+ if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
+ dep->flags |= DWC3_EP_DELAY_STOP;
+ return;
+ }
+
/*
* NOTICE: We are violating what the Databook says about the
* EndTransfer command. Ideally we would _always_ wait for the
@@ -3795,6 +3826,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
}
dwc3_reset_gadget(dwc);
+
+ /*
+ * From SNPS databook section 8.1.2, the EP0 should be in setup
+ * phase. So ensure that EP0 is in setup phase by issuing a stall
+ * and restart if EP0 is not in setup phase.
+ */
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ unsigned int dir;
+
+ dir = !!dwc->ep0_expect_in;
+ if (dwc->ep0state == EP0_DATA_PHASE)
+ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+ else
+ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+
+ dwc->eps[0]->trb_enqueue = 0;
+ dwc->eps[1]->trb_enqueue = 0;
+
+ dwc3_ep0_stall_and_restart(dwc);
+ }
+
/*
* In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
* Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
@@ -4197,7 +4249,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
}
evt->count = 0;
- evt->flags &= ~DWC3_EVENT_PENDING;
ret = IRQ_HANDLED;
/* Unmask interrupt */
@@ -4209,6 +4260,9 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
}
+ /* Keep the clearing of DWC3_EVENT_PENDING at the end */
+ evt->flags &= ~DWC3_EVENT_PENDING;
+
return ret;
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index f763380e672e..55a56cf67d73 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 44/64] usb: dwc3: import from kernel v6.0
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (42 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 43/64] usb: dwc3: import from kernel v5.19 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 45/64] usb: dwc3: import from kernel v6.1 Jens Wiklander
` (23 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v5.19 to v6.0.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v5.19..v6.0
Commits imported:
f5c5936d6b4d usb: dwc3: st: Fix node's child name
91062e663b26 usb: dwc3: core: leave default DMA if the controller does not support 64-bit DMA
6000b8d900cd usb: dwc3: disable USB core PHY management
e41a88f38d87 usb: dwc3: qcom: suppress unused-variable warning
040f2dbd2010 usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop
bad0d1d726ac usb: dwc3: pci: Add support for Intel Raptor Lake
ac6928f83f8d usb: dwc3: qcom: clean up suspend callbacks
e3fafbd8e365 usb: dwc3: qcom: fix wakeup implementation
c5f14abeb52b usb: dwc3: qcom: fix peripheral and OTG suspend
6498a96c8c9c usb: dwc3: qcom: fix runtime PM wakeup
a872ab303d5d usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup
c06795f114a6 usb: dwc3: qcom: fix gadget-only builds
762e744922b5 Revert "usb: dwc3: qcom: Keep power domain on to retain controller status"
d2ac7bef95c9 usb: dwc3: fix PHY disable sequence
dc14036fb324 Merge 5.19-rc7 into usb-next
69bb3520db7c usb: dwc3: qcom: fix missing optional irq warnings
07903626d988 usb: dwc3: core: Do not perform GCTL_CORE_SOFTRESET during bootup
5e76ee96be8f usb: dwc3: ep0: Properly handle setup_packet_pending scenario in data stage
8affe37c525d usb: dwc3: gadget: fix high speed multiplier setting
23385cec5f35 usb: dwc3: gadget: refactor dwc3_repare_one_trb
babfcd947eba usb: dwc3: gadget: fix a kernel-doc warning
ad44cf402486 usb: dwc3: document async_callbacks field
d9be8d5c5b03 usb: dwc3: qcom: Keep power domain on to retain controller status
6895ea55c385 usb: dwc3: qcom: Configure wakeup interrupts during suspend
360e8230516d usb: dwc3: qcom: Add helper functions to enable,disable wake irqs
649f5c842ba3 usb: dwc3: core: Host wake up support from system suspend
afbd04e66e5d usb: dwc3: core: Deprecate GCTL.CORESOFTRESET
fb119dcb97f4 Revert "usb: dwc3: Remove the checks of -ENOSYS"
22fe2b36493f Merge v5.19-rc3 into usb-next
3085d1bd47f2 usb: dwc3: Fix typos in Kconfig
d1b39dd5819a usb: dwc3: Fix a repeated word checkpatch warning
ca80ca61863f usb: dwc3: Fix bare use of unsigned checkpatch warning
df22ecc41b54 usb: dwc3: Remove the checks of -ENOSYS
3497b9a5c8c3 usb: dwc3: add power down scale setting
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 87 ++++++++++++++++++++++----------
drivers/usb/dwc3/core.h | 3 ++
drivers/usb/dwc3/ep0.c | 9 +++-
drivers/usb/dwc3/gadget.c | 101 +++++++++++++++++---------------------
4 files changed, 119 insertions(+), 81 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 573421984948..d0237b30c9be 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -158,8 +158,13 @@ static void __dwc3_set_mode(struct work_struct *work)
break;
}
- /* For DRD host or device mode only */
- if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) {
+ /*
+ * When current_dr_role is not set, there's no role switching.
+ * Only perform GCTL.CoreSoftReset when there's DRD role switching.
+ */
+ if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
+ DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
+ dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -426,7 +431,7 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
* otherwise ERR_PTR(errno).
*/
static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
- unsigned length)
+ unsigned int length)
{
struct dwc3_event_buffer *evt;
@@ -469,7 +474,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
* Returns 0 on success otherwise negative errno. In the error case, dwc
* may contain some buffers allocated but not all which were requested.
*/
-static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
+static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)
{
struct dwc3_event_buffer *evt;
@@ -828,15 +833,16 @@ static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
+
usb_phy_shutdown(dwc->usb2_phy);
usb_phy_shutdown(dwc->usb3_phy);
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- phy_power_off(dwc->usb2_generic_phy);
- phy_power_off(dwc->usb3_generic_phy);
dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset);
}
@@ -1029,6 +1035,37 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
}
+static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
+{
+ u32 scale;
+ u32 reg;
+
+ if (!dwc->susp_clk)
+ return;
+
+ /*
+ * The power down scale field specifies how many suspend_clk
+ * periods fit into a 16KHz clock period. When performing
+ * the division, round up the remainder.
+ *
+ * The power down scale value is calculated using the fastest
+ * frequency of the suspend_clk. If it isn't fixed (but within
+ * the accuracy requirement), the driver may not know the max
+ * rate of the suspend_clk, so only update the power down scale
+ * if the default is less than the calculated value from
+ * clk_get_rate() or if the default is questionably high
+ * (3x or more) to be within the requirement.
+ */
+ scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000);
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ if ((reg & DWC3_GCTL_PWRDNSCALE_MASK) < DWC3_GCTL_PWRDNSCALE(scale) ||
+ (reg & DWC3_GCTL_PWRDNSCALE_MASK) > DWC3_GCTL_PWRDNSCALE(scale*3)) {
+ reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
+ reg |= DWC3_GCTL_PWRDNSCALE(scale);
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ }
+}
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -1105,6 +1142,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err1;
+ /* Set power down scale of suspend_clk */
+ dwc3_set_power_down_clk_scale(dwc);
+
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc);
@@ -1712,12 +1752,6 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
- if (!dwc->sysdev_is_parent) {
- ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
- if (ret)
- return ret;
- }
-
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset))
return PTR_ERR(dwc->reset);
@@ -1783,6 +1817,13 @@ static int dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
+ if (!dwc->sysdev_is_parent &&
+ DWC3_GHWPARAMS0_AWIDTH(dwc->hwparams.hwparams0) == 64) {
+ ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
+ if (ret)
+ goto disable_clks;
+ }
+
spin_lock_init(&dwc->lock);
mutex_init(&dwc->mutex);
@@ -1839,16 +1880,16 @@ err5:
dwc3_debugfs_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
dwc3_ulpi_exit(dwc);
err4:
@@ -1943,7 +1984,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
- if (!PMSG_IS_AUTO(msg)) {
+ if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
dwc3_core_exit(dwc);
break;
}
@@ -2004,7 +2045,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
spin_unlock_irqrestore(&dwc->lock, flags);
break;
case DWC3_GCTL_PRTCAP_HOST:
- if (!PMSG_IS_AUTO(msg)) {
+ if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
@@ -2081,8 +2122,6 @@ static int dwc3_runtime_suspend(struct device *dev)
if (ret)
return ret;
- device_init_wakeup(dev, true);
-
return 0;
}
@@ -2091,8 +2130,6 @@ static int dwc3_runtime_resume(struct device *dev)
struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
- device_init_wakeup(dev, false);
-
ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
if (ret)
return ret;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 81c486b3941c..4fe4287dc934 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -231,6 +231,7 @@
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
+#define DWC3_GCTL_PWRDNSCALE_MASK GENMASK(31, 19)
#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
@@ -1086,6 +1087,8 @@ struct dwc3_scratchpad_array {
* @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled.
* @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled.
* @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3
+ * @async_callbacks: if set, indicate that async callbacks will be used.
+ *
* @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
* in GUSB2PHYCFG, specify that USB2 PHY doesn't
* provide a free-running PHY clock.
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 5d642660fd15..197af63f8d05 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -239,6 +239,8 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
dwc3_gadget_giveback(dep, req, -ECONNRESET);
}
+ dwc->eps[0]->trb_enqueue = 0;
+ dwc->eps[1]->trb_enqueue = 0;
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
}
@@ -473,7 +475,7 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
case USB_DEVICE_REMOTE_WAKEUP:
break;
/*
- * 9.4.1 says only only for SS, in AddressState only for
+ * 9.4.1 says only for SS, in AddressState only for
* default control pipe
*/
case USB_DEVICE_U1_ENABLE:
@@ -1140,6 +1142,11 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
+ if (dwc->setup_packet_pending) {
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
+
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 0d89dfa6eef5..eca945feeec3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -657,6 +657,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
/**
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
* @dwc: pointer to the DWC3 context
+ * @mult: multiplier to be used when calculating the fifo_size
*
* Calculates the size value based on the equation below:
*
@@ -1182,17 +1183,49 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
return trbs_left;
}
-static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
- dma_addr_t dma, unsigned int length, unsigned int chain,
- unsigned int node, unsigned int stream_id,
- unsigned int short_not_ok, unsigned int no_interrupt,
- unsigned int is_last, bool must_interrupt)
+/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @trb_length: buffer size of the TRB
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ * @use_bounce_buffer: set to use bounce buffer
+ * @must_interrupt: set to interrupt on TRB completion
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int trb_length,
+ unsigned int chain, unsigned int node, bool use_bounce_buffer,
+ bool must_interrupt)
{
+ struct dwc3_trb *trb;
+ dma_addr_t dma;
+ unsigned int stream_id = req->request.stream_id;
+ unsigned int short_not_ok = req->request.short_not_ok;
+ unsigned int no_interrupt = req->request.no_interrupt;
+ unsigned int is_last = req->request.is_last;
struct dwc3 *dwc = dep->dwc;
struct usb_gadget *gadget = dwc->gadget;
enum usb_device_speed speed = gadget->speed;
- trb->size = DWC3_TRB_SIZE_LENGTH(length);
+ if (use_bounce_buffer)
+ dma = dep->dwc->bounce_addr;
+ else if (req->request.num_sgs > 0)
+ dma = sg_dma_address(req->start_sg);
+ else
+ dma = req->request.dma;
+
+ trb = &dep->trb_pool[dep->trb_enqueue];
+
+ if (!req->trb) {
+ dwc3_gadget_move_started_request(req);
+ req->trb = trb;
+ req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+ }
+
+ req->num_trbs++;
+
+ trb->size = DWC3_TRB_SIZE_LENGTH(trb_length);
trb->bpl = lower_32_bits(dma);
trb->bph = upper_32_bits(dma);
@@ -1232,10 +1265,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
unsigned int mult = 2;
unsigned int maxp = usb_endpoint_maxp(ep->desc);
- if (length <= (2 * maxp))
+ if (req->request.length <= (2 * maxp))
mult--;
- if (length <= maxp)
+ if (req->request.length <= maxp)
mult--;
trb->size |= DWC3_TRB_SIZE_PCM1(mult);
@@ -1309,50 +1342,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
trace_dwc3_prepare_trb(dep, trb);
}
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- * @trb_length: buffer size of the TRB
- * @chain: should this TRB be chained to the next?
- * @node: only for isochronous endpoints. First TRB needs different type.
- * @use_bounce_buffer: set to use bounce buffer
- * @must_interrupt: set to interrupt on TRB completion
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
- struct dwc3_request *req, unsigned int trb_length,
- unsigned int chain, unsigned int node, bool use_bounce_buffer,
- bool must_interrupt)
-{
- struct dwc3_trb *trb;
- dma_addr_t dma;
- unsigned int stream_id = req->request.stream_id;
- unsigned int short_not_ok = req->request.short_not_ok;
- unsigned int no_interrupt = req->request.no_interrupt;
- unsigned int is_last = req->request.is_last;
-
- if (use_bounce_buffer)
- dma = dep->dwc->bounce_addr;
- else if (req->request.num_sgs > 0)
- dma = sg_dma_address(req->start_sg);
- else
- dma = req->request.dma;
-
- trb = &dep->trb_pool[dep->trb_enqueue];
-
- if (!req->trb) {
- dwc3_gadget_move_started_request(req);
- req->trb = trb;
- req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- }
-
- req->num_trbs++;
-
- __dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
- stream_id, short_not_ok, no_interrupt, is_last,
- must_interrupt);
-}
-
static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
{
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
@@ -2550,9 +2539,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
is_on = !!is_on;
- if (dwc->pullups_connected == is_on)
- return 0;
-
dwc->softconnect = is_on;
/*
@@ -2577,6 +2563,11 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
return 0;
}
+ if (dwc->pullups_connected == is_on) {
+ pm_runtime_put(dwc->dev);
+ return 0;
+ }
+
if (!is_on) {
ret = dwc3_gadget_soft_disconnect(dwc);
} else {
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 45/64] usb: dwc3: import from kernel v6.1
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (43 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 44/64] usb: dwc3: import from kernel v6.0 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 46/64] usb: dwc3: import from kernel v6.2 Jens Wiklander
` (22 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.0 to v6.1.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.0..v6.1
Commits imported:
f90f5afd5083 usb: dwc3: gadget: Clear ep descriptor last
e0481e5b3cc1 usb: dwc3: exynos: Fix remove() function
3aa07f72894d usb: dwc3: gadget: Disable GUSB2PHYCFG.SUSPHY for End Transfer
d68cc25b7c7f usb: dwc3: Do not get extcon device when usb-role-switch is used
5c294de36e7f Revert "usb: dwc3: disable USB core PHY management"
ffb9da4a04c6 usb: dwc3: gadget: Return -ESHUTDOWN on ep disable
308c316d16cb usb: dwc3: gadget: Don't set IMI for no_interrupt
f78961f8380b usb: dwc3: gadget: Stop processing more requests on IMI
3f53c329b31d usb: dwc3: st: Rely on child's compatible instead of name
4db0fbb60136 usb: dwc3: gadget: Don't delay End Transfer on delayed_status
d182c2e1bc92 usb: dwc3: Don't switch OTG -> peripheral if extcon is present
d3dcbe24a0fc Merge tag 'usb-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
7a84e7353e23 Revert "usb: dwc3: Don't switch OTG -> peripheral if extcon is present"
2adc960ce79d Revert "USB: fixup for merge issue with "usb: dwc3: Don't switch OTG -> peripheral if extcon is present""
67102bd31b4e Merge 6.0-rc7 into usb-next
2a735e4b5580 usb: dwc3: core: fix some leaks in probe
63d7f9810a38 usb: dwc3: core: Enable GUCTL1 bit 10 for fixing termination error after resume bug
a6fc2f1b0927 usb: dwc3: core: add gfladj_refclk_lpm_sel quirk
93440d1fdf0a usb: dwc3: pci: Add PCIe device ID for USB3 controller on CPU sub-system for Alder Lake P
ff2d2bee4750 usb: dwc3: pci: Update the macro names for USB PCIe device ID's for Alder Lake platforms
d6edcdc1ef06 usb: dwc3: xilinx: fix usb3 non-wakeup source resume failure
ec50e114385f usb: dwc3: xilinx: add power management ops support
875296ea8ff2 usb: dwc3: qcom: drop unneeded compatibles
76bff31c7fba usb: dwc3: gadget: Do not clear ep delayed stop flag during ep disable
af870d93c706 usb: dwc3: Fix typos in gadget.c
8422b769fa46 usb: dwc3: gadget: Submit endxfer command if delayed during disconnect
b353eb6dc285 usb: dwc3: gadget: Skip waiting for CMDACT cleared during endxfer
461ee467507c usb: dwc3: Increase DWC3 controller halt timeout
5265397f9442 usb: dwc3: Remove DWC3 locking during gadget suspend/resume
2b2da6574e77 usb: dwc3: Avoid unmapping USB requests if endxfer is not complete
a956f91247da Merge 6.0-rc4 into usb-next
dff981842a0b usb: dwc3: gadget: Continue handling EP0 xfercomplete events
9711c67de748 usb: dwc3: gadget: Synchronize IRQ between soft connect/disconnect
e1ee843488d5 usb: dwc3: gadget: Force sending delayed status during soft disconnect
359d5a85a758 usb: dwc3: Do not service EP0 and conndone events if soft disconnected
d75807ab9569 usb: dwc3: qcom: clean up icc init
b89bffa2efc9 usb: dwc3: qcom: only parse 'maximum-speed' once
808e8bff6fe4 usb: dwc3: trace: add Start of Frame Number to trace event
37a136aac3fe usb: dwc3: debug: show events parameters in hex
b44c0e7fef51 usb: dwc3: gadget: conditionally remove requests
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 100 +++++++++++++++++++++++++++-----------
drivers/usb/dwc3/core.h | 7 +++
drivers/usb/dwc3/debug.h | 4 +-
drivers/usb/dwc3/ep0.c | 11 +++--
drivers/usb/dwc3/gadget.c | 100 ++++++++++++++++++++++++++++++--------
5 files changed, 167 insertions(+), 55 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index d0237b30c9be..1f348bc867c2 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -408,6 +408,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
+
+ if (dwc->gfladj_refclk_lpm_sel)
+ reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
+
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
}
@@ -789,7 +793,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
else
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
- if (dwc->dis_u2_freeclk_exists_quirk)
+ if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel)
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
@@ -1180,6 +1184,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
+ /*
+ * When configured in HOST mode, after issuing U3/L2 exit controller
+ * fails to send proper CRC checksum in CRC5 feild. Because of this
+ * behaviour Transaction Error is generated, resulting in reset and
+ * re-enumeration of usb device attached. All the termsel, xcvrsel,
+ * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1
+ * will correct this problem. This option is to support certain
+ * legacy ULPI PHYs.
+ */
+ if (dwc->resume_hs_terminations) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
@@ -1523,8 +1542,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,dis-del-phy-power-chg-quirk");
dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
"snps,dis-tx-ipgap-linecheck-quirk");
+ dwc->resume_hs_terminations = device_property_read_bool(dev,
+ "snps,resume-hs-terminations");
dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
"snps,parkmode-disable-ss-quirk");
+ dwc->gfladj_refclk_lpm_sel = device_property_read_bool(dev,
+ "snps,gfladj-refclk-lpm-sel-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
@@ -1687,6 +1710,16 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
if (device_property_read_string(dev, "linux,extcon-name", &name) == 0)
return extcon_get_extcon_dev(name);
+ /*
+ * Check explicitly if "usb-role-switch" is used since
+ * extcon_find_edev_by_node() can not be used to check the absence of
+ * an extcon device. In the absence of an device it will always return
+ * EPROBE_DEFER.
+ */
+ if (IS_ENABLED(CONFIG_USB_ROLE_SWITCH) &&
+ device_property_read_bool(dev, "usb-role-switch"))
+ return NULL;
+
/*
* Try to get an extcon device from the USB PHY controller's "port"
* node. Check if it has the "port" node first, to avoid printing the
@@ -1753,8 +1786,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
- if (IS_ERR(dwc->reset))
- return PTR_ERR(dwc->reset);
+ if (IS_ERR(dwc->reset)) {
+ ret = PTR_ERR(dwc->reset);
+ goto put_usb_psy;
+ }
if (dev->of_node) {
/*
@@ -1764,45 +1799,57 @@ static int dwc3_probe(struct platform_device *pdev)
* check for them to retain backwards compatibility.
*/
dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
- if (IS_ERR(dwc->bus_clk))
- return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
- "could not get bus clock\n");
+ if (IS_ERR(dwc->bus_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+ goto put_usb_psy;
+ }
if (dwc->bus_clk == NULL) {
dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
- if (IS_ERR(dwc->bus_clk))
- return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
- "could not get bus clock\n");
+ if (IS_ERR(dwc->bus_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+ goto put_usb_psy;
+ }
}
dwc->ref_clk = devm_clk_get_optional(dev, "ref");
- if (IS_ERR(dwc->ref_clk))
- return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
- "could not get ref clock\n");
+ if (IS_ERR(dwc->ref_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+ goto put_usb_psy;
+ }
if (dwc->ref_clk == NULL) {
dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
- if (IS_ERR(dwc->ref_clk))
- return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
- "could not get ref clock\n");
+ if (IS_ERR(dwc->ref_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+ goto put_usb_psy;
+ }
}
dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
- if (IS_ERR(dwc->susp_clk))
- return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
- "could not get suspend clock\n");
+ if (IS_ERR(dwc->susp_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
+ goto put_usb_psy;
+ }
if (dwc->susp_clk == NULL) {
dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
- if (IS_ERR(dwc->susp_clk))
- return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
- "could not get suspend clock\n");
+ if (IS_ERR(dwc->susp_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
+ goto put_usb_psy;
+ }
}
}
ret = reset_control_deassert(dwc->reset);
if (ret)
- return ret;
+ goto put_usb_psy;
ret = dwc3_clk_enable(dwc);
if (ret)
@@ -1846,8 +1893,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->edev = dwc3_get_extcon(dwc);
if (IS_ERR(dwc->edev)) {
- ret = PTR_ERR(dwc->edev);
- dev_err_probe(dwc->dev, ret, "failed to get extcon\n");
+ ret = dev_err_probe(dwc->dev, PTR_ERR(dwc->edev), "failed to get extcon\n");
goto err3;
}
@@ -1909,7 +1955,7 @@ disable_clks:
dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);
-
+put_usb_psy:
if (dwc->usb_psy)
power_supply_put(dwc->usb_psy);
@@ -1977,9 +2023,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
case DWC3_GCTL_PRTCAP_DEVICE:
if (pm_runtime_suspended(dwc->dev))
break;
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
synchronize_irq(dwc->irq_gadget);
dwc3_core_exit(dwc);
break;
@@ -2040,9 +2084,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
break;
case DWC3_GCTL_PRTCAP_HOST:
if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4fe4287dc934..8f9959ba9fd4 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -263,6 +263,7 @@
#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
+#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10)
/* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10)
@@ -391,6 +392,7 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
+#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23)
#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
@@ -1096,6 +1098,8 @@ struct dwc3_scratchpad_array {
* change quirk.
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
* check during HS transmit.
+ * @resume-hs-terminations: Set if we enable quirk for fixing improper crc
+ * generation after resume from suspend.
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
* instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
@@ -1311,7 +1315,9 @@ struct dwc3 {
unsigned dis_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1;
unsigned dis_tx_ipgap_linecheck_quirk:1;
+ unsigned resume_hs_terminations:1;
unsigned parkmode_disable_ss_quirk:1;
+ unsigned gfladj_refclk_lpm_sel:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
@@ -1560,6 +1566,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
u32 param);
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
+void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index d223c54115f4..48b44b88dc25 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -278,7 +278,7 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
break;
case DWC3_DEPEVT_XFERINPROGRESS:
scnprintf(str + len, size - len,
- "Transfer In Progress [%d] (%c%c%c)",
+ "Transfer In Progress [%08x] (%c%c%c)",
event->parameters,
status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i',
@@ -286,7 +286,7 @@ static inline const char *dwc3_ep_event_string(char *str, size_t size,
break;
case DWC3_DEPEVT_XFERNOTREADY:
len += scnprintf(str + len, size - len,
- "Transfer Not Ready [%d]%s",
+ "Transfer Not Ready [%08x]%s",
event->parameters,
status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
" (Active)" : " (Not Active)");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 197af63f8d05..61de693461da 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc || !dwc->pullups_connected) {
+ if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
ret = -ESHUTDOWN;
@@ -293,7 +293,10 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
continue;
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
- dwc3_stop_active_transfer(dwc3_ep, true, true);
+ if (dwc->connected)
+ dwc3_stop_active_transfer(dwc3_ep, true, true);
+ else
+ dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN);
}
}
@@ -815,7 +818,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
int ret = -EINVAL;
u32 len;
- if (!dwc->gadget_driver || !dwc->connected)
+ if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out;
trace_dwc3_ctrl_req(ctrl);
@@ -1118,6 +1121,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
{
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
+ if (!dwc->softconnect || !dwc->connected)
+ return;
/*
* We already have a DATA transfer in the controller's cache,
* if we receive a XferNotReady(DATA) we will ignore it, unless
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index eca945feeec3..6d524fa76443 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -291,7 +291,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
*
* DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
- if (dwc->gadget->speed <= USB_SPEED_HIGH) {
+ if (dwc->gadget->speed <= USB_SPEED_HIGH ||
+ DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -366,7 +367,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
- if (!(cmd & DWC3_DEPCMD_CMDACT)) {
+ if (!(cmd & DWC3_DEPCMD_CMDACT) ||
+ (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
+ !(cmd & DWC3_DEPCMD_CMDIOC))) {
ret = 0;
goto skip_status;
}
@@ -965,29 +968,33 @@ out:
return 0;
}
-static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
+void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status)
{
struct dwc3_request *req;
dwc3_stop_active_transfer(dep, true, false);
+ /* If endxfer is delayed, avoid unmapping requests */
+ if (dep->flags & DWC3_EP_DELAY_STOP)
+ return;
+
/* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) {
req = next_request(&dep->started_list);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ dwc3_gadget_giveback(dep, req, status);
}
while (!list_empty(&dep->pending_list)) {
req = next_request(&dep->pending_list);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ dwc3_gadget_giveback(dep, req, status);
}
while (!list_empty(&dep->cancelled_list)) {
req = next_request(&dep->cancelled_list);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ dwc3_gadget_giveback(dep, req, status);
}
}
@@ -1005,6 +1012,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
+ u32 mask;
trace_dwc3_gadget_ep_disable(dep);
@@ -1016,18 +1024,26 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
reg &= ~DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
+
+ dep->stream_capable = false;
+ dep->type = 0;
+ mask = DWC3_EP_TXFIFO_RESIZED;
+ /*
+ * dwc3_remove_requests() can exit early if DWC3 EP delayed stop is
+ * set. Do not clear DEP flags, so that the end transfer command will
+ * be reattempted during the next SETUP stage.
+ */
+ if (dep->flags & DWC3_EP_DELAY_STOP)
+ mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
+ dep->flags &= mask;
+
/* Clear out the ep descriptors for non-ep0 */
if (dep->number > 1) {
dep->endpoint.comp_desc = NULL;
dep->endpoint.desc = NULL;
}
- dwc3_remove_requests(dwc, dep);
-
- dep->stream_capable = false;
- dep->type = 0;
- dep->flags &= DWC3_EP_TXFIFO_RESIZED;
-
return 0;
}
@@ -1277,8 +1293,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
}
- /* always enable Interrupt on Missed ISOC */
- trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ if (!no_interrupt && !chain)
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
break;
case USB_ENDPOINT_XFER_BULK:
@@ -1683,6 +1699,16 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(¶ms, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ /*
+ * If the End Transfer command was timed out while the device is
+ * not in SETUP phase, it's possible that an incoming Setup packet
+ * may prevent the command's completion. Let's retry when the
+ * ep0state returns to EP0_SETUP_PHASE.
+ */
+ if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) {
+ dep->flags |= DWC3_EP_DELAY_STOP;
+ return 0;
+ }
WARN_ON_ONCE(ret);
dep->resource_index = 0;
@@ -2340,7 +2366,7 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
if (!dep)
continue;
- dwc3_remove_requests(dwc, dep);
+ dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
}
}
@@ -2440,7 +2466,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
u32 reg;
- u32 timeout = 500;
+ u32 timeout = 2000;
if (pm_runtime_suspended(dwc->dev))
return 0;
@@ -2473,6 +2499,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
dwc3_gadget_dctl_write_safe(dwc, reg);
do {
+ usleep_range(1000, 2000);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg));
@@ -2501,6 +2528,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
if (dwc->ep0state != EP0_SETUP_PHASE) {
int ret;
+ if (dwc->delayed_status)
+ dwc3_ep0_send_delayed_status(dwc);
+
reinit_completion(&dwc->ep0_in_setup);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2568,6 +2598,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
return 0;
}
+ synchronize_irq(dwc->irq_gadget);
+
if (!is_on) {
ret = dwc3_gadget_soft_disconnect(dwc);
} else {
@@ -2718,6 +2750,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
+ dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
@@ -2725,6 +2758,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
}
dep = dwc->eps[1];
+ dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
@@ -3215,6 +3249,10 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
if (event->status & DEPEVT_STATUS_SHORT && !chain)
return 1;
+ if ((trb->ctrl & DWC3_TRB_CTRL_ISP_IMI) &&
+ DWC3_TRB_SIZE_TRBSTS(trb->size) == DWC3_TRBSTS_MISSED_ISOC)
+ return 1;
+
if ((trb->ctrl & DWC3_TRB_CTRL_IOC) ||
(trb->ctrl & DWC3_TRB_CTRL_LST))
return 1;
@@ -3568,7 +3606,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
* streams are updated, and the device controller will not be
* triggered to generate ERDY to move the next stream data. To
* workaround this and maintain compatibility with various
- * hosts, force to reinitate the stream until the host is ready
+ * hosts, force to reinitiate the stream until the host is ready
* instead of waiting for the host to prime the endpoint.
*/
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
@@ -3596,11 +3634,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep = dwc->eps[epnum];
if (!(dep->flags & DWC3_EP_ENABLED)) {
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
return;
/* Handle only EPCMDCMPLT when EP disabled */
- if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
+ if ((event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) &&
+ !(epnum <= 1 && event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE))
return;
}
@@ -3763,13 +3802,24 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_gadget_dctl_write_safe(dwc, reg);
+ dwc->connected = false;
+
dwc3_disconnect_gadget(dwc);
dwc->gadget->speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
- dwc->connected = false;
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ unsigned int dir;
+
+ dir = !!dwc->ep0_expect_in;
+ if (dwc->ep0state == EP0_DATA_PHASE)
+ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+ else
+ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+ dwc3_ep0_stall_and_restart(dwc);
+ }
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -3867,6 +3917,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
u8 lanes = 1;
u8 speed;
+ if (!dwc->softconnect)
+ return;
+
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
@@ -4129,7 +4182,7 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
unsigned int is_ss = evtinfo & BIT(4);
/*
- * WORKAROUND: DWC3 revison 2.20a with hibernation support
+ * WORKAROUND: DWC3 revision 2.20a with hibernation support
* have a known issue which can cause USB CV TD.9.23 to fail
* randomly.
*
@@ -4507,12 +4560,17 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
+ unsigned long flags;
+
if (!dwc->gadget_driver)
return 0;
dwc3_gadget_run_stop(dwc, false, false);
+
+ spin_lock_irqsave(&dwc->lock, flags);
dwc3_disconnect_gadget(dwc);
__dwc3_gadget_stop(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 46/64] usb: dwc3: import from kernel v6.2
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (44 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 45/64] usb: dwc3: import from kernel v6.1 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 47/64] usb: dwc3: import from kernel v6.3 Jens Wiklander
` (21 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.1 to v6.2.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.1..v6.2
Commits imported:
eb320f76e31d usb: dwc3: qcom: enable vbus override when in OTG dr-mode
7d80dbd708c1 usb: dwc3: fix extcon dependency
c4e3ef568539 usb: dwc3: gadget: Ignore End Transfer delay on teardown
e498a0444324 usb: dwc3: xilinx: include linux/gpio/consumer.h
63130462c919 usb: dwc3: core: defer probe on ulpi_read_id timeout
97a48da1619b usb: dwc3: qcom: Fix memory leak in dwc3_qcom_interconnect_init
62c73bfea048 usb: dwc3: Fix race between dwc3_set_mode and __dwc3_set_mode
f05f80f217bf usb: dwc3: pci: Update PCIe device ID for USB3 controller on CPU sub-system for Raptor Lake
9d1566e1f36b Merge 6.1-rc7 into usb-next
3205054dc6fe usb: dwc3: improve the config dependency of USB_DWC3_XILINX
d9c3b34d3b3a Merge 6.1-rc6 into usb-next
049142335613 usb: dwc3: gadget: Reduce TRB IOC settings
8527e9421690 Merge 6.1-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 23 ++++++++++++++++-------
drivers/usb/dwc3/gadget.c | 19 ++++++++++++++++---
2 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 1f348bc867c2..476b63618511 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -122,21 +122,25 @@ static void __dwc3_set_mode(struct work_struct *work)
unsigned long flags;
int ret;
u32 reg;
+ u32 desired_dr_role;
mutex_lock(&dwc->mutex);
+ spin_lock_irqsave(&dwc->lock, flags);
+ desired_dr_role = dwc->desired_dr_role;
+ spin_unlock_irqrestore(&dwc->lock, flags);
pm_runtime_get_sync(dwc->dev);
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
dwc3_otg_update(dwc, 0);
- if (!dwc->desired_dr_role)
+ if (!desired_dr_role)
goto out;
- if (dwc->desired_dr_role == dwc->current_dr_role)
+ if (desired_dr_role == dwc->current_dr_role)
goto out;
- if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
+ if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
goto out;
switch (dwc->current_dr_role) {
@@ -164,7 +168,7 @@ static void __dwc3_set_mode(struct work_struct *work)
*/
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
- dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
+ desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -184,11 +188,11 @@ static void __dwc3_set_mode(struct work_struct *work)
spin_lock_irqsave(&dwc->lock, flags);
- dwc3_set_prtcap(dwc, dwc->desired_dr_role);
+ dwc3_set_prtcap(dwc, desired_dr_role);
spin_unlock_irqrestore(&dwc->lock, flags);
- switch (dwc->desired_dr_role) {
+ switch (desired_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
ret = dwc3_host_init(dwc);
if (ret) {
@@ -1096,8 +1100,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (!dwc->ulpi_ready) {
ret = dwc3_core_ulpi_init(dwc);
- if (ret)
+ if (ret) {
+ if (ret == -ETIMEDOUT) {
+ dwc3_core_soft_reset(dwc);
+ ret = -EPROBE_DEFER;
+ }
goto err0;
+ }
dwc->ulpi_ready = true;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 6d524fa76443..89dcfac01235 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1464,8 +1464,18 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
*/
if (num_trbs_left == 1 || (needs_extra_trb &&
num_trbs_left <= 2 &&
- sg_dma_len(sg_next(s)) >= length))
- must_interrupt = true;
+ sg_dma_len(sg_next(s)) >= length)) {
+ struct dwc3_request *r;
+
+ /* Check if previous requests already set IOC */
+ list_for_each_entry(r, &dep->started_list, list) {
+ if (r != req && !r->request.no_interrupt)
+ break;
+
+ if (r == req)
+ must_interrupt = true;
+ }
+ }
dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false,
must_interrupt);
@@ -1717,6 +1727,7 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
else if (!ret)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_DELAY_STOP;
return ret;
}
@@ -3722,8 +3733,10 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
return;
+ if (interrupt && (dep->flags & DWC3_EP_DELAY_STOP))
+ return;
+
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
- (dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 47/64] usb: dwc3: import from kernel v6.3
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (45 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 46/64] usb: dwc3: import from kernel v6.2 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 48/64] usb: dwc3: import from kernel v6.4 Jens Wiklander
` (20 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.2 to v6.3.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.2..v6.3
Commits imported:
ec799c8a92e0 usb: dwc3: pci: add support for the Intel Meteor Lake-S
d8a2bb4eb758 usb: dwc3: gadget: Add 1ms delay after end transfer command without IOC
02c1820345e7 usb: dwc3: Fix a typo in field name
e3eafcf0fabe usb: dwc3: xilinx: Remove unused of_gpio,h
8e5248c3a877 usb: dwc3: pci: add support for the Intel Meteor Lake-M
be308d68785b USB: dwc3: fix memory leak with using debugfs_lookup()
924fb3ec50f5 Merge 6.2-rc7 into usb-next
e3e9fc7fa7ad Merge 6.2-rc5 into usb-next
c2fb9a214da1 Merge 6.2-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.h | 4 +++-
drivers/usb/dwc3/debug.h | 3 +++
drivers/usb/dwc3/gadget.c | 18 ++++++++++++------
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 8f9959ba9fd4..4743e918dcaf 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1098,7 +1098,7 @@ struct dwc3_scratchpad_array {
* change quirk.
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
* check during HS transmit.
- * @resume-hs-terminations: Set if we enable quirk for fixing improper crc
+ * @resume_hs_terminations: Set if we enable quirk for fixing improper crc
* generation after resume from suspend.
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
* instances in park mode.
@@ -1117,6 +1117,7 @@ struct dwc3_scratchpad_array {
* address.
* @num_ep_resized: carries the current number endpoints which have had its tx
* fifo resized.
+ * @debug_root: root debugfs directory for this device to put its files in.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1332,6 +1333,7 @@ struct dwc3 {
int max_cfg_eps;
int last_fifo_depth;
int num_ep_resized;
+ struct dentry *debug_root;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 48b44b88dc25..8bb2c9e3b9ac 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -414,11 +414,14 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
#ifdef CONFIG_DEBUG_FS
extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep);
+extern void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep);
extern void dwc3_debugfs_init(struct dwc3 *d);
extern void dwc3_debugfs_exit(struct dwc3 *d);
#else
static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
{ }
+static inline void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep)
+{ }
static inline void dwc3_debugfs_init(struct dwc3 *d)
{ }
static inline void dwc3_debugfs_exit(struct dwc3 *d)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 89dcfac01235..cf5b4f49c3ed 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1699,6 +1699,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
*/
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
{
+ struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
@@ -1722,10 +1723,13 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
WARN_ON_ONCE(ret);
dep->resource_index = 0;
- if (!interrupt)
+ if (!interrupt) {
+ if (!DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC3, 310A))
+ mdelay(1);
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- else if (!ret)
+ } else if (!ret) {
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ }
dep->flags &= ~DWC3_EP_DELAY_STOP;
return ret;
@@ -3194,9 +3198,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
list_del(&dep->endpoint.ep_list);
}
- debugfs_remove_recursive(debugfs_lookup(dep->name,
- debugfs_lookup(dev_name(dep->dwc->dev),
- usb_debug_root)));
+ dwc3_debugfs_remove_endpoint_dir(dep);
kfree(dep);
}
}
@@ -3776,7 +3778,11 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
* enabled, the EndTransfer command will have completed upon
* returning from this function.
*
- * This mode is NOT available on the DWC_usb31 IP.
+ * This mode is NOT available on the DWC_usb31 IP. In this
+ * case, if the IOC bit is not set, then delay by 1ms
+ * after issuing the EndTransfer command. This allows for the
+ * controller to handle the command completely before DWC3
+ * remove requests attempts to unmap USB request buffers.
*/
__dwc3_stop_active_transfer(dep, force, interrupt);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 48/64] usb: dwc3: import from kernel v6.4
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (46 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 47/64] usb: dwc3: import from kernel v6.3 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 49/64] usb: dwc3: import from kernel v6.5 Jens Wiklander
` (19 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.3 to v6.4.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.3..v6.4
Commits imported:
e3dbb6575715 USB: dwc3: fix use-after-free on core driver unbind
d2d69354226d USB: dwc3: qcom: fix NULL-deref on suspend
00f8205ffcf1 usb: dwc3: gadget: Reset num TRBs before giving back the request
8018018d9c56 usb: dwc3: fix a test for error in dwc3_core_init()
614ce6a2ea50 usb: dwc3: debugfs: Resume dwc3 before accessing registers
4e8ef34e36f2 usb: dwc3: fix gadget mode suspend interrupt handler issue
c8540870af4c usb: dwc3: gadget: Improve dwc3_gadget_suspend() and dwc3_gadget_resume()
8f40fc080813 usb: dwc3: gadget: Refactor EP0 forced stall/restart into a separate API
39674be56fba usb: dwc3: gadget: Execute gadget stop after halting the controller
02435a739b81 usb: dwc3: gadget: Stall and restart EP0 if host is unresponsive
d21a797a3eeb usb: dwc3: core: add support for disabling High-speed park mode
9a4d7dd19903 USB: dwc3: clean up probe declarations
bd82857424d3 USB: dwc3: refactor clock lookups
1d72fab47656 USB: dwc3: refactor phy handling
d2f197822d58 USB: dwc3: clean up core init error handling
c8e9eccf6ed2 USB: dwc3: clean up phy init error handling
fe296046c721 USB: dwc3: clean up probe error labels
f56d0d29b018 USB: dwc3: drop dead hibernation code
bdb19d01026a USB: dwc3: gadget: drop dead hibernation code
6b3b2402ca5b USB: dwc3: disable autosuspend on unbind
44d257e9012e USB: dwc3: fix runtime pm imbalance on unbind
9a8ad10c9f2e USB: dwc3: fix runtime pm imbalance on probe errors
091b813d3d10 usb: Add explicit of.h of_platform.h include
8e86652e3e71 Merge 6.3-rc6 into usb-next
917dc99b6591 usb: dwc3: pci: Change PCI device macros
92c08a84b53e usb: dwc3: Add function suspend and function wakeup support
047161686b81 usb: dwc3: Add remote wakeup handling
2926c5275028 usb: dwc3-am62: Fix up wake-up configuration and spurious wake up
db2c2b161d4a usb: dwc3: host: remove dead code in dwc3_host_get_irq()
97318d6427f6 Merge 6.3-rc4 into usb-next
8c4853c48d6c usb: dwc3: add several registers dump for debugfs
4e3972b589da usb: dwc3-am62: Enable as a wakeup source by default
84364a00b264 usb: dwc3-am62: Add support for system wakeup based on USB events
4fa1387261e7 usb: remove dead code in dwc3_gadget_get_irq
f09d24aa463c usb: dwc3: change some trace event __dynamic_array() to __get_buf()
4decf4060ecf usb: dwc3: gadget: Change condition for processing suspend event
b84ba26c922a usb: dwc3: core: add external vBus supply support for ulpi phy
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 448 ++++++++++++++++-------------------
drivers/usb/dwc3/core.h | 23 +-
drivers/usb/dwc3/debug.h | 2 +
drivers/usb/dwc3/dwc3-am62.c | 52 +++-
drivers/usb/dwc3/ep0.c | 19 +-
drivers/usb/dwc3/gadget.c | 353 ++++++++++++++++-----------
6 files changed, 497 insertions(+), 400 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 476b63618511..d68958e151a7 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -534,90 +534,6 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
}
-static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
-{
- if (!dwc->has_hibernation)
- return 0;
-
- if (!dwc->nr_scratch)
- return 0;
-
- dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
- DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
- if (!dwc->scratchbuf)
- return -ENOMEM;
-
- return 0;
-}
-
-static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
-{
- dma_addr_t scratch_addr;
- u32 param;
- int ret;
-
- if (!dwc->has_hibernation)
- return 0;
-
- if (!dwc->nr_scratch)
- return 0;
-
- /* should never fall here */
- if (!WARN_ON(dwc->scratchbuf))
- return 0;
-
- scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf,
- dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dwc->sysdev, scratch_addr)) {
- dev_err(dwc->sysdev, "failed to map scratch buffer\n");
- ret = -EFAULT;
- goto err0;
- }
-
- dwc->scratch_addr = scratch_addr;
-
- param = lower_32_bits(scratch_addr);
-
- ret = dwc3_send_gadget_generic_command(dwc,
- DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
- if (ret < 0)
- goto err1;
-
- param = upper_32_bits(scratch_addr);
-
- ret = dwc3_send_gadget_generic_command(dwc,
- DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
- if (ret < 0)
- goto err1;
-
- return 0;
-
-err1:
- dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
- DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
-
-err0:
- return ret;
-}
-
-static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
-{
- if (!dwc->has_hibernation)
- return;
-
- if (!dwc->nr_scratch)
- return;
-
- /* should never fall here */
- if (!WARN_ON(dwc->scratchbuf))
- return;
-
- dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
- DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
- kfree(dwc->scratchbuf);
-}
-
static void dwc3_core_num_eps(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
@@ -800,11 +716,91 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel)
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+ /*
+ * Some ULPI USB PHY does not support internal VBUS supply, to drive
+ * the CPEN pin requires the configuration of the ULPI DRVVBUSEXTERNAL
+ * bit of OTG_CTRL register. Controller configures the USB2 PHY
+ * ULPIEXTVBUSDRV bit[17] of the GUSB2PHYCFG register to drive vBus
+ * with an external supply.
+ */
+ if (dwc->ulpi_ext_vbus_drv)
+ reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
+
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0;
}
+static int dwc3_phy_init(struct dwc3 *dwc)
+{
+ int ret;
+
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
+
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err_shutdown_usb3_phy;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_exit_usb2_phy;
+
+ return 0;
+
+err_exit_usb2_phy:
+ phy_exit(dwc->usb2_generic_phy);
+err_shutdown_usb3_phy:
+ usb_phy_shutdown(dwc->usb3_phy);
+ usb_phy_shutdown(dwc->usb2_phy);
+
+ return ret;
+}
+
+static void dwc3_phy_exit(struct dwc3 *dwc)
+{
+ phy_exit(dwc->usb3_generic_phy);
+ phy_exit(dwc->usb2_generic_phy);
+
+ usb_phy_shutdown(dwc->usb3_phy);
+ usb_phy_shutdown(dwc->usb2_phy);
+}
+
+static int dwc3_phy_power_on(struct dwc3 *dwc)
+{
+ int ret;
+
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+
+ ret = phy_power_on(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err_suspend_usb3_phy;
+
+ ret = phy_power_on(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_power_off_usb2_phy;
+
+ return 0;
+
+err_power_off_usb2_phy:
+ phy_power_off(dwc->usb2_generic_phy);
+err_suspend_usb3_phy:
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+
+ return ret;
+}
+
+static void dwc3_phy_power_off(struct dwc3 *dwc)
+{
+ phy_power_off(dwc->usb3_generic_phy);
+ phy_power_off(dwc->usb2_generic_phy);
+
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+}
+
static int dwc3_clk_enable(struct dwc3 *dwc)
{
int ret;
@@ -840,17 +836,8 @@ static void dwc3_clk_disable(struct dwc3 *dwc)
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
-
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- phy_power_off(dwc->usb2_generic_phy);
- phy_power_off(dwc->usb3_generic_phy);
-
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-
+ dwc3_phy_power_off(dwc);
+ dwc3_phy_exit(dwc);
dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset);
}
@@ -877,7 +864,6 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
static void dwc3_core_setup_global_control(struct dwc3 *dwc)
{
- u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -905,9 +891,6 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
break;
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
- /* enable hibernation here */
- dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
-
/*
* REVISIT Enabling this bit so that host-mode hibernation
* will work. Device-mode hibernation is not yet implemented.
@@ -1096,7 +1079,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
ret = dwc3_phy_setup(dwc);
if (ret)
- goto err0;
+ return ret;
if (!dwc->ulpi_ready) {
ret = dwc3_core_ulpi_init(dwc);
@@ -1105,7 +1088,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_core_soft_reset(dwc);
ret = -EPROBE_DEFER;
}
- goto err0;
+ return ret;
}
dwc->ulpi_ready = true;
}
@@ -1113,25 +1096,17 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (!dwc->phys_ready) {
ret = dwc3_core_get_phy(dwc);
if (ret)
- goto err0a;
+ goto err_exit_ulpi;
dwc->phys_ready = true;
}
- usb_phy_init(dwc->usb2_phy);
- usb_phy_init(dwc->usb3_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err0a;
-
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0) {
- phy_exit(dwc->usb2_generic_phy);
- goto err0a;
- }
+ ret = dwc3_phy_init(dwc);
+ if (ret)
+ goto err_exit_ulpi;
ret = dwc3_core_soft_reset(dwc);
if (ret)
- goto err1;
+ goto err_exit_phy;
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
@@ -1151,10 +1126,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
- ret = dwc3_setup_scratch_buffers(dwc);
- if (ret)
- goto err1;
-
/* Set power down scale of suspend_clk */
dwc3_set_power_down_clk_scale(dwc);
@@ -1166,20 +1137,14 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
- usb_phy_set_suspend(dwc->usb2_phy, 0);
- usb_phy_set_suspend(dwc->usb3_phy, 0);
- ret = phy_power_on(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err2;
-
- ret = phy_power_on(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err3;
+ ret = dwc3_phy_power_on(dwc);
+ if (ret)
+ goto err_exit_phy;
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err4;
+ goto err_power_off_phy;
}
/*
@@ -1233,6 +1198,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->parkmode_disable_ss_quirk)
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+ if (dwc->parkmode_disable_hs_quirk)
+ reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS;
+
if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
(dwc->maximum_speed == USB_SPEED_HIGH ||
dwc->maximum_speed == USB_SPEED_FULL))
@@ -1296,26 +1264,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
return 0;
-err4:
- phy_power_off(dwc->usb3_generic_phy);
-
-err3:
- phy_power_off(dwc->usb2_generic_phy);
-
-err2:
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
-
-err1:
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-
-err0a:
+err_power_off_phy:
+ dwc3_phy_power_off(dwc);
+err_exit_phy:
+ dwc3_phy_exit(dwc);
+err_exit_ulpi:
dwc3_ulpi_exit(dwc);
-err0:
return ret;
}
@@ -1553,8 +1508,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,dis-tx-ipgap-linecheck-quirk");
dwc->resume_hs_terminations = device_property_read_bool(dev,
"snps,resume-hs-terminations");
+ dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev,
+ "snps,ulpi-ext-vbus-drv");
dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
"snps,parkmode-disable-ss-quirk");
+ dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev,
+ "snps,parkmode-disable-hs-quirk");
dwc->gfladj_refclk_lpm_sel = device_property_read_bool(dev,
"snps,gfladj-refclk-lpm-sel-quirk");
@@ -1750,16 +1709,72 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
return edev;
}
+static int dwc3_get_clocks(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+
+ if (!dev->of_node)
+ return 0;
+
+ /*
+ * Clocks are optional, but new DT platforms should support all clocks
+ * as required by the DT-binding.
+ * Some devices have different clock names in legacy device trees,
+ * check for them to retain backwards compatibility.
+ */
+ dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
+ if (IS_ERR(dwc->bus_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+ }
+
+ if (dwc->bus_clk == NULL) {
+ dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
+ if (IS_ERR(dwc->bus_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
+ "could not get bus clock\n");
+ }
+ }
+
+ dwc->ref_clk = devm_clk_get_optional(dev, "ref");
+ if (IS_ERR(dwc->ref_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+ }
+
+ if (dwc->ref_clk == NULL) {
+ dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
+ if (IS_ERR(dwc->ref_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
+ "could not get ref clock\n");
+ }
+ }
+
+ dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
+ if (IS_ERR(dwc->susp_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
+ }
+
+ if (dwc->susp_clk == NULL) {
+ dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
+ if (IS_ERR(dwc->susp_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
+ "could not get suspend clock\n");
+ }
+ }
+
+ return 0;
+}
+
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res, dwc_res;
+ void __iomem *regs;
struct dwc3 *dwc;
-
int ret;
- void __iomem *regs;
-
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
if (!dwc)
return -ENOMEM;
@@ -1797,77 +1812,25 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
- goto put_usb_psy;
+ goto err_put_psy;
}
- if (dev->of_node) {
- /*
- * Clocks are optional, but new DT platforms should support all
- * clocks as required by the DT-binding.
- * Some devices have different clock names in legacy device trees,
- * check for them to retain backwards compatibility.
- */
- dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
- if (IS_ERR(dwc->bus_clk)) {
- ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
- "could not get bus clock\n");
- goto put_usb_psy;
- }
-
- if (dwc->bus_clk == NULL) {
- dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
- if (IS_ERR(dwc->bus_clk)) {
- ret = dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
- "could not get bus clock\n");
- goto put_usb_psy;
- }
- }
-
- dwc->ref_clk = devm_clk_get_optional(dev, "ref");
- if (IS_ERR(dwc->ref_clk)) {
- ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
- "could not get ref clock\n");
- goto put_usb_psy;
- }
-
- if (dwc->ref_clk == NULL) {
- dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
- if (IS_ERR(dwc->ref_clk)) {
- ret = dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
- "could not get ref clock\n");
- goto put_usb_psy;
- }
- }
-
- dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
- if (IS_ERR(dwc->susp_clk)) {
- ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
- "could not get suspend clock\n");
- goto put_usb_psy;
- }
-
- if (dwc->susp_clk == NULL) {
- dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
- if (IS_ERR(dwc->susp_clk)) {
- ret = dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
- "could not get suspend clock\n");
- goto put_usb_psy;
- }
- }
- }
+ ret = dwc3_get_clocks(dwc);
+ if (ret)
+ goto err_put_psy;
ret = reset_control_deassert(dwc->reset);
if (ret)
- goto put_usb_psy;
+ goto err_put_psy;
ret = dwc3_clk_enable(dwc);
if (ret)
- goto assert_reset;
+ goto err_assert_reset;
if (!dwc3_core_is_valid(dwc)) {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
- goto disable_clks;
+ goto err_disable_clks;
}
platform_set_drvdata(pdev, dwc);
@@ -1877,19 +1840,17 @@ static int dwc3_probe(struct platform_device *pdev)
DWC3_GHWPARAMS0_AWIDTH(dwc->hwparams.hwparams0) == 64) {
ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
if (ret)
- goto disable_clks;
+ goto err_disable_clks;
}
spin_lock_init(&dwc->lock);
mutex_init(&dwc->mutex);
+ pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
pm_runtime_enable(dev);
- ret = pm_runtime_get_sync(dev);
- if (ret < 0)
- goto err1;
pm_runtime_forbid(dev);
@@ -1897,27 +1858,23 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
- goto err2;
+ goto err_allow_rpm;
}
dwc->edev = dwc3_get_extcon(dwc);
if (IS_ERR(dwc->edev)) {
ret = dev_err_probe(dwc->dev, PTR_ERR(dwc->edev), "failed to get extcon\n");
- goto err3;
+ goto err_free_event_buffers;
}
ret = dwc3_get_dr_mode(dwc);
if (ret)
- goto err3;
-
- ret = dwc3_alloc_scratch_buffers(dwc);
- if (ret)
- goto err3;
+ goto err_free_event_buffers;
ret = dwc3_core_init(dwc);
if (ret) {
dev_err_probe(dev, ret, "failed to initialize core\n");
- goto err4;
+ goto err_free_event_buffers;
}
dwc3_check_params(dwc);
@@ -1925,46 +1882,31 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init_mode(dwc);
if (ret)
- goto err5;
+ goto err_exit_debugfs;
pm_runtime_put(dev);
return 0;
-err5:
+err_exit_debugfs:
dwc3_debugfs_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
-
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- phy_power_off(dwc->usb2_generic_phy);
- phy_power_off(dwc->usb3_generic_phy);
-
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-
+ dwc3_phy_power_off(dwc);
+ dwc3_phy_exit(dwc);
dwc3_ulpi_exit(dwc);
-
-err4:
- dwc3_free_scratch_buffers(dwc);
-
-err3:
+err_free_event_buffers:
dwc3_free_event_buffers(dwc);
-
-err2:
- pm_runtime_allow(&pdev->dev);
-
-err1:
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
-
-disable_clks:
+err_allow_rpm:
+ pm_runtime_allow(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+err_disable_clks:
dwc3_clk_disable(dwc);
-assert_reset:
+err_assert_reset:
reset_control_assert(dwc->reset);
-put_usb_psy:
+err_put_psy:
if (dwc->usb_psy)
power_supply_put(dwc->usb_psy);
@@ -1983,12 +1925,18 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
+ pm_runtime_allow(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
+ /*
+ * HACK: Clear the driver data, which is currently accessed by parent
+ * glue drivers, before allowing the parent to suspend.
+ */
+ platform_set_drvdata(pdev, NULL);
pm_runtime_set_suspended(&pdev->dev);
dwc3_free_event_buffers(dwc);
- dwc3_free_scratch_buffers(dwc);
if (dwc->usb_psy)
power_supply_put(dwc->usb_psy);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4743e918dcaf..1f043c31a096 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -263,6 +263,7 @@
#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
+#define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16)
#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10)
/* Global Status Register */
@@ -280,6 +281,7 @@
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
+#define DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV BIT(17)
#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
@@ -526,6 +528,7 @@
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
+#define DWC3_DGCMD_DEV_NOTIFICATION 0x07
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
#define DWC3_DGCMD_CMDACT BIT(10)
@@ -538,6 +541,8 @@
#define DWC3_DGCMDPAR_TX_FIFO BIT(5)
#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0)
+#define DWC3_DGCMDPAR_DN_FUNC_WAKE BIT(0)
+#define DWC3_DGCMDPAR_INTF_SEL(n) ((n) << 4)
/* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16
@@ -969,12 +974,10 @@ struct dwc3_scratchpad_array {
* @drd_work: workqueue used for role swapping
* @ep0_trb: trb which is used for the ctrl_req
* @bounce: address of bounce buffer
- * @scratchbuf: address of scratch buffer
* @setup_buf: used while precessing STD USB requests
* @ep0_trb_addr: dma address of @ep0_trb
* @bounce_addr: dma address of @bounce
* @ep0_usb_req: dummy req used while handling STD USB requests
- * @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
* @mutex: for mode switching
@@ -999,7 +1002,6 @@ struct dwc3_scratchpad_array {
* @current_otg_role: current role of operation while using the OTG block
* @desired_otg_role: desired role of operation while using the OTG block
* @otg_restart_host: flag that OTG controller needs to restart host
- * @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count
@@ -1056,7 +1058,6 @@ struct dwc3_scratchpad_array {
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
- * @has_hibernation: true when dwc3 was configured with Hibernation
* @sysdev_is_parent: true when dwc3 device has a parent driver
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
@@ -1100,8 +1101,12 @@ struct dwc3_scratchpad_array {
* check during HS transmit.
* @resume_hs_terminations: Set if we enable quirk for fixing improper crc
* generation after resume from suspend.
+ * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin
+ * VBUS with an external supply.
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
* instances in park mode.
+ * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed
+ * instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -1110,6 +1115,8 @@ struct dwc3_scratchpad_array {
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @dis_split_quirk: set to disable split boundary.
+ * @wakeup_configured: set if the device is configured for remote wakeup.
+ * @suspended: set to track suspend event due to U3/L2.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
* @max_cfg_eps: current max number of IN eps used across all USB configs.
@@ -1123,11 +1130,9 @@ struct dwc3 {
struct work_struct drd_work;
struct dwc3_trb *ep0_trb;
void *bounce;
- void *scratchbuf;
u8 *setup_buf;
dma_addr_t ep0_trb_addr;
dma_addr_t bounce_addr;
- dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
struct completion ep0_in_setup;
@@ -1187,7 +1192,6 @@ struct dwc3 {
u32 current_otg_role;
u32 desired_otg_role;
bool otg_restart_host;
- u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
u32 gadget_max_speed;
@@ -1284,7 +1288,6 @@ struct dwc3 {
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
- unsigned has_hibernation:1;
unsigned sysdev_is_parent:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
@@ -1317,7 +1320,9 @@ struct dwc3 {
unsigned dis_del_phy_power_chg_quirk:1;
unsigned dis_tx_ipgap_linecheck_quirk:1;
unsigned resume_hs_terminations:1;
+ unsigned ulpi_ext_vbus_drv:1;
unsigned parkmode_disable_ss_quirk:1;
+ unsigned parkmode_disable_hs_quirk:1;
unsigned gfladj_refclk_lpm_sel:1;
unsigned tx_de_emphasis_quirk:1;
@@ -1327,6 +1332,8 @@ struct dwc3 {
unsigned dis_split_quirk:1;
unsigned async_callbacks:1;
+ unsigned wakeup_configured:1;
+ unsigned suspended:1;
u16 imod_interval;
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 8bb2c9e3b9ac..09d703852a92 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -72,6 +72,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)
return "Set Endpoint Prime";
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
return "Run SoC Bus Loopback Test";
+ case DWC3_DGCMD_DEV_NOTIFICATION:
+ return "Device Notification";
default:
return "UNKNOWN";
}
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 173cf3579c55..cda9458c809b 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -11,12 +11,14 @@
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/pinctrl/consumer.h>
+#include "core.h"
+
/* USB WRAPPER register offsets */
#define USBSS_PID 0x0
#define USBSS_OVERCURRENT_CTRL 0x4
@@ -45,6 +47,10 @@
#define USBSS_PHY_VBUS_SEL_SHIFT 1
#define USBSS_PHY_LANE_REVERSE BIT(0)
+/* CORE STAT register bits */
+#define USBSS_CORE_OPERATIONAL_MODE_MASK GENMASK(13, 12)
+#define USBSS_CORE_OPERATIONAL_MODE_SHIFT 12
+
/* MODE CONTROL register bits */
#define USBSS_MODE_VALID BIT(0)
@@ -54,6 +60,13 @@
#define USBSS_WAKEUP_CFG_SESSVALID_EN BIT(1)
#define USBSS_WAKEUP_CFG_VBUSVALID_EN BIT(0)
+#define USBSS_WAKEUP_CFG_ALL (USBSS_WAKEUP_CFG_VBUSVALID_EN | \
+ USBSS_WAKEUP_CFG_SESSVALID_EN | \
+ USBSS_WAKEUP_CFG_LINESTATE_EN | \
+ USBSS_WAKEUP_CFG_OVERCURRENT_EN)
+
+#define USBSS_WAKEUP_CFG_NONE 0
+
/* WAKEUP STAT register bits */
#define USBSS_WAKEUP_STAT_OVERCURRENT BIT(4)
#define USBSS_WAKEUP_STAT_LINESTATE BIT(3)
@@ -97,6 +110,7 @@ struct dwc3_data {
struct regmap *syscon;
unsigned int offset;
unsigned int vbus_divider;
+ u32 wakeup_stat;
};
static const int dwc3_ti_rate_table[] = { /* in KHZ */
@@ -233,6 +247,12 @@ static int dwc3_ti_probe(struct platform_device *pdev)
reg |= USBSS_MODE_VALID;
dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+ /* Device has capability to wakeup system from sleep */
+ device_set_wakeup_capable(dev, true);
+ ret = device_wakeup_enable(dev);
+ if (ret)
+ dev_err(dev, "couldn't enable device as a wakeup source: %d\n", ret);
+
/* Setting up autosuspend */
pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(dev);
@@ -281,6 +301,27 @@ static int dwc3_ti_remove(struct platform_device *pdev)
static int dwc3_ti_suspend_common(struct device *dev)
{
struct dwc3_data *data = dev_get_drvdata(dev);
+ u32 reg, current_prtcap_dir;
+
+ if (device_may_wakeup(dev)) {
+ reg = dwc3_ti_readl(data, USBSS_CORE_STAT);
+ current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK)
+ >> USBSS_CORE_OPERATIONAL_MODE_SHIFT;
+ /* Set wakeup config enable bits */
+ reg = dwc3_ti_readl(data, USBSS_WAKEUP_CONFIG);
+ if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) {
+ reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN;
+ } else {
+ reg = USBSS_WAKEUP_CFG_VBUSVALID_EN | USBSS_WAKEUP_CFG_SESSVALID_EN;
+ /*
+ * Enable LINESTATE wake up only if connected to bus
+ * and in U2/L3 state else it causes spurious wake-up.
+ */
+ }
+ dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, reg);
+ /* clear wakeup status so we know what caused the wake up */
+ dwc3_ti_writel(data, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
+ }
clk_disable_unprepare(data->usb2_refclk);
@@ -290,9 +331,18 @@ static int dwc3_ti_suspend_common(struct device *dev)
static int dwc3_ti_resume_common(struct device *dev)
{
struct dwc3_data *data = dev_get_drvdata(dev);
+ u32 reg;
clk_prepare_enable(data->usb2_refclk);
+ if (device_may_wakeup(dev)) {
+ /* Clear wakeup config enable bits */
+ dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
+ }
+
+ reg = dwc3_ti_readl(data, USBSS_WAKEUP_STAT);
+ data->wakeup_stat = reg;
+
return 0;
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 61de693461da..953b752a5052 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -30,6 +30,8 @@
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
+static int dwc3_ep0_delegate_req(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl);
static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
dma_addr_t buf_dma, u32 len, u32 type, bool chain)
@@ -356,6 +358,9 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
if (reg & DWC3_DCTL_INITU2ENA)
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
+ } else {
+ usb_status |= dwc->gadget->wakeup_armed <<
+ USB_DEVICE_REMOTE_WAKEUP;
}
break;
@@ -365,7 +370,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
- break;
+ return dwc3_ep0_delegate_req(dwc, ctrl);
case USB_RECIP_ENDPOINT:
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
@@ -476,6 +481,10 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
+ if (dwc->wakeup_configured)
+ dwc->gadget->wakeup_armed = set;
+ else
+ ret = -EINVAL;
break;
/*
* 9.4.1 says only for SS, in AddressState only for
@@ -510,13 +519,7 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
- /*
- * REVISIT: Ideally we would enable some low power mode here,
- * however it's unclear what we should be doing here.
- *
- * For now, we're not doing anything, just making sure we return
- * 0 so USB Command Verifier tests pass without any errors.
- */
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
default:
ret = -EINVAL;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index cf5b4f49c3ed..b78599dd705c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -139,6 +139,24 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
return -ETIMEDOUT;
}
+static void dwc3_ep0_reset_state(struct dwc3 *dwc)
+{
+ unsigned int dir;
+
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ dir = !!dwc->ep0_expect_in;
+ if (dwc->ep0state == EP0_DATA_PHASE)
+ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+ else
+ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+
+ dwc->eps[0]->trb_enqueue = 0;
+ dwc->eps[1]->trb_enqueue = 0;
+
+ dwc3_ep0_stall_and_restart(dwc);
+ }
+}
+
/**
* dwc3_ep_inc_trb - increment a trb index.
* @index: Pointer to the TRB index to increment.
@@ -180,6 +198,7 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
list_del(&req->list);
req->remaining = 0;
req->needs_extra_trb = false;
+ req->num_trbs = 0;
if (req->request.status == -EINPROGRESS)
req->request.status = status;
@@ -258,7 +277,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
return ret;
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
/**
* dwc3_send_gadget_ep_cmd - issue an endpoint command
@@ -325,7 +344,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
fallthrough;
case DWC3_LINK_STATE_U3:
- ret = __dwc3_gadget_wakeup(dwc);
+ ret = __dwc3_gadget_wakeup(dwc, false);
dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
ret);
break;
@@ -2273,6 +2292,22 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
/* -------------------------------------------------------------------------- */
+static void dwc3_gadget_enable_linksts_evts(struct dwc3 *dwc, bool set)
+{
+ u32 reg;
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
+ if (set)
+ reg |= DWC3_DEVTEN_ULSTCNGEN;
+ else
+ reg &= ~DWC3_DEVTEN_ULSTCNGEN;
+
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+}
+
static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
@@ -2280,7 +2315,7 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return __dwc3_gadget_get_frame(dwc);
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
{
int retries;
@@ -2311,9 +2346,13 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
return -EINVAL;
}
+ if (async)
+ dwc3_gadget_enable_linksts_evts(dwc, true);
+
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
dev_err(dwc->dev, "failed to put link in Recovery\n");
+ dwc3_gadget_enable_linksts_evts(dwc, false);
return ret;
}
@@ -2325,6 +2364,13 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
+ /*
+ * Since link status change events are enabled we will receive
+ * an U0 event when wakeup is successful. So bail out.
+ */
+ if (async)
+ return 0;
+
/* poll until Link State changes to ON */
retries = 20000;
@@ -2350,13 +2396,78 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
unsigned long flags;
int ret;
+ if (!dwc->wakeup_configured) {
+ dev_err(dwc->dev, "remote wakeup not configured\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (!dwc->gadget->wakeup_armed) {
+ dev_err(dwc->dev, "not armed for remote wakeup\n");
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -EINVAL;
+ }
+ ret = __dwc3_gadget_wakeup(dwc, true);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static void dwc3_resume_gadget(struct dwc3 *dwc);
+
+static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret;
+ int link_state;
+
+ if (!dwc->wakeup_configured) {
+ dev_err(dwc->dev, "remote wakeup not configured\n");
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_wakeup(dwc);
+ /*
+ * If the link is in U3, signal for remote wakeup and wait for the
+ * link to transition to U0 before sending device notification.
+ */
+ link_state = dwc3_gadget_get_link_state(dwc);
+ if (link_state == DWC3_LINK_STATE_U3) {
+ ret = __dwc3_gadget_wakeup(dwc, false);
+ if (ret) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -EINVAL;
+ }
+ dwc3_resume_gadget(dwc);
+ dwc->suspended = false;
+ dwc->link_state = DWC3_LINK_STATE_U0;
+ }
+
+ ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
+ DWC3_DGCMDPAR_DN_FUNC_WAKE |
+ DWC3_DGCMDPAR_INTF_SEL(intf_id));
+ if (ret)
+ dev_err(dwc->dev, "function remote wakeup failed, ret:%d\n", ret);
+
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
}
+static int dwc3_gadget_set_remote_wakeup(struct usb_gadget *g, int set)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->wakeup_configured = !!set;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
int is_selfpowered)
{
@@ -2478,7 +2589,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
}
-static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
{
u32 reg;
u32 timeout = 2000;
@@ -2497,17 +2608,11 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
- if (dwc->has_hibernation)
- reg |= DWC3_DCTL_KEEP_CONNECT;
-
__dwc3_gadget_set_speed(dwc);
dwc->pullups_connected = true;
} else {
reg &= ~DWC3_DCTL_RUN_STOP;
- if (dwc->has_hibernation && !suspend)
- reg &= ~DWC3_DCTL_KEEP_CONNECT;
-
dwc->pullups_connected = false;
}
@@ -2532,29 +2637,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc);
static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&dwc->lock, flags);
dwc->connected = false;
/*
- * Per databook, when we want to stop the gadget, if a control transfer
- * is still in process, complete it and get the core into setup phase.
+ * Attempt to end pending SETUP status phase, and not wait for the
+ * function to do so.
*/
- if (dwc->ep0state != EP0_SETUP_PHASE) {
- int ret;
-
- if (dwc->delayed_status)
- dwc3_ep0_send_delayed_status(dwc);
-
- reinit_completion(&dwc->ep0_in_setup);
-
- spin_unlock_irqrestore(&dwc->lock, flags);
- ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
- msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
- spin_lock_irqsave(&dwc->lock, flags);
- if (ret == 0)
- dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
- }
+ if (dwc->delayed_status)
+ dwc3_ep0_send_delayed_status(dwc);
/*
* In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
@@ -2564,9 +2657,28 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
* bit.
*/
dwc3_stop_active_transfers(dwc);
- __dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ /*
+ * Per databook, when we want to stop the gadget, if a control transfer
+ * is still in process, complete it and get the core into setup phase.
+ * In case the host is unresponsive to a SETUP transaction, forcefully
+ * stall the transfer, and move back to the SETUP phase, so that any
+ * pending endxfers can be executed.
+ */
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ reinit_completion(&dwc->ep0_in_setup);
+
+ ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+ msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+ if (ret == 0) {
+ dev_warn(dwc->dev, "wait for SETUP phase timed out\n");
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_ep0_reset_state(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+ }
+
/*
* Note: if the GEVNTCOUNT indicates events in the event buffer, the
* driver needs to acknowledge them before the controller can halt.
@@ -2574,7 +2686,34 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
* remaining event generated by the controller while polling for
* DSTS.DEVCTLHLT.
*/
- return dwc3_gadget_run_stop(dwc, false, false);
+ ret = dwc3_gadget_run_stop(dwc, false);
+
+ /*
+ * Stop the gadget after controller is halted, so that if needed, the
+ * events to update EP0 state can still occur while the run/stop
+ * routine polls for the halted state. DEVTEN is cleared as part of
+ * gadget stop.
+ */
+ spin_lock_irqsave(&dwc->lock, flags);
+ __dwc3_gadget_stop(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_soft_connect(struct dwc3 *dwc)
+{
+ /*
+ * In the Synopsys DWC_usb31 1.90a programming guide section
+ * 4.1.9, it specifies that for a reconnect after a
+ * device-initiated disconnect requires a core soft reset
+ * (DCTL.CSftRst) before enabling the run/stop bit.
+ */
+ dwc3_core_soft_reset(dwc);
+
+ dwc3_event_buffers_setup(dwc);
+ __dwc3_gadget_start(dwc);
+ return dwc3_gadget_run_stop(dwc, true);
}
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
@@ -2615,21 +2754,10 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
synchronize_irq(dwc->irq_gadget);
- if (!is_on) {
+ if (!is_on)
ret = dwc3_gadget_soft_disconnect(dwc);
- } else {
- /*
- * In the Synopsys DWC_usb31 1.90a programming guide section
- * 4.1.9, it specifies that for a reconnect after a
- * device-initiated disconnect requires a core soft reset
- * (DCTL.CSftRst) before enabling the run/stop bit.
- */
- dwc3_core_soft_reset(dwc);
-
- dwc3_event_buffers_setup(dwc);
- __dwc3_gadget_start(dwc);
- ret = dwc3_gadget_run_stop(dwc, true, false);
- }
+ else
+ ret = dwc3_gadget_soft_connect(dwc);
pm_runtime_put(dwc->dev);
@@ -2982,6 +3110,8 @@ static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
+ .func_wakeup = dwc3_gadget_func_wakeup,
+ .set_remote_wakeup = dwc3_gadget_set_remote_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
@@ -3814,6 +3944,8 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
int reg;
+ dwc->suspended = false;
+
dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -3827,24 +3959,19 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->gadget->speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
+ dwc->gadget->wakeup_armed = false;
+ dwc3_gadget_enable_linksts_evts(dwc, false);
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
- if (dwc->ep0state != EP0_SETUP_PHASE) {
- unsigned int dir;
-
- dir = !!dwc->ep0_expect_in;
- if (dwc->ep0state == EP0_DATA_PHASE)
- dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
- else
- dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
- dwc3_ep0_stall_and_restart(dwc);
- }
+ dwc3_ep0_reset_state(dwc);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
+ dwc->suspended = false;
+
/*
* Ideally, dwc3_reset_gadget() would trigger the function
* drivers to stop any active transfers through ep disable.
@@ -3892,20 +4019,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
* phase. So ensure that EP0 is in setup phase by issuing a stall
* and restart if EP0 is not in setup phase.
*/
- if (dwc->ep0state != EP0_SETUP_PHASE) {
- unsigned int dir;
-
- dir = !!dwc->ep0_expect_in;
- if (dwc->ep0state == EP0_DATA_PHASE)
- dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
- else
- dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
-
- dwc->eps[0]->trb_enqueue = 0;
- dwc->eps[1]->trb_enqueue = 0;
-
- dwc3_ep0_stall_and_restart(dwc);
- }
+ dwc3_ep0_reset_state(dwc);
/*
* In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
@@ -3920,6 +4034,8 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
+ dwc->gadget->wakeup_armed = false;
+ dwc3_gadget_enable_linksts_evts(dwc, false);
dwc3_clear_stall_all_ep(dwc);
/* Reset device address to zero */
@@ -4072,8 +4188,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
*/
}
-static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
+static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, unsigned int evtinfo)
{
+ dwc->suspended = false;
+
/*
* TODO take core out of low power mode when that's
* implemented.
@@ -4084,6 +4202,8 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
}
+
+ dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
}
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
@@ -4165,6 +4285,13 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
}
switch (next) {
+ case DWC3_LINK_STATE_U0:
+ if (dwc->gadget->wakeup_armed) {
+ dwc3_gadget_enable_linksts_evts(dwc, false);
+ dwc3_resume_gadget(dwc);
+ dwc->suspended = false;
+ }
+ break;
case DWC3_LINK_STATE_U1:
if (dwc->speed == USB_SPEED_SUPER)
dwc3_suspend_gadget(dwc);
@@ -4189,36 +4316,14 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
{
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
- if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
+ if (!dwc->suspended && next == DWC3_LINK_STATE_U3) {
+ dwc->suspended = true;
dwc3_suspend_gadget(dwc);
+ }
dwc->link_state = next;
}
-static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
- unsigned int evtinfo)
-{
- unsigned int is_ss = evtinfo & BIT(4);
-
- /*
- * WORKAROUND: DWC3 revision 2.20a with hibernation support
- * have a known issue which can cause USB CV TD.9.23 to fail
- * randomly.
- *
- * Because of this issue, core could generate bogus hibernation
- * events which SW needs to ignore.
- *
- * Refers to:
- *
- * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
- * Device Fallback from SuperSpeed
- */
- if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
- return;
-
- /* enter hibernation here */
-}
-
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
const struct dwc3_event_devt *event)
{
@@ -4233,29 +4338,18 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_conndone_interrupt(dwc);
break;
case DWC3_DEVICE_EVENT_WAKEUP:
- dwc3_gadget_wakeup_interrupt(dwc);
+ dwc3_gadget_wakeup_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
- if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
- "unexpected hibernation event\n"))
- break;
-
- dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
+ dev_WARN_ONCE(dwc->dev, true, "unexpected hibernation event\n");
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_SUSPEND:
/* It changed to be suspend event for version 2.30a and above */
- if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
- /*
- * Ignore suspend event until the gadget enters into
- * USB_STATE_CONFIGURED state.
- */
- if (dwc->gadget->state >= USB_STATE_CONFIGURED)
- dwc3_gadget_suspend_interrupt(dwc,
- event->event_info);
- }
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
+ dwc3_gadget_suspend_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_SOF:
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
@@ -4417,11 +4511,6 @@ static int dwc3_gadget_get_irq(struct dwc3 *dwc)
goto out;
irq = platform_get_irq(dwc3_pdev, 0);
- if (irq > 0)
- goto out;
-
- if (!irq)
- irq = -EINVAL;
out:
return irq;
@@ -4493,6 +4582,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget->sg_supported = true;
dwc->gadget->name = "dwc3-gadget";
dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable;
+ dwc->gadget->wakeup_capable = true;
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -4580,42 +4670,39 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
unsigned long flags;
+ int ret;
if (!dwc->gadget_driver)
return 0;
- dwc3_gadget_run_stop(dwc, false, false);
+ ret = dwc3_gadget_soft_disconnect(dwc);
+ if (ret)
+ goto err;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_disconnect_gadget(dwc);
- __dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
+
+err:
+ /*
+ * Attempt to reset the controller's state. Likely no
+ * communication can be established until the host
+ * performs a port reset.
+ */
+ if (dwc->softconnect)
+ dwc3_gadget_soft_connect(dwc);
+
+ return ret;
}
int dwc3_gadget_resume(struct dwc3 *dwc)
{
- int ret;
-
if (!dwc->gadget_driver || !dwc->softconnect)
return 0;
- ret = __dwc3_gadget_start(dwc);
- if (ret < 0)
- goto err0;
-
- ret = dwc3_gadget_run_stop(dwc, true, false);
- if (ret < 0)
- goto err1;
-
- return 0;
-
-err1:
- __dwc3_gadget_stop(dwc);
-
-err0:
- return ret;
+ return dwc3_gadget_soft_connect(dwc);
}
void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 49/64] usb: dwc3: import from kernel v6.5
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (47 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 48/64] usb: dwc3: import from kernel v6.4 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 50/64] usb: dwc3: import from kernel v6.6 Jens Wiklander
` (18 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.4 to v6.5.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.4..v6.5
Commits imported:
3ddaa6a27457 usb: dwc3: Properly handle processing of pending events
b32b8f2b9542 usb: dwc3: pci: skip BYT GPIO lookup table for hardwired phy
734ae15ab95a Revert "usb: dwc3: core: Enable AutoRetry feature in the controller"
e835c0a4e23c usb: dwc3: don't reset device side if dwc3 was configured as host-only
56cbceab928d Merge tag 'usb-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
c0aabed9cabe usb: dwc3: gadget: Propagate core init errors to UDC during pullup
01052b91c980 usb: dwc3-meson-g12a: Fix an error handling path in dwc3_meson_g12a_probe()
60d5b71933c4 usb: dwc3: qcom: use dev_err_probe() where appropriate
4a944da70712 usb: dwc3: qcom: Fix an error handling path in dwc3_qcom_probe()
8fd95da2cfb5 usb: dwc3: qcom: Release the correct resources in dwc3_qcom_remove()
097fb3ee710d usb: dwc3: qcom: Fix potential memory leak
b4a4be847184 USB: fix up merge of 6.4-rc4 into usb-next
7e530d32a365 Merge 6.4-rc4 into usb-next
5b3eb973bf38 usb: dwc3-xilinx: Convert to platform remove callback returning void
2f6453761e7c usb: dwc3-st: Convert to platform remove callback returning void
d662268dcca8 usb: dwc3-qcom: Convert to platform remove callback returning void
abe04efc13ed usb: dwc3-omap: Convert to platform remove callback returning void
86a2b452179b usb: dwc3-of-simple: Convert to platform remove callback returning void
3ffea6e0f34c usb: dwc3-meson-g12a: Convert to platform remove callback returning void
039e3dede538 usb: dwc3-keystone: Convert to platform remove callback returning void
3791a3e6f455 usb: dwc3-imx8mp: Convert to platform remove callback returning void
8257d5f548d6 usb: dwc3-exynos: Convert to platform remove callback returning void
890258e22117 usb: dwc3-am62: Convert to platform remove callback returning void
0176568702a5 usb: core: Convert to platform remove callback returning void
6bae03b0484b usb: dwc3-meson-g12a: support OTG switch for all IP versions
813f44d57e19 usb: dwc3: gadget: Bail out in pullup if soft reset timeout happens
2f28c3c9c347 usb: dwc3: Add error logs for unknown endpoint events
ec5eb43813a4 usb: dwc3: core: add support for realtek SoCs custom's global register start address
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 37 +++++++++++++++---------------------
drivers/usb/dwc3/core.h | 5 ++---
drivers/usb/dwc3/dwc3-am62.c | 5 ++---
drivers/usb/dwc3/ep0.c | 3 +++
drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++---
5 files changed, 41 insertions(+), 31 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index d68958e151a7..9c6bf054f15d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -277,9 +277,9 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
/*
* We're resetting only the device side because, if we're in host mode,
* XHCI driver will reset the host block. If dwc3 was configured for
- * host-only mode, then we can return early.
+ * host-only mode or current role is host, then we can return early.
*/
- if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
+ if (dwc->dr_mode == USB_DR_MODE_HOST || dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -1209,22 +1209,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
- if (dwc->dr_mode == USB_DR_MODE_HOST ||
- dwc->dr_mode == USB_DR_MODE_OTG) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
-
- /*
- * Enable Auto retry Feature to make the controller operating in
- * Host mode on seeing transaction errors(CRC errors or internal
- * overrun scenerios) on IN transfers to reply to the device
- * with a non-terminating retry ACK (i.e, an ACK transcation
- * packet with Retry=1 & Nump != 0)
- */
- reg |= DWC3_GUCTL_HSTINAUTORETRY;
-
- dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
- }
-
/*
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
@@ -1800,6 +1784,17 @@ static int dwc3_probe(struct platform_device *pdev)
dwc_res = *res;
dwc_res.start += DWC3_GLOBALS_REGS_START;
+ if (dev->of_node) {
+ struct device_node *parent = of_get_parent(dev->of_node);
+
+ if (of_device_is_compatible(parent, "realtek,rtd-dwc3")) {
+ dwc_res.start -= DWC3_GLOBALS_REGS_START;
+ dwc_res.start += DWC3_RTK_RTD_GLOBALS_REGS_START;
+ }
+
+ of_node_put(parent);
+ }
+
regs = devm_ioremap_resource(dev, &dwc_res);
if (IS_ERR(regs))
return PTR_ERR(regs);
@@ -1913,7 +1908,7 @@ err_put_psy:
return ret;
}
-static int dwc3_remove(struct platform_device *pdev)
+static void dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
@@ -1940,8 +1935,6 @@ static int dwc3_remove(struct platform_device *pdev)
if (dwc->usb_psy)
power_supply_put(dwc->usb_psy);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -2252,7 +2245,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
- .remove = dwc3_remove,
+ .remove_new = dwc3_remove,
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1f043c31a096..a69ac67d89fe 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -84,6 +84,8 @@
#define DWC3_OTG_REGS_START 0xcc00
#define DWC3_OTG_REGS_END 0xccff
+#define DWC3_RTK_RTD_GLOBALS_REGS_START 0x8100
+
/* Global Registers */
#define DWC3_GSBUSCFG0 0xc100
#define DWC3_GSBUSCFG1 0xc104
@@ -254,9 +256,6 @@
#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
-/* Global User Control Register */
-#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
-
/* Global User Control 1 Register */
#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index cda9458c809b..1755f2f848c5 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -275,7 +275,7 @@ static int dwc3_ti_remove_core(struct device *dev, void *c)
return 0;
}
-static int dwc3_ti_remove(struct platform_device *pdev)
+static void dwc3_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3_data *data = platform_get_drvdata(pdev);
@@ -294,7 +294,6 @@ static int dwc3_ti_remove(struct platform_device *pdev)
pm_runtime_set_suspended(dev);
platform_set_drvdata(pdev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -362,7 +361,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
static struct platform_driver dwc3_ti_driver = {
.probe = dwc3_ti_probe,
- .remove = dwc3_ti_remove,
+ .remove_new = dwc3_ti_remove,
.driver = {
.name = "dwc3-am62",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 953b752a5052..b94243237293 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1207,5 +1207,8 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
}
break;
+ default:
+ dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event);
+ break;
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index b78599dd705c..858fe4c299b7 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2703,13 +2703,17 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
static int dwc3_gadget_soft_connect(struct dwc3 *dwc)
{
+ int ret;
+
/*
* In the Synopsys DWC_usb31 1.90a programming guide section
* 4.1.9, it specifies that for a reconnect after a
* device-initiated disconnect requires a core soft reset
* (DCTL.CSftRst) before enabling the run/stop bit.
*/
- dwc3_core_soft_reset(dwc);
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret)
+ return ret;
dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc);
@@ -2744,7 +2748,9 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
ret = pm_runtime_get_sync(dwc->dev);
if (!ret || ret < 0) {
pm_runtime_put(dwc->dev);
- return 0;
+ if (ret < 0)
+ pm_runtime_set_suspended(dwc->dev);
+ return ret;
}
if (dwc->pullups_connected == is_on) {
@@ -3809,6 +3815,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
+ default:
+ dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event);
+ break;
}
}
@@ -4446,9 +4455,14 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
u32 count;
if (pm_runtime_suspended(dwc->dev)) {
+ dwc->pending_events = true;
+ /*
+ * Trigger runtime resume. The get() function will be balanced
+ * after processing the pending events in dwc3_process_pending
+ * events().
+ */
pm_runtime_get(dwc->dev);
disable_irq_nosync(dwc->irq_gadget);
- dwc->pending_events = true;
return IRQ_HANDLED;
}
@@ -4709,6 +4723,8 @@ void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
{
if (dwc->pending_events) {
dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf);
+ dwc3_thread_interrupt(dwc->irq_gadget, dwc->ev_buf);
+ pm_runtime_put(dwc->dev);
dwc->pending_events = false;
enable_irq(dwc->irq_gadget);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 50/64] usb: dwc3: import from kernel v6.6
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (48 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 49/64] usb: dwc3: import from kernel v6.5 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 51/64] usb: dwc3: import from kernel v6.7 Jens Wiklander
` (17 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.5 to v6.6.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.5..v6.6
Commits imported:
8bea147dfdf8 usb: dwc3: Soft reset phy on probe for host
ac2224a467b4 Merge tag 'mips_6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
592d7a4663d2 usb: dwc3: exynos: Add support for Exynos850 variant
8d1de0ebce71 usb: dwc3: remove unnecessary platform_set_drvdata()
1fa206bb764f usb: dwc3: meson-g12a: do post init to fix broken usb after resumption
bbb9e06d2c64 Merge 6.5-rc6 into usb-next
fb57f829beef usb: dwc3: dwc3-octeon: Verify clock divider
d9216d3ef538 usb: dwc3: dwc3-octeon: Add SPDX header and copyright
540264746d83 usb: dwc3: dwc3-octeon: Dump control register on clock init failure
dc0092ce24dc usb: dwc3: dwc3-octeon: Move node parsing into driver probe
c61101631cdc usb: dwc3: dwc3-octeon: Avoid half-initialized controller state
23f87bcac649 usb: dwc3: dwc3-octeon: Pass dwc3_octeon to setup functions
417840663ab8 usb: dwc3: dwc3-octeon: Use _ULL bitfields defines
976f82e8aa3c usb: dwc3: dwc3-octeon: Convert to glue driver
98a9e32bdf25 Merge 6.5-rc4 into usb-next
3609699c32aa usb: dwc3-am62: Rename private data
484468fb0f7d usb: Explicitly include correct DT includes
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 39 ++++++++++++++-
drivers/usb/dwc3/dwc3-am62.c | 96 ++++++++++++++++++------------------
2 files changed, 85 insertions(+), 50 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9c6bf054f15d..343d2570189f 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -279,9 +279,46 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
* XHCI driver will reset the host block. If dwc3 was configured for
* host-only mode or current role is host, then we can return early.
*/
- if (dwc->dr_mode == USB_DR_MODE_HOST || dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
+ /*
+ * If the dr_mode is host and the dwc->current_dr_role is not the
+ * corresponding DWC3_GCTL_PRTCAP_HOST, then the dwc3_core_init_mode
+ * isn't executed yet. Ensure the phy is ready before the controller
+ * updates the GCTL.PRTCAPDIR or other settings by soft-resetting
+ * the phy.
+ *
+ * Note: GUSB3PIPECTL[n] and GUSB2PHYCFG[n] are port settings where n
+ * is port index. If this is a multiport host, then we need to reset
+ * all active ports.
+ */
+ if (dwc->dr_mode == USB_DR_MODE_HOST) {
+ u32 usb3_port;
+ u32 usb2_port;
+
+ usb3_port = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ usb3_port |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port);
+
+ usb2_port = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ usb2_port |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port);
+
+ /* Small delay for phy reset assertion */
+ usleep_range(1000, 2000);
+
+ usb3_port &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port);
+
+ usb2_port &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port);
+
+ /* Wait for clock synchronization */
+ msleep(50);
+ return 0;
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
reg &= ~DWC3_DCTL_RUN_STOP;
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 1755f2f848c5..90a587bc29b7 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -102,7 +102,7 @@
#define DWC3_AM62_AUTOSUSPEND_DELAY 100
-struct dwc3_data {
+struct dwc3_am62 {
struct device *dev;
void __iomem *usbss;
struct clk *usb2_refclk;
@@ -129,19 +129,19 @@ static const int dwc3_ti_rate_table[] = { /* in KHZ */
52000,
};
-static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
+static inline u32 dwc3_ti_readl(struct dwc3_am62 *am62, u32 offset)
{
- return readl((data->usbss) + offset);
+ return readl((am62->usbss) + offset);
}
-static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
+static inline void dwc3_ti_writel(struct dwc3_am62 *am62, u32 offset, u32 value)
{
- writel(value, (data->usbss) + offset);
+ writel(value, (am62->usbss) + offset);
}
-static int phy_syscon_pll_refclk(struct dwc3_data *data)
+static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
{
- struct device *dev = data->dev;
+ struct device *dev = am62->dev;
struct device_node *node = dev->of_node;
struct of_phandle_args args;
struct regmap *syscon;
@@ -153,16 +153,16 @@ static int phy_syscon_pll_refclk(struct dwc3_data *data)
return PTR_ERR(syscon);
}
- data->syscon = syscon;
+ am62->syscon = syscon;
ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
0, &args);
if (ret)
return ret;
- data->offset = args.args[0];
+ am62->offset = args.args[0];
- ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
+ ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code);
if (ret) {
dev_err(dev, "failed to set phy pll reference clock rate\n");
return ret;
@@ -175,32 +175,32 @@ static int dwc3_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
- struct dwc3_data *data;
+ struct dwc3_am62 *am62;
int i, ret;
unsigned long rate;
u32 reg;
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
+ am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
+ if (!am62)
return -ENOMEM;
- data->dev = dev;
- platform_set_drvdata(pdev, data);
+ am62->dev = dev;
+ platform_set_drvdata(pdev, am62);
- data->usbss = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(data->usbss)) {
+ am62->usbss = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(am62->usbss)) {
dev_err(dev, "can't map IOMEM resource\n");
- return PTR_ERR(data->usbss);
+ return PTR_ERR(am62->usbss);
}
- data->usb2_refclk = devm_clk_get(dev, "ref");
- if (IS_ERR(data->usb2_refclk)) {
+ am62->usb2_refclk = devm_clk_get(dev, "ref");
+ if (IS_ERR(am62->usb2_refclk)) {
dev_err(dev, "can't get usb2_refclk\n");
- return PTR_ERR(data->usb2_refclk);
+ return PTR_ERR(am62->usb2_refclk);
}
/* Calculate the rate code */
- rate = clk_get_rate(data->usb2_refclk);
+ rate = clk_get_rate(am62->usb2_refclk);
rate /= 1000; // To KHz
for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
if (dwc3_ti_rate_table[i] == rate)
@@ -212,20 +212,20 @@ static int dwc3_ti_probe(struct platform_device *pdev)
return -EINVAL;
}
- data->rate_code = i;
+ am62->rate_code = i;
/* Read the syscon property and set the rate code */
- ret = phy_syscon_pll_refclk(data);
+ ret = phy_syscon_pll_refclk(am62);
if (ret)
return ret;
/* VBUS divider select */
- data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
- reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG);
- if (data->vbus_divider)
+ am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+ reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
+ if (am62->vbus_divider)
reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
- dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
+ dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -233,7 +233,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
* Don't ignore its dependencies with its children
*/
pm_suspend_ignore_children(dev, false);
- clk_prepare_enable(data->usb2_refclk);
+ clk_prepare_enable(am62->usb2_refclk);
pm_runtime_get_noresume(dev);
ret = of_platform_populate(node, NULL, NULL, dev);
@@ -243,9 +243,9 @@ static int dwc3_ti_probe(struct platform_device *pdev)
}
/* Set mode valid bit to indicate role is valid */
- reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
reg |= USBSS_MODE_VALID;
- dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+ dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
/* Device has capability to wakeup system from sleep */
device_set_wakeup_capable(dev, true);
@@ -261,7 +261,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
return 0;
err_pm_disable:
- clk_disable_unprepare(data->usb2_refclk);
+ clk_disable_unprepare(am62->usb2_refclk);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
return ret;
@@ -278,36 +278,34 @@ static int dwc3_ti_remove_core(struct device *dev, void *c)
static void dwc3_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct dwc3_data *data = platform_get_drvdata(pdev);
+ struct dwc3_am62 *am62 = platform_get_drvdata(pdev);
u32 reg;
device_for_each_child(dev, NULL, dwc3_ti_remove_core);
/* Clear mode valid bit */
- reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
reg &= ~USBSS_MODE_VALID;
- dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+ dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
pm_runtime_put_sync(dev);
- clk_disable_unprepare(data->usb2_refclk);
+ clk_disable_unprepare(am62->usb2_refclk);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
-
- platform_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
static int dwc3_ti_suspend_common(struct device *dev)
{
- struct dwc3_data *data = dev_get_drvdata(dev);
+ struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg, current_prtcap_dir;
if (device_may_wakeup(dev)) {
- reg = dwc3_ti_readl(data, USBSS_CORE_STAT);
+ reg = dwc3_ti_readl(am62, USBSS_CORE_STAT);
current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK)
>> USBSS_CORE_OPERATIONAL_MODE_SHIFT;
/* Set wakeup config enable bits */
- reg = dwc3_ti_readl(data, USBSS_WAKEUP_CONFIG);
+ reg = dwc3_ti_readl(am62, USBSS_WAKEUP_CONFIG);
if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) {
reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN;
} else {
@@ -317,30 +315,30 @@ static int dwc3_ti_suspend_common(struct device *dev)
* and in U2/L3 state else it causes spurious wake-up.
*/
}
- dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, reg);
+ dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, reg);
/* clear wakeup status so we know what caused the wake up */
- dwc3_ti_writel(data, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
+ dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
}
- clk_disable_unprepare(data->usb2_refclk);
+ clk_disable_unprepare(am62->usb2_refclk);
return 0;
}
static int dwc3_ti_resume_common(struct device *dev)
{
- struct dwc3_data *data = dev_get_drvdata(dev);
+ struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg;
- clk_prepare_enable(data->usb2_refclk);
+ clk_prepare_enable(am62->usb2_refclk);
if (device_may_wakeup(dev)) {
/* Clear wakeup config enable bits */
- dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
+ dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
}
- reg = dwc3_ti_readl(data, USBSS_WAKEUP_STAT);
- data->wakeup_stat = reg;
+ reg = dwc3_ti_readl(am62, USBSS_WAKEUP_STAT);
+ am62->wakeup_stat = reg;
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 51/64] usb: dwc3: import from kernel v6.7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (49 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 50/64] usb: dwc3: import from kernel v6.6 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 52/64] usb: dwc3: import from kernel v6.8 Jens Wiklander
` (16 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.6 to v6.7.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.6..v6.7
Commits imported:
9cf87666fc6e USB: dwc3: qcom: fix ACPI platform device leak
9feefbf57d92 USB: dwc3: qcom: fix software node leak on probe errors
51392a1879ff USB: dwc3: qcom: fix resource leaks on probe deferral
aee70a1d7113 USB: dwc3: qcom: simplify wakeup interrupt setup
41f5a0973259 USB: dwc3: qcom: fix wakeup after probe deferral
8bbae288a85a usb: dwc3: set the dma max_seg_size
791cd7afe51b usb: dwc3: add missing of_node_put and platform_device_put
10d510abd096 usb: dwc3: Fix default mode initialization
97789b93b792 usb: dwc3: add optional PHY interface clocks
de7ecc4e0570 usb: dwc3: xilinx: add reset-controller support
d0d27ef87e1c Merge 6.6-rc6 into usb-next
e24bc293a6a6 usb: dwc3: document gfladj_refclk_lpm_sel field
e72fc8d6a12a usb: dwc3: core: configure TX/RX threshold for DWC3_IP
34c200483569 usb: dwc3: add Realtek DHC RTD SoC dwc3 glue layer driver
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 190 ++++++++++++++++++++++++++++++++--------
drivers/usb/dwc3/core.h | 19 ++++
2 files changed, 173 insertions(+), 36 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 343d2570189f..b101dbf8c5dc 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -854,8 +854,20 @@ static int dwc3_clk_enable(struct dwc3 *dwc)
if (ret)
goto disable_ref_clk;
+ ret = clk_prepare_enable(dwc->utmi_clk);
+ if (ret)
+ goto disable_susp_clk;
+
+ ret = clk_prepare_enable(dwc->pipe_clk);
+ if (ret)
+ goto disable_utmi_clk;
+
return 0;
+disable_utmi_clk:
+ clk_disable_unprepare(dwc->utmi_clk);
+disable_susp_clk:
+ clk_disable_unprepare(dwc->susp_clk);
disable_ref_clk:
clk_disable_unprepare(dwc->ref_clk);
disable_bus_clk:
@@ -865,6 +877,8 @@ disable_bus_clk:
static void dwc3_clk_disable(struct dwc3 *dwc)
{
+ clk_disable_unprepare(dwc->pipe_clk);
+ clk_disable_unprepare(dwc->utmi_clk);
clk_disable_unprepare(dwc->susp_clk);
clk_disable_unprepare(dwc->ref_clk);
clk_disable_unprepare(dwc->bus_clk);
@@ -1094,6 +1108,111 @@ static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
}
}
+static void dwc3_config_threshold(struct dwc3 *dwc)
+{
+ u32 reg;
+ u8 rx_thr_num;
+ u8 rx_maxburst;
+ u8 tx_thr_num;
+ u8 tx_maxburst;
+
+ /*
+ * Must config both number of packets and max burst settings to enable
+ * RX and/or TX threshold.
+ */
+ if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ rx_thr_num = dwc->rx_thr_num_pkt_prd;
+ rx_maxburst = dwc->rx_max_burst_prd;
+ tx_thr_num = dwc->tx_thr_num_pkt_prd;
+ tx_maxburst = dwc->tx_max_burst_prd;
+
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC31_RXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num);
+
+ reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC31_TXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num);
+
+ reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ }
+
+ rx_thr_num = dwc->rx_thr_num_pkt;
+ rx_maxburst = dwc->rx_max_burst;
+ tx_thr_num = dwc->tx_thr_num_pkt;
+ tx_maxburst = dwc->tx_max_burst;
+
+ if (DWC3_IP_IS(DWC3)) {
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC3_GRXTHRCFG_PKTCNTSEL;
+
+ reg &= ~DWC3_GRXTHRCFG_RXPKTCNT(~0);
+ reg |= DWC3_GRXTHRCFG_RXPKTCNT(rx_thr_num);
+
+ reg &= ~DWC3_GRXTHRCFG_MAXRXBURSTSIZE(~0);
+ reg |= DWC3_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC3_GTXTHRCFG_PKTCNTSEL;
+
+ reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0);
+ reg |= DWC3_GTXTHRCFG_TXPKTCNT(tx_thr_num);
+
+ reg &= ~DWC3_GTXTHRCFG_MAXTXBURSTSIZE(~0);
+ reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ } else {
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC31_GRXTHRCFG_PKTCNTSEL;
+
+ reg &= ~DWC31_GRXTHRCFG_RXPKTCNT(~0);
+ reg |= DWC31_GRXTHRCFG_RXPKTCNT(rx_thr_num);
+
+ reg &= ~DWC31_GRXTHRCFG_MAXRXBURSTSIZE(~0);
+ reg |= DWC31_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC31_GTXTHRCFG_PKTCNTSEL;
+
+ reg &= ~DWC31_GTXTHRCFG_TXPKTCNT(~0);
+ reg |= DWC31_GTXTHRCFG_TXPKTCNT(tx_thr_num);
+
+ reg &= ~DWC31_GTXTHRCFG_MAXTXBURSTSIZE(~0);
+ reg |= DWC31_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ }
+}
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -1246,42 +1365,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
- /*
- * Must config both number of packets and max burst settings to enable
- * RX and/or TX threshold.
- */
- if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
- u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
- u8 rx_maxburst = dwc->rx_max_burst_prd;
- u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
- u8 tx_maxburst = dwc->tx_max_burst_prd;
-
- if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- reg |= DWC31_RXTHRNUMPKTSEL_PRD;
-
- reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
- reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num);
-
- reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
- reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
-
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
- }
-
- if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
- reg |= DWC31_TXTHRNUMPKTSEL_PRD;
-
- reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
- reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num);
-
- reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
- reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
-
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
- }
- }
+ dwc3_config_threshold(dwc);
return 0;
@@ -1417,6 +1501,10 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
+ u8 rx_thr_num_pkt = 0;
+ u8 rx_max_burst = 0;
+ u8 tx_thr_num_pkt = 0;
+ u8 tx_max_burst = 0;
u8 rx_thr_num_pkt_prd = 0;
u8 rx_max_burst_prd = 0;
u8 tx_thr_num_pkt_prd = 0;
@@ -1479,6 +1567,14 @@ static void dwc3_get_properties(struct dwc3 *dwc)
"snps,usb2-lpm-disable");
dwc->usb2_gadget_lpm_disable = device_property_read_bool(dev,
"snps,usb2-gadget-lpm-disable");
+ device_property_read_u8(dev, "snps,rx-thr-num-pkt",
+ &rx_thr_num_pkt);
+ device_property_read_u8(dev, "snps,rx-max-burst",
+ &rx_max_burst);
+ device_property_read_u8(dev, "snps,tx-thr-num-pkt",
+ &tx_thr_num_pkt);
+ device_property_read_u8(dev, "snps,tx-max-burst",
+ &tx_max_burst);
device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
&rx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,rx-max-burst-prd",
@@ -1560,6 +1656,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->hird_threshold = hird_threshold;
+ dwc->rx_thr_num_pkt = rx_thr_num_pkt;
+ dwc->rx_max_burst = rx_max_burst;
+
+ dwc->tx_thr_num_pkt = tx_thr_num_pkt;
+ dwc->tx_max_burst = tx_max_burst;
+
dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
dwc->rx_max_burst_prd = rx_max_burst_prd;
@@ -1785,6 +1887,20 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
}
}
+ /* specific to Rockchip RK3588 */
+ dwc->utmi_clk = devm_clk_get_optional(dev, "utmi");
+ if (IS_ERR(dwc->utmi_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->utmi_clk),
+ "could not get utmi clock\n");
+ }
+
+ /* specific to Rockchip RK3588 */
+ dwc->pipe_clk = devm_clk_get_optional(dev, "pipe");
+ if (IS_ERR(dwc->pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(dwc->pipe_clk),
+ "could not get pipe clock\n");
+ }
+
return 0;
}
@@ -1918,6 +2034,8 @@ static int dwc3_probe(struct platform_device *pdev)
pm_runtime_put(dev);
+ dma_set_max_seg_size(dev, UINT_MAX);
+
return 0;
err_exit_debugfs:
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index a69ac67d89fe..efe6caf4d0e8 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -211,6 +211,11 @@
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
+/* Global TX Threshold Configuration Register */
+#define DWC3_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0xff) << 16)
+#define DWC3_GTXTHRCFG_TXPKTCNT(n) (((n) & 0xf) << 24)
+#define DWC3_GTXTHRCFG_PKTCNTSEL BIT(29)
+
/* Global RX Threshold Configuration Register for DWC_usb31 only */
#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16)
#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21)
@@ -991,6 +996,8 @@ struct dwc3_scratchpad_array {
* @bus_clk: clock for accessing the registers
* @ref_clk: reference clock
* @susp_clk: clock used when the SS phy is in low power (S3) state
+ * @utmi_clk: clock used for USB2 PHY communication
+ * @pipe_clk: clock used for USB3 PHY communication
* @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
@@ -1045,6 +1052,10 @@ struct dwc3_scratchpad_array {
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
+ * @rx_thr_num_pkt: USB receive packet count
+ * @rx_max_burst: max USB receive burst size
+ * @tx_thr_num_pkt: USB transmit packet count
+ * @tx_max_burst: max USB transmit burst size
* @rx_thr_num_pkt_prd: periodic ESS receive packet count
* @rx_max_burst_prd: max periodic ESS receive burst size
* @tx_thr_num_pkt_prd: periodic ESS transmit packet count
@@ -1106,6 +1117,8 @@ struct dwc3_scratchpad_array {
* instances in park mode.
* @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed
* instances in park mode.
+ * @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter
+ * running based on ref_clk
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -1156,6 +1169,8 @@ struct dwc3 {
struct clk *bus_clk;
struct clk *ref_clk;
struct clk *susp_clk;
+ struct clk *utmi_clk;
+ struct clk *pipe_clk;
struct reset_control *reset;
@@ -1273,6 +1288,10 @@ struct dwc3 {
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
+ u8 rx_thr_num_pkt;
+ u8 rx_max_burst;
+ u8 tx_thr_num_pkt;
+ u8 tx_max_burst;
u8 rx_thr_num_pkt_prd;
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 52/64] usb: dwc3: import from kernel v6.8
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (50 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 51/64] usb: dwc3: import from kernel v6.7 Jens Wiklander
@ 2026-05-07 9:27 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 53/64] usb: dwc3: import from kernel v6.9 Jens Wiklander
` (15 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:27 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.7 to v6.8.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.7..v6.8
Commits imported:
b191a18cb5c4 usb: dwc3: gadget: Don't disconnect if not started
7d708c145b26 Revert "usb: dwc3: Support EBC feature of DWC_usb31"
61a348857e86 usb: dwc3: gadget: Fix NULL pointer dereference in dwc3_gadget_suspend
de4b5b28c87c usb: dwc3: pci: add support for the Intel Arrow Lake-H
817349b6d26a usb: dwc3: host: Set XHCI_SG_TRB_CACHE_SIZE_QUIRK
aefdcd89d72b usb: dwc3: qcom: Rename hs_phy_irq to qusb2_phy_irq
3c7af52c7616 usb: dwc3: gadget: Queue PM runtime idle on disconnect event
68c26fe58182 usb: dwc3: set pm runtime active before resume common
398aa9a7e77c usb: dwc3: Support EBC feature of DWC_usb31
91736d0619eb usb: dwc3: core: set force_gen1 bit in USB31 devices if max speed is SS
e9d40b215e38 usb: dwc: ep0: Update request status in dwc3_ep0_stall_restart
730e12fbec53 usb: dwc3: gadget: Handle EP0 request dequeuing properly
afe28cd686ae Revert "usb: dwc3: don't reset device side if dwc3 was configured as host-only"
7059fbebcb00 Revert "usb: dwc3: Soft reset phy on probe for host"
c084af69a8f4 usb: dwc3: imx8mp: Fix smatch warning
53b5ff83d893 usb: dwc3: xilinx: improve error handling for PM APIs
3396b3372e61 Merge 6.7-rc3 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 60 +++++++++++++--------------------------
drivers/usb/dwc3/core.h | 5 ++++
drivers/usb/dwc3/ep0.c | 5 +++-
drivers/usb/dwc3/gadget.c | 30 ++++++++++++++++----
4 files changed, 53 insertions(+), 47 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index b101dbf8c5dc..3e55838c0001 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -277,48 +277,11 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
/*
* We're resetting only the device side because, if we're in host mode,
* XHCI driver will reset the host block. If dwc3 was configured for
- * host-only mode or current role is host, then we can return early.
+ * host-only mode, then we can return early.
*/
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
- /*
- * If the dr_mode is host and the dwc->current_dr_role is not the
- * corresponding DWC3_GCTL_PRTCAP_HOST, then the dwc3_core_init_mode
- * isn't executed yet. Ensure the phy is ready before the controller
- * updates the GCTL.PRTCAPDIR or other settings by soft-resetting
- * the phy.
- *
- * Note: GUSB3PIPECTL[n] and GUSB2PHYCFG[n] are port settings where n
- * is port index. If this is a multiport host, then we need to reset
- * all active ports.
- */
- if (dwc->dr_mode == USB_DR_MODE_HOST) {
- u32 usb3_port;
- u32 usb2_port;
-
- usb3_port = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- usb3_port |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port);
-
- usb2_port = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- usb2_port |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port);
-
- /* Small delay for phy reset assertion */
- usleep_range(1000, 2000);
-
- usb3_port &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port);
-
- usb2_port &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port);
-
- /* Wait for clock synchronization */
- msleep(50);
- return 0;
- }
-
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
reg &= ~DWC3_DCTL_RUN_STOP;
@@ -1367,6 +1330,18 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_config_threshold(dwc);
+ /*
+ * Modify this for all supported Super Speed ports when
+ * multiport support is added.
+ */
+ if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET &&
+ (DWC3_IP_IS(DWC31)) &&
+ dwc->maximum_speed == USB_SPEED_SUPER) {
+ reg = dwc3_readl(dwc->regs, DWC3_LLUCTL);
+ reg |= DWC3_LLUCTL_FORCE_GEN1;
+ dwc3_writel(dwc->regs, DWC3_LLUCTL, reg);
+ }
+
return 0;
err_power_off_phy:
@@ -2340,12 +2315,15 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+
ret = dwc3_resume_common(dwc, PMSG_RESUME);
- if (ret)
+ if (ret) {
+ pm_runtime_set_suspended(dev);
return ret;
+ }
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index efe6caf4d0e8..e120611a5174 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -172,6 +172,8 @@
#define DWC3_OEVTEN 0xcc0C
#define DWC3_OSTS 0xcc10
+#define DWC3_LLUCTL 0xd024
+
/* Bit fields */
/* Global SoC Bus Configuration INCRx Register 0 */
@@ -657,6 +659,9 @@
#define DWC3_OSTS_VBUSVLD BIT(1)
#define DWC3_OSTS_CONIDSTS BIT(0)
+/* Force Gen1 speed on Gen2 link */
+#define DWC3_LLUCTL_FORCE_GEN1 BIT(10)
+
/* Structures */
struct dwc3_trb;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index b94243237293..6ae8a36f21cf 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -238,7 +238,10 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
struct dwc3_request *req;
req = next_request(&dep->pending_list);
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ if (!dwc->connected)
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ else
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
}
dwc->eps[0]->trb_enqueue = 0;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 858fe4c299b7..28f49400f3e8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2103,7 +2103,17 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
list_for_each_entry(r, &dep->pending_list, list) {
if (r == req) {
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ /*
+ * Explicitly check for EP0/1 as dequeue for those
+ * EPs need to be handled differently. Control EP
+ * only deals with one USB req, and giveback will
+ * occur during dwc3_ep0_stall_and_restart(). EP0
+ * requests are never added to started_list.
+ */
+ if (dep->number > 1)
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ else
+ dwc3_ep0_reset_state(dwc);
goto out;
}
}
@@ -2640,6 +2650,11 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (!dwc->pullups_connected) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return 0;
+ }
+
dwc->connected = false;
/*
@@ -3973,6 +3988,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
dwc3_ep0_reset_state(dwc);
+
+ /*
+ * Request PM idle to address condition where usage count is
+ * already decremented to zero, but waiting for the disconnect
+ * interrupt to set dwc->connected to FALSE.
+ */
+ pm_request_idle(dwc->dev);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -4686,15 +4708,13 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
unsigned long flags;
int ret;
- if (!dwc->gadget_driver)
- return 0;
-
ret = dwc3_gadget_soft_disconnect(dwc);
if (ret)
goto err;
spin_lock_irqsave(&dwc->lock, flags);
- dwc3_disconnect_gadget(dwc);
+ if (dwc->gadget_driver)
+ dwc3_disconnect_gadget(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 53/64] usb: dwc3: import from kernel v6.9
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (51 preceding siblings ...)
2026-05-07 9:27 ` [RFC PATCH v2 52/64] usb: dwc3: import from kernel v6.8 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 54/64] usb: dwc3: import from kernel v6.10 Jens Wiklander
` (14 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.8 to v6.9.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.8..v6.9
Commits imported:
6d735722063a usb: dwc3: core: Prevent phy suspend during init
f2e0eee47038 usb: dwc3: ep0: Don't reset resource alloc flag
f121531703ae usb: dwc3: pci: Drop duplicate ID
f9aa41130ac6 usb: dwc3: Properly set system wakeup
1e43c86d84fb usb: dwc3: core: Add DWC31 version 2.00a controller
f0b9c578408e usb: dwc3-am62: add workaround for Errata i2409
d78ff37567c9 usb: dwc3-am62: Fix PHY core voltage selection
4ead695e6b3c usb: dwc3-am62: Disable wakeup at remove
6661befe4100 usb: dwc3-am62: fix module unload/reload behavior
8d36c0e433e0 usb: dwc3: of-simple: Add compatible for hi3798mv200 DWC3 controller
41717b88abf1 usb: dwc3: qcom: Remove ACPI support from glue driver
a560a5672826 Merge v6.8-rc6 into usb-next
a09ebb32afbe Merge 6.8-rc5 into usb-next
a6ba1e453174 usb: dwc3: apply snps,host-vbus-glitches workaround unconditionally
b65bdf7fd605 usb: dwc3: Fix an IS_ERR() vs NULL check in dwc3_power_off_all_roothub_ports()
50c72a46eb41 usb: dwc3: gadget: Remove redundant assignment to pointer trb
b311048c174d usb: dwc3: gadget: Rewrite endpoint allocation flow
ed5551279c91 Merge 6.8-rc3 into usb-next
2d2a3349521d usb: dwc3: Add workaround for host mode VBUS glitch when boot
44481a010344 usb: dwc3-of-simple: Stop using of_reset_control_array_get() directly
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 92 ++++++++++++++-----------------
drivers/usb/dwc3/core.h | 5 ++
drivers/usb/dwc3/dwc3-am62.c | 42 ++++++++++----
drivers/usb/dwc3/ep0.c | 4 +-
drivers/usb/dwc3/gadget.c | 103 +++++++++++++++++------------------
drivers/usb/dwc3/gadget.h | 1 +
6 files changed, 130 insertions(+), 117 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3e55838c0001..100041320e8d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -104,6 +104,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
+void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ if (enable && !dwc->dis_u3_susphy_quirk)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (enable && !dwc->dis_u2_susphy_quirk)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+}
+
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -585,11 +606,8 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
*/
static int dwc3_phy_setup(struct dwc3 *dwc)
{
- unsigned int hw_mode;
u32 reg;
- hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
-
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
@@ -599,21 +617,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
/*
- * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
- * to '0' during coreConsultant configuration. So default value
- * will be '0' when the core is reset. Application needs to set it
- * to '1' after the core initialization is completed.
- */
- if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
-
- /*
- * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after
- * power-on reset, and it can be set after core initialization, which is
- * after device soft-reset during initialization.
+ * Above DWC_usb3.0 1.94a, it is recommended to set
+ * DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration.
+ * So default value will be '0' when the core is reset. Application
+ * needs to set it to '1' after the core initialization is completed.
+ *
+ * Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be
+ * cleared after power-on reset, and it can be set after core
+ * initialization.
*/
- if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
@@ -639,9 +652,6 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->tx_de_emphasis_quirk)
reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis);
- if (dwc->dis_u3_susphy_quirk)
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
-
if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
@@ -689,24 +699,15 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
}
/*
- * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
- * '0' during coreConsultant configuration. So default value will
- * be '0' when the core is reset. Application needs to set it to
- * '1' after the core initialization is completed.
- */
- if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
-
- /*
- * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after
- * power-on reset, and it can be set after core initialization, which is
- * after device soft-reset during initialization.
+ * Above DWC_usb3.0 1.94a, it is recommended to set
+ * DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration.
+ * So default value will be '0' when the core is reset. Application
+ * needs to set it to '1' after the core initialization is completed.
+ *
+ * Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared
+ * after power-on reset, and it can be set after core initialization.
*/
- if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-
- if (dwc->dis_u2_susphy_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
@@ -1227,21 +1228,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err_exit_phy;
- if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
- !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
- if (!dwc->dis_u3_susphy_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
- }
-
- if (!dwc->dis_u2_susphy_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
- }
- }
-
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
@@ -1519,6 +1505,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
else
dwc->sysdev = dwc->dev;
+ dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
+
ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
if (ret >= 0) {
dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e120611a5174..180dd8d29287 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -755,6 +755,7 @@ struct dwc3_ep {
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13)
+#define DWC3_EP_RESOURCE_ALLOCATED BIT(14)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -1132,6 +1133,7 @@ struct dwc3_scratchpad_array {
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @dis_split_quirk: set to disable split boundary.
+ * @sys_wakeup: set if the device may do system wakeup.
* @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2.
* @imod_interval: set the interrupt moderation interval in 250ns
@@ -1257,6 +1259,7 @@ struct dwc3 {
#define DWC31_REVISION_170A 0x3137302a
#define DWC31_REVISION_180A 0x3138302a
#define DWC31_REVISION_190A 0x3139302a
+#define DWC31_REVISION_200A 0x3230302a
#define DWC32_REVISION_ANY 0x0
#define DWC32_REVISION_100A 0x3130302a
@@ -1355,6 +1358,7 @@ struct dwc3 {
unsigned dis_split_quirk:1;
unsigned async_callbacks:1;
+ unsigned sys_wakeup:1;
unsigned wakeup_configured:1;
unsigned suspended:1;
@@ -1576,6 +1580,7 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc);
void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
int dwc3_core_soft_reset(struct dwc3 *dwc);
+void dwc3_enable_susphy(struct dwc3 *dwc, bool enable);
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 90a587bc29b7..fad151e78fd6 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -97,9 +97,15 @@
#define USBSS_VBUS_STAT_SESSVALID BIT(2)
#define USBSS_VBUS_STAT_VBUSVALID BIT(0)
-/* Mask for PHY PLL REFCLK */
+/* USB_PHY_CTRL register bits in CTRL_MMR */
+#define PHY_CORE_VOLTAGE_MASK BIT(31)
#define PHY_PLL_REFCLK_MASK GENMASK(3, 0)
+/* USB PHY2 register offsets */
+#define USB_PHY_PLL_REG12 0x130
+#define USB_PHY_PLL_LDO_REF_EN BIT(5)
+#define USB_PHY_PLL_LDO_REF_EN_EN BIT(4)
+
#define DWC3_AM62_AUTOSUSPEND_DELAY 100
struct dwc3_am62 {
@@ -162,6 +168,13 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
am62->offset = args.args[0];
+ /* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
+ ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0);
+ if (ret) {
+ dev_err(dev, "failed to set phy core voltage\n");
+ return ret;
+ }
+
ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code);
if (ret) {
dev_err(dev, "failed to set phy pll reference clock rate\n");
@@ -176,8 +189,9 @@ static int dwc3_ti_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct dwc3_am62 *am62;
- int i, ret;
unsigned long rate;
+ void __iomem *phy;
+ int i, ret;
u32 reg;
am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
@@ -219,6 +233,17 @@ static int dwc3_ti_probe(struct platform_device *pdev)
if (ret)
return ret;
+ /* Workaround Errata i2409 */
+ phy = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "can't map PHY IOMEM resource. Won't apply i2409 fix.\n");
+ phy = NULL;
+ } else {
+ reg = readl(phy + USB_PHY_PLL_REG12);
+ reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
+ writel(reg, phy + USB_PHY_PLL_REG12);
+ }
+
/* VBUS divider select */
am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
@@ -267,21 +292,15 @@ err_pm_disable:
return ret;
}
-static int dwc3_ti_remove_core(struct device *dev, void *c)
-{
- struct platform_device *pdev = to_platform_device(dev);
-
- platform_device_unregister(pdev);
- return 0;
-}
-
static void dwc3_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3_am62 *am62 = platform_get_drvdata(pdev);
u32 reg;
- device_for_each_child(dev, NULL, dwc3_ti_remove_core);
+ pm_runtime_get_sync(dev);
+ device_init_wakeup(dev, false);
+ of_platform_depopulate(dev);
/* Clear mode valid bit */
reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
@@ -289,7 +308,6 @@ static void dwc3_ti_remove(struct platform_device *pdev)
dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
pm_runtime_put_sync(dev);
- clk_disable_unprepare(am62->usb2_refclk);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 6ae8a36f21cf..d96ffbe52039 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -226,7 +226,8 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
/* reinitialize physical ep1 */
dep = dwc->eps[1];
- dep->flags = DWC3_EP_ENABLED;
+ dep->flags &= DWC3_EP_RESOURCE_ALLOCATED;
+ dep->flags |= DWC3_EP_ENABLED;
/* stall is always issued on EP0 */
dep = dwc->eps[0];
@@ -646,6 +647,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
case USB_STATE_ADDRESS:
+ dwc3_gadget_start_config(dwc, 2);
dwc3_gadget_clear_tx_fifos(dwc);
ret = dwc3_ep0_delegate_req(dwc, ctrl);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 28f49400f3e8..f94f68f1e7d2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -519,77 +519,56 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
+ int ret;
+
+ if (dep->flags & DWC3_EP_RESOURCE_ALLOCATED)
+ return 0;
memset(¶ms, 0x00, sizeof(params));
params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
- return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
+ ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
¶ms);
+ if (ret)
+ return ret;
+
+ dep->flags |= DWC3_EP_RESOURCE_ALLOCATED;
+ return 0;
}
/**
- * dwc3_gadget_start_config - configure ep resources
- * @dep: endpoint that is being enabled
- *
- * Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's
- * completion, it will set Transfer Resource for all available endpoints.
- *
- * The assignment of transfer resources cannot perfectly follow the data book
- * due to the fact that the controller driver does not have all knowledge of the
- * configuration in advance. It is given this information piecemeal by the
- * composite gadget framework after every SET_CONFIGURATION and
- * SET_INTERFACE. Trying to follow the databook programming model in this
- * scenario can cause errors. For two reasons:
- *
- * 1) The databook says to do %DWC3_DEPCMD_DEPSTARTCFG for every
- * %USB_REQ_SET_CONFIGURATION and %USB_REQ_SET_INTERFACE (8.1.5). This is
- * incorrect in the scenario of multiple interfaces.
- *
- * 2) The databook does not mention doing more %DWC3_DEPCMD_DEPXFERCFG for new
- * endpoint on alt setting (8.1.6).
- *
- * The following simplified method is used instead:
+ * dwc3_gadget_start_config - reset endpoint resources
+ * @dwc: pointer to the DWC3 context
+ * @resource_index: DEPSTARTCFG.XferRscIdx value (must be 0 or 2)
*
- * All hardware endpoints can be assigned a transfer resource and this setting
- * will stay persistent until either a core reset or hibernation. So whenever we
- * do a %DWC3_DEPCMD_DEPSTARTCFG(0) we can go ahead and do
- * %DWC3_DEPCMD_DEPXFERCFG for every hardware endpoint as well. We are
- * guaranteed that there are as many transfer resources as endpoints.
+ * Set resource_index=0 to reset all endpoints' resources allocation. Do this as
+ * part of the power-on/soft-reset initialization.
*
- * This function is called for each endpoint when it is being enabled but is
- * triggered only when called for EP0-out, which always happens first, and which
- * should only happen in one of the above conditions.
+ * Set resource_index=2 to reset only non-control endpoints' resources. Do this
+ * on receiving the SET_CONFIGURATION request or hibernation resume.
*/
-static int dwc3_gadget_start_config(struct dwc3_ep *dep)
+int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
{
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3 *dwc;
u32 cmd;
int i;
int ret;
- if (dep->number)
- return 0;
+ if (resource_index != 0 && resource_index != 2)
+ return -EINVAL;
memset(¶ms, 0x00, sizeof(params));
cmd = DWC3_DEPCMD_DEPSTARTCFG;
- dwc = dep->dwc;
+ cmd |= DWC3_DEPCMD_PARAM(resource_index);
- ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ ret = dwc3_send_gadget_ep_cmd(dwc->eps[0], cmd, ¶ms);
if (ret)
return ret;
- for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
- struct dwc3_ep *dep = dwc->eps[i];
-
- if (!dep)
- continue;
-
- ret = dwc3_gadget_set_xfer_resource(dep);
- if (ret)
- return ret;
- }
+ /* Reset resource allocation flags */
+ for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
+ dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
return 0;
}
@@ -884,16 +863,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
ret = dwc3_gadget_resize_tx_fifos(dep);
if (ret)
return ret;
-
- ret = dwc3_gadget_start_config(dep);
- if (ret)
- return ret;
}
ret = dwc3_gadget_set_ep_config(dep, action);
if (ret)
return ret;
+ if (!(dep->flags & DWC3_EP_RESOURCE_ALLOCATED)) {
+ ret = dwc3_gadget_set_xfer_resource(dep);
+ if (ret)
+ return ret;
+ }
+
if (!(dep->flags & DWC3_EP_ENABLED)) {
struct dwc3_trb *trb_st_hw;
struct dwc3_trb *trb_link;
@@ -1047,7 +1028,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false;
dep->type = 0;
- mask = DWC3_EP_TXFIFO_RESIZED;
+ mask = DWC3_EP_TXFIFO_RESIZED | DWC3_EP_RESOURCE_ALLOCATED;
/*
* dwc3_remove_requests() can exit early if DWC3 EP delayed stop is
* set. Do not clear DEP flags, so that the end transfer command will
@@ -2913,6 +2894,12 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ ret = dwc3_gadget_start_config(dwc, 0);
+ if (ret) {
+ dev_err(dwc->dev, "failed to config endpoints\n");
+ return ret;
+ }
+
dep = dwc->eps[0];
dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
@@ -2937,6 +2924,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_ep0_out_start(dwc);
dwc3_gadget_enable_irq(dwc);
+ dwc3_enable_susphy(dwc, true);
return 0;
@@ -2968,6 +2956,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc->gadget_driver = driver;
spin_unlock_irqrestore(&dwc->lock, flags);
+ if (dwc->sys_wakeup)
+ device_wakeup_enable(dwc->sysdev);
+
return 0;
}
@@ -2983,6 +2974,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ if (dwc->sys_wakeup)
+ device_wakeup_disable(dwc->sysdev);
+
spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = NULL;
dwc->max_cfg_eps = 0;
@@ -3428,7 +3422,7 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
struct dwc3_request *req, const struct dwc3_event_depevt *event,
int status)
{
- struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
+ struct dwc3_trb *trb;
struct scatterlist *sg = req->sg;
struct scatterlist *s;
unsigned int num_queued = req->num_queued_sgs;
@@ -4664,6 +4658,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
else
dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
+ /* No system wakeup if no gadget driver bound */
+ if (dwc->sys_wakeup)
+ device_wakeup_disable(dwc->sysdev);
+
return 0;
err5:
@@ -4693,6 +4691,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
if (!dwc->gadget)
return;
+ dwc3_enable_susphy(dwc, false);
usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
usb_put_gadget(dwc->gadget);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 55a56cf67d73..d73e735e4081 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -119,6 +119,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
+int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 54/64] usb: dwc3: import from kernel v6.10
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (52 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 53/64] usb: dwc3: import from kernel v6.9 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 55/64] usb: dwc3: import from kernel v6.11 Jens Wiklander
` (13 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.9 to v6.10.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.9..v6.10
Commits imported:
2bf35ea46d0b usb: dwc3: pci: add support for the Intel Panther Lake
fc1d1a712b51 usb: dwc3: core: Workaround for CSR read timeout
7838de15bb70 usb: dwc3: core: remove lock of otg mode during gadget suspend/resume to avoid deadlock
2c92ca849fcc tracing/treewide: Remove second parameter of __assign_str()
01be965ce5ab usb: dwc3: core: Fix unused variable warning in core driver
1d26ba0944d3 usb: dwc3: Wait unconditionally after issuing EndXfer command
adeab5bfb818 Merge 6.9-rc7 into usb-next
9b780c845fb6 usb: dwc3: exynos: add support for Google Tensor gs101
3f12222a4beb usb: dwc3: core: Fix compile warning on s390 gcc in dwc3_get_phy call
a160e1202ca3 usb: dwc3: qcom: Add multiport suspend/resume support for wrapper
5df44c6f4f39 usb: dwc3: qcom: Enable wakeup for applicable ports of multiport
2bfc9916a0e4 usb: dwc3: qcom: Refactor IRQ handling in glue driver
6410c8033ba7 usb: dwc3: qcom: Add helper function to request wakeup interrupts
30a46746ca5a usb: dwc3: core: Refactor PHY logic to support Multiport Controller
89d7f9629946 usb: dwc3: core: Skip setting event buffers for host only controllers
921e109c6200 usb: dwc3: core: Access XHCI address space temporarily to read port info
0d31ea587709 Merge 6.9-rc5 into usb-next
684e9f5f97eb usb: dwc3: exynos: Use DEFINE_SIMPLE_DEV_PM_OPS for PM functions
606c096adc79 usb: dwc3: Select 2.0 or 3.0 clk base on maximum_speed
0fb782b5d5c4 usb: dwc3: pci: Don't set "linux,phy_charger_detect" property on Lenovo Yoga Tab2 1380
5bab5dc780c9 Merge 6.9-rc2 into usb-next
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 346 +++++++++++++++++++++++++++++---------
drivers/usb/dwc3/core.h | 20 ++-
drivers/usb/dwc3/gadget.c | 4 +-
3 files changed, 284 insertions(+), 86 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 100041320e8d..cb82557678dd 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -39,6 +39,7 @@
#include "io.h"
#include "debug.h"
+#include "../host/xhci-ext-caps.h"
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
@@ -144,6 +145,7 @@ static void __dwc3_set_mode(struct work_struct *work)
int ret;
u32 reg;
u32 desired_dr_role;
+ int i;
mutex_lock(&dwc->mutex);
spin_lock_irqsave(&dwc->lock, flags);
@@ -221,8 +223,12 @@ static void __dwc3_set_mode(struct work_struct *work)
} else {
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
+
if (dwc->dis_split_quirk) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE;
@@ -237,8 +243,8 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
@@ -506,6 +512,13 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)
{
struct dwc3_event_buffer *evt;
+ unsigned int hw_mode;
+
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
+ dwc->ev_buf = NULL;
+ return 0;
+ }
evt = dwc3_alloc_one_event_buffer(dwc, length);
if (IS_ERR(evt)) {
@@ -527,6 +540,9 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ if (!dwc->ev_buf)
+ return 0;
+
evt = dwc->ev_buf;
evt->lpos = 0;
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
@@ -544,6 +560,9 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ if (!dwc->ev_buf)
+ return;
+
evt = dwc->ev_buf;
evt->lpos = 0;
@@ -596,19 +615,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
return ret;
}
-/**
- * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
- * @dwc: Pointer to our controller context structure
- *
- * Returns 0 on success. The USB PHY interfaces are configured but not
- * initialized. The PHY interfaces and the PHYs get initialized together with
- * the core in dwc3_core_init.
- */
-static int dwc3_phy_setup(struct dwc3 *dwc)
+static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index));
/*
* Make sure UX_EXIT_PX is cleared as that causes issues with some
@@ -655,9 +666,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg);
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ return 0;
+}
+
+static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
/* Select the HS PHY interface */
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
@@ -669,7 +687,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
} else if (dwc->hsphy_interface &&
!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
} else {
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
@@ -727,7 +745,35 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->ulpi_ext_vbus_drv)
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+
+ return 0;
+}
+
+/**
+ * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success. The USB PHY interfaces are configured but not
+ * initialized. The PHY interfaces and the PHYs get initialized together with
+ * the core in dwc3_core_init.
+ */
+static int dwc3_phy_setup(struct dwc3 *dwc)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ ret = dwc3_ss_phy_setup(dwc, i);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ ret = dwc3_hs_phy_setup(dwc, i);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -735,23 +781,34 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
static int dwc3_phy_init(struct dwc3 *dwc)
{
int ret;
+ int i;
+ int j;
usb_phy_init(dwc->usb2_phy);
usb_phy_init(dwc->usb3_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err_shutdown_usb3_phy;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ ret = phy_init(dwc->usb2_generic_phy[i]);
+ if (ret < 0)
+ goto err_exit_usb2_phy;
+ }
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_exit_usb2_phy;
+ for (j = 0; j < dwc->num_usb3_ports; j++) {
+ ret = phy_init(dwc->usb3_generic_phy[j]);
+ if (ret < 0)
+ goto err_exit_usb3_phy;
+ }
return 0;
+err_exit_usb3_phy:
+ while (--j >= 0)
+ phy_exit(dwc->usb3_generic_phy[j]);
+
err_exit_usb2_phy:
- phy_exit(dwc->usb2_generic_phy);
-err_shutdown_usb3_phy:
+ while (--i >= 0)
+ phy_exit(dwc->usb2_generic_phy[i]);
+
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
@@ -760,8 +817,13 @@ err_shutdown_usb3_phy:
static void dwc3_phy_exit(struct dwc3 *dwc)
{
- phy_exit(dwc->usb3_generic_phy);
- phy_exit(dwc->usb2_generic_phy);
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_exit(dwc->usb3_generic_phy[i]);
+
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_exit(dwc->usb2_generic_phy[i]);
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
@@ -770,23 +832,34 @@ static void dwc3_phy_exit(struct dwc3 *dwc)
static int dwc3_phy_power_on(struct dwc3 *dwc)
{
int ret;
+ int i;
+ int j;
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
- ret = phy_power_on(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err_suspend_usb3_phy;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ ret = phy_power_on(dwc->usb2_generic_phy[i]);
+ if (ret < 0)
+ goto err_power_off_usb2_phy;
+ }
- ret = phy_power_on(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_power_off_usb2_phy;
+ for (j = 0; j < dwc->num_usb3_ports; j++) {
+ ret = phy_power_on(dwc->usb3_generic_phy[j]);
+ if (ret < 0)
+ goto err_power_off_usb3_phy;
+ }
return 0;
+err_power_off_usb3_phy:
+ while (--j >= 0)
+ phy_power_off(dwc->usb3_generic_phy[j]);
+
err_power_off_usb2_phy:
- phy_power_off(dwc->usb2_generic_phy);
-err_suspend_usb3_phy:
+ while (--i >= 0)
+ phy_power_off(dwc->usb2_generic_phy[i]);
+
usb_phy_set_suspend(dwc->usb3_phy, 1);
usb_phy_set_suspend(dwc->usb2_phy, 1);
@@ -795,8 +868,13 @@ err_suspend_usb3_phy:
static void dwc3_phy_power_off(struct dwc3 *dwc)
{
- phy_power_off(dwc->usb3_generic_phy);
- phy_power_off(dwc->usb2_generic_phy);
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_power_off(dwc->usb3_generic_phy[i]);
+
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_power_off(dwc->usb2_generic_phy[i]);
usb_phy_set_suspend(dwc->usb3_phy, 1);
usb_phy_set_suspend(dwc->usb2_phy, 1);
@@ -879,12 +957,16 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
static void dwc3_core_setup_global_control(struct dwc3 *dwc)
{
+ unsigned int power_opt;
+ unsigned int hw_mode;
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
- switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
+ switch (power_opt) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
/**
* WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
@@ -917,6 +999,20 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
break;
}
+ /*
+ * This is a workaround for STAR#4846132, which only affects
+ * DWC_usb31 version2.00a operating in host mode.
+ *
+ * There is a problem in DWC_usb31 version 2.00a operating
+ * in host mode that would cause a CSR read timeout When CSR
+ * read coincides with RAM Clock Gating Entry. By disable
+ * Clock Gating, sacrificing power consumption for normal
+ * operation.
+ */
+ if (power_opt != DWC3_GHWPARAMS1_EN_PWROPT_NO &&
+ hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && DWC3_VER_IS(DWC31, 200A))
+ reg |= DWC3_GCTL_DSBLCLKGTNG;
+
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
dev_info(dwc->dev, "Running with FPGA optimizations\n");
@@ -1306,10 +1402,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->parkmode_disable_hs_quirk)
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS;
- if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
- (dwc->maximum_speed == USB_SPEED_HIGH ||
- dwc->maximum_speed == USB_SPEED_FULL))
- reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+ if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY)) {
+ if (dwc->maximum_speed == USB_SPEED_FULL ||
+ dwc->maximum_speed == USB_SPEED_HIGH)
+ reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+ else
+ reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+ }
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@@ -1344,7 +1443,9 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
struct device_node *node = dev->of_node;
+ char phy_name[9];
int ret;
+ u8 i;
if (node) {
dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
@@ -1370,22 +1471,38 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
return dev_err_probe(dev, ret, "no usb3 phy configured\n");
}
- dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
- if (IS_ERR(dwc->usb2_generic_phy)) {
- ret = PTR_ERR(dwc->usb2_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV)
- dwc->usb2_generic_phy = NULL;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ if (dwc->num_usb2_ports == 1)
+ snprintf(phy_name, sizeof(phy_name), "usb2-phy");
else
- return dev_err_probe(dev, ret, "no usb2 phy configured\n");
+ snprintf(phy_name, sizeof(phy_name), "usb2-%u", i);
+
+ dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name);
+ if (IS_ERR(dwc->usb2_generic_phy[i])) {
+ ret = PTR_ERR(dwc->usb2_generic_phy[i]);
+ if (ret == -ENOSYS || ret == -ENODEV)
+ dwc->usb2_generic_phy[i] = NULL;
+ else
+ return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
+ phy_name);
+ }
}
- dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
- if (IS_ERR(dwc->usb3_generic_phy)) {
- ret = PTR_ERR(dwc->usb3_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV)
- dwc->usb3_generic_phy = NULL;
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ if (dwc->num_usb3_ports == 1)
+ snprintf(phy_name, sizeof(phy_name), "usb3-phy");
else
- return dev_err_probe(dev, ret, "no usb3 phy configured\n");
+ snprintf(phy_name, sizeof(phy_name), "usb3-%u", i);
+
+ dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name);
+ if (IS_ERR(dwc->usb3_generic_phy[i])) {
+ ret = PTR_ERR(dwc->usb3_generic_phy[i]);
+ if (ret == -ENOSYS || ret == -ENODEV)
+ dwc->usb3_generic_phy[i] = NULL;
+ else
+ return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
+ phy_name);
+ }
}
return 0;
@@ -1395,6 +1512,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
int ret;
+ int i;
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
@@ -1402,8 +1520,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
@@ -1414,8 +1532,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
ret = dwc3_host_init(dwc);
if (ret)
@@ -1867,10 +1987,60 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
return 0;
}
+static int dwc3_get_num_ports(struct dwc3 *dwc)
+{
+ void __iomem *base;
+ u8 major_revision;
+ u32 offset;
+ u32 val;
+
+ /*
+ * Remap xHCI address space to access XHCI ext cap regs since it is
+ * needed to get information on number of ports present.
+ */
+ base = ioremap(dwc->xhci_resources[0].start,
+ resource_size(&dwc->xhci_resources[0]));
+ if (!base)
+ return -ENOMEM;
+
+ offset = 0;
+ do {
+ offset = xhci_find_next_ext_cap(base, offset,
+ XHCI_EXT_CAPS_PROTOCOL);
+ if (!offset)
+ break;
+
+ val = readl(base + offset);
+ major_revision = XHCI_EXT_PORT_MAJOR(val);
+
+ val = readl(base + offset + 0x08);
+ if (major_revision == 0x03) {
+ dwc->num_usb3_ports += XHCI_EXT_PORT_COUNT(val);
+ } else if (major_revision <= 0x02) {
+ dwc->num_usb2_ports += XHCI_EXT_PORT_COUNT(val);
+ } else {
+ dev_warn(dwc->dev, "unrecognized port major revision %d\n",
+ major_revision);
+ }
+ } while (1);
+
+ dev_dbg(dwc->dev, "hs-ports: %u ss-ports: %u\n",
+ dwc->num_usb2_ports, dwc->num_usb3_ports);
+
+ iounmap(base);
+
+ if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS ||
+ dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS)
+ return -EINVAL;
+
+ return 0;
+}
+
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res, dwc_res;
+ unsigned int hw_mode;
void __iomem *regs;
struct dwc3 *dwc;
int ret;
@@ -1954,6 +2124,20 @@ static int dwc3_probe(struct platform_device *pdev)
goto err_disable_clks;
}
+ /*
+ * Currently only DWC3 controllers that are host-only capable
+ * can have more than one port.
+ */
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
+ ret = dwc3_get_num_ports(dwc);
+ if (ret)
+ goto err_disable_clks;
+ } else {
+ dwc->num_usb2_ports = 1;
+ dwc->num_usb3_ports = 1;
+ }
+
spin_lock_init(&dwc->lock);
mutex_init(&dwc->mutex);
@@ -2084,8 +2268,8 @@ assert_reset:
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
- unsigned long flags;
u32 reg;
+ int i;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -2104,17 +2288,21 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
/* Let controller to suspend HSPHY before PHY driver suspends */
if (dwc->dis_u2_susphy_quirk ||
dwc->dis_enblslpm_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
- DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
+ DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
/* Give some time for USB2 PHY to suspend */
usleep_range(5000, 6000);
}
- phy_pm_runtime_put_sync(dwc->usb2_generic_phy);
- phy_pm_runtime_put_sync(dwc->usb3_generic_phy);
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* do nothing during runtime_suspend */
@@ -2122,9 +2310,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
break;
if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
synchronize_irq(dwc->irq_gadget);
}
@@ -2141,9 +2327,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
{
- unsigned long flags;
int ret;
u32 reg;
+ int i;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -2163,17 +2349,21 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
break;
}
/* Restore GUSB2PHYCFG bits that were modified in suspend */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (dwc->dis_u2_susphy_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ if (dwc->dis_u2_susphy_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- if (dwc->dis_enblslpm_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ if (dwc->dis_enblslpm_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
- phy_pm_runtime_get_sync(dwc->usb2_generic_phy);
- phy_pm_runtime_get_sync(dwc->usb3_generic_phy);
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* nothing to do on runtime_resume */
@@ -2190,9 +2380,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
dwc3_otg_host_init(dwc);
} else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
}
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 180dd8d29287..3781c736c1a1 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -33,6 +33,13 @@
#include <linux/power_supply.h>
+/*
+ * DWC3 Multiport controllers support up to 15 High-Speed PHYs
+ * and 4 SuperSpeed PHYs.
+ */
+#define DWC3_USB2_MAX_PORTS 15
+#define DWC3_USB3_MAX_PORTS 4
+
#define DWC3_MSG_MAX 500
/* Global constants */
@@ -1037,8 +1044,10 @@ struct dwc3_scratchpad_array {
* @usb_psy: pointer to power supply interface.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
- * @usb2_generic_phy: pointer to USB2 PHY
- * @usb3_generic_phy: pointer to USB3 PHY
+ * @usb2_generic_phy: pointer to array of USB2 PHYs
+ * @usb3_generic_phy: pointer to array of USB3 PHYs
+ * @num_usb2_ports: number of USB2 ports
+ * @num_usb3_ports: number of USB3 ports
* @phys_ready: flag to indicate that PHYs are ready
* @ulpi: pointer to ulpi interface
* @ulpi_ready: flag to indicate that ULPI is initialized
@@ -1184,8 +1193,11 @@ struct dwc3 {
struct usb_phy *usb2_phy;
struct usb_phy *usb3_phy;
- struct phy *usb2_generic_phy;
- struct phy *usb3_generic_phy;
+ struct phy *usb2_generic_phy[DWC3_USB2_MAX_PORTS];
+ struct phy *usb3_generic_phy[DWC3_USB3_MAX_PORTS];
+
+ u8 num_usb2_ports;
+ u8 num_usb3_ports;
bool phys_ready;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f94f68f1e7d2..89fc690fdf34 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1699,7 +1699,6 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
*/
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
{
- struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
@@ -1724,8 +1723,7 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
dep->resource_index = 0;
if (!interrupt) {
- if (!DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC3, 310A))
- mdelay(1);
+ mdelay(1);
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
} else if (!ret) {
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 55/64] usb: dwc3: import from kernel v6.11
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (53 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 54/64] usb: dwc3: import from kernel v6.10 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 56/64] usb: dwc3: import from kernel v6.12 Jens Wiklander
` (12 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.10 to v6.11.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.10..v6.11
Commits imported:
9149c9b0c7e0 usb: dwc3: core: update LC timer as per USB Spec V3.2
00dcf2fa449f usb: dwc3: Avoid waking up gadget during startxfer
cd4897bfd14f usb: dwc3: st: add missing depopulate in probe error path
ddfcfeba8910 usb: dwc3: st: fix probed platform device ref count on probe error path
72fca8371f20 usb: dwc3: ep0: Don't reset resource alloc flag (including ep0)
16f2a21d9d7e usb: dwc3: xilinx: add missing depopulate in probe error path
2aa765a43817 usb: dwc3: omap: add missing depopulate in probe error path
14e497183df2 usb: dwc3: core: Prevent USB core invalid event buffer address access
04d17331ca33 Merge tag 'usb-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
d504bfa6cfd1 usb: dwc3: enable CCI support for AMD-xilinx DWC3 controller
179264157bba usb: dwc3: core: Check all ports when set phy suspend
f7697db8b1b3 Merge 6.10-rc6 into usb-next
b7ec7fd63256 usb: dwc3: Support quirk for writing high-low order
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 89 +++++++++++++++++++++++++++++++++------
drivers/usb/dwc3/core.h | 10 +++++
drivers/usb/dwc3/ep0.c | 3 +-
drivers/usb/dwc3/gadget.c | 41 ++++++++----------
4 files changed, 106 insertions(+), 37 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index cb82557678dd..9eb085f359ce 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -108,22 +108,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
{
u32 reg;
+ int i;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- if (enable && !dwc->dis_u3_susphy_quirk)
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
- else
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
+ if (enable && !dwc->dis_u3_susphy_quirk)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
+ }
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (enable && !dwc->dis_u2_susphy_quirk)
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- else
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ if (enable && !dwc->dis_u2_susphy_quirk)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
}
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
@@ -559,9 +564,17 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ u32 reg;
if (!dwc->ev_buf)
return;
+ /*
+ * Exynos platforms may not be able to access event buffer if the
+ * controller failed to halt on dwc3_core_exit().
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ if (!(reg & DWC3_DSTS_DEVCTRLHLT))
+ return;
evt = dwc->ev_buf;
@@ -599,6 +612,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
+static void dwc3_config_soc_bus(struct dwc3 *dwc)
+{
+ if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
+ reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ }
+}
+
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
{
int intf;
@@ -1338,6 +1363,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
+ dwc3_config_soc_bus(dwc);
+
ret = dwc3_phy_power_on(dwc);
if (ret)
goto err_exit_phy;
@@ -1359,6 +1386,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
+ /*
+ * STAR 9001285599: This issue affects DWC_usb3 version 3.20a
+ * only. If the PM TIMER ECM is enabled through GUCTL2[19], the
+ * link compliance test (TD7.21) may fail. If the ECN is not
+ * enabled (GUCTL2[19] = 0), the controller will use the old timer
+ * value (5us), which is still acceptable for the link compliance
+ * test. Therefore, do not enable PM TIMER ECM in 3.20a by
+ * setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
+ */
+ if (DWC3_VER_IS(DWC3, 320A)) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg &= ~DWC3_GUCTL2_LC_TIMER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
/*
* When configured in HOST mode, after issuing U3/L2 exit controller
* fails to send proper CRC checksum in CRC5 feild. Because of this
@@ -1576,6 +1618,27 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
+static void dwc3_get_software_properties(struct dwc3 *dwc)
+{
+ struct device *tmpdev;
+ u16 gsbuscfg0_reqinfo;
+ int ret;
+
+ dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+
+ /*
+ * Iterate over all parent nodes for finding swnode properties
+ * and non-DT (non-ABI) properties.
+ */
+ for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) {
+ ret = device_property_read_u16(tmpdev,
+ "snps,gsbuscfg0-reqinfo",
+ &gsbuscfg0_reqinfo);
+ if (!ret)
+ dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo;
+ }
+}
+
static void dwc3_get_properties(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@@ -2090,6 +2153,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
+ dwc3_get_software_properties(dwc);
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 3781c736c1a1..c71240e8f7c7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -194,6 +194,10 @@
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+/* Global SoC Bus Configuration Register: AHB-prot/AXI-cache/OCP-ReqInfo */
+#define DWC3_GSBUSCFG0_REQINFO(n) (((n) & 0xffff) << 16)
+#define DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED 0xffffffff
+
/* Global Debug LSP MUX Select */
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
@@ -417,6 +421,7 @@
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
+#define DWC3_GUCTL2_LC_TIMER BIT(19)
/* Global User Control Register 3 */
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
@@ -1153,6 +1158,9 @@ struct dwc3_scratchpad_array {
* @num_ep_resized: carries the current number endpoints which have had its tx
* fifo resized.
* @debug_root: root debugfs directory for this device to put its files in.
+ * @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
+ * DATWRREQINFO, and DESWRREQINFO value passed from
+ * glue driver.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1262,6 +1270,7 @@ struct dwc3 {
#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
+#define DWC3_REVISION_320A 0x5533320a
#define DWC3_REVISION_330A 0x5533330a
#define DWC31_REVISION_ANY 0x0
@@ -1380,6 +1389,7 @@ struct dwc3 {
int last_fifo_depth;
int num_ep_resized;
struct dentry *debug_root;
+ u32 gsbuscfg0_reqinfo;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index d96ffbe52039..c9533a99e47c 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -232,7 +232,8 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
/* stall is always issued on EP0 */
dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1, false);
- dep->flags = DWC3_EP_ENABLED;
+ dep->flags &= DWC3_EP_RESOURCE_ALLOCATED;
+ dep->flags |= DWC3_EP_ENABLED;
dwc->delayed_status = false;
if (!list_empty(&dep->pending_list)) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 89fc690fdf34..291bc549935b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -287,6 +287,23 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
*
* Caller should handle locking. This function will issue @cmd with given
* @params to @dep and wait for its completion.
+ *
+ * According to the programming guide, if the link state is in L1/L2/U3,
+ * then sending the Start Transfer command may not complete. The
+ * programming guide suggested to bring the link state back to ON/U0 by
+ * performing remote wakeup prior to sending the command. However, don't
+ * initiate remote wakeup when the user/function does not send wakeup
+ * request via wakeup ops. Send the command when it's allowed.
+ *
+ * Notes:
+ * For L1 link state, issuing a command requires the clearing of
+ * GUSB2PHYCFG.SUSPENDUSB2, which turns on the signal required to complete
+ * the given command (usually within 50us). This should happen within the
+ * command timeout set by driver. No additional step is needed.
+ *
+ * For L2 or U3 link state, the gadget is in USB suspend. Care should be
+ * taken when sending Start Transfer command to ensure that it's done after
+ * USB resume.
*/
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params)
@@ -327,30 +344,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
- if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- int link_state;
-
- /*
- * Initiate remote wakeup if the link state is in U3 when
- * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
- * link state is in U1/U2, no remote wakeup is needed. The Start
- * Transfer command will initiate the link recovery.
- */
- link_state = dwc3_gadget_get_link_state(dwc);
- switch (link_state) {
- case DWC3_LINK_STATE_U2:
- if (dwc->gadget->speed >= USB_SPEED_SUPER)
- break;
-
- fallthrough;
- case DWC3_LINK_STATE_U3:
- ret = __dwc3_gadget_wakeup(dwc, false);
- dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
- ret);
- break;
- }
- }
-
/*
* For some commands such as Update Transfer command, DEPCMDPARn
* registers are reserved. Since the driver often sends Update Transfer
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 56/64] usb: dwc3: import from kernel v6.12
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (54 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 55/64] usb: dwc3: import from kernel v6.11 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 57/64] usb: dwc3: import from kernel v6.13 Jens Wiklander
` (11 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.11 to v6.12.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.11..v6.12
Commits imported:
9cfb31e4c89d usb: dwc3: fix fault at system suspend if device was already runtime suspended
705e3ce37bcc usb: dwc3: core: Fix system suspend on TI AM62 platforms
c96e31252110 usb: dwc3: Wait for EndXfer completion before restoring GUSB2PHYCFG
0d410e8913f5 usb: dwc3: core: Stop processing of pending events if controller is halted
897e13a8f9a2 usb: dwc3: re-enable runtime PM after failed resume
e8afd5ace118 usb: dwc3: rtk: Clean up error code in __get_dwc3_maximum_speed()
4664b73859dc usb: dwc3: imx8mp: disable SS_CON and U3 wakeup for system sleep
a9400f1979a0 usb: dwc3: imx8mp: add 2 software managed quirk properties for host mode
f299cd11f753 Merge 6.11-rc7 into usb-next
e23593ee41fa usb: dwc3: xilinx: simplify with dev_err_probe
819c0c31a78e usb: dwc3: rtk: return directly and simplify with devm_platform_ioremap_resource
f93e96c544ca usb: dwc3: rtk: use scoped device node handling to simplify error paths
17d206a3687e usb: dwc3: qcom: simplify with devm_platform_ioremap_resource
a85f13d50925 usb: dwc3: qcom: use scoped device node handling to simplify error paths
34e8df2fb2de usb: dwc3: imx8mp: use scoped device node handling to simplify error paths
81d905b10220 usb: dwc3: imx8mp: simplify with dev_err_probe
3fdfebc58e0d usb: dwc3: imx8mp: simplify with devm_clk_get_enabled
fcc78cce2f96 usb: dwc3: st: simplify pdev->dev usage
a93c3ad6a9b2 usb: dwc3: st: simplify with dev_err_probe
bde053252c02 usb: dwc3: st: use scoped device node handling to simplify error paths
35f4a629641b Merge 6.11-rc6 into usb-next
f35860242548 usb: dwc3: Use of_property_read_variable_u32_array() to read "power"
89b7539123bf usb: dwc3: imx8mp: Switch to RUNTIME/SYSTEM_SLEEP_PM_OPS()
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 48 ++++++++++++++++++++++++++++++++-------
drivers/usb/dwc3/core.h | 7 +++---
drivers/usb/dwc3/gadget.c | 21 +++++------------
3 files changed, 49 insertions(+), 27 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9eb085f359ce..98114c2827c0 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -544,6 +544,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)
int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ u32 reg;
if (!dwc->ev_buf)
return 0;
@@ -556,8 +557,10 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
upper_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
+ /* Clear any stale event */
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
return 0;
}
@@ -584,7 +587,10 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
| DWC3_GEVNTSIZ_SIZE(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
+
+ /* Clear any stale event */
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
}
static void dwc3_core_num_eps(struct dwc3 *dwc)
@@ -2336,6 +2342,19 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
u32 reg;
int i;
+ if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
+ dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
+ DWC3_GUSB2PHYCFG_SUSPHY) ||
+ (dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) &
+ DWC3_GUSB3PIPECTL_SUSPHY);
+ /*
+ * TI AM62 platform requires SUSPHY to be
+ * enabled for system suspend to work.
+ */
+ if (!dwc->susphy_state)
+ dwc3_enable_susphy(dwc, true);
+ }
+
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
if (pm_runtime_suspended(dwc->dev))
@@ -2454,6 +2473,11 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
break;
}
+ if (!PMSG_IS_AUTO(msg)) {
+ /* restore SUSPHY state to that before system suspend. */
+ dwc3_enable_susphy(dwc, dwc->susphy_state);
+ }
+
return 0;
}
@@ -2499,7 +2523,11 @@ static int dwc3_runtime_resume(struct device *dev)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
- dwc3_gadget_process_pending_events(dwc);
+ if (dwc->pending_events) {
+ pm_runtime_put(dwc->dev);
+ dwc->pending_events = false;
+ enable_irq(dwc->irq_gadget);
+ }
break;
case DWC3_GCTL_PRTCAP_HOST:
default:
@@ -2552,7 +2580,7 @@ static int dwc3_suspend(struct device *dev)
static int dwc3_resume(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
- int ret;
+ int ret = 0;
pinctrl_pm_select_default_state(dev);
@@ -2560,14 +2588,12 @@ static int dwc3_resume(struct device *dev)
pm_runtime_set_active(dev);
ret = dwc3_resume_common(dwc, PMSG_RESUME);
- if (ret) {
+ if (ret)
pm_runtime_set_suspended(dev);
- return ret;
- }
pm_runtime_enable(dev);
- return 0;
+ return ret;
}
static void dwc3_complete(struct device *dev)
@@ -2589,6 +2615,12 @@ static void dwc3_complete(struct device *dev)
static const struct dev_pm_ops dwc3_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
.complete = dwc3_complete,
+
+ /*
+ * Runtime suspend halts the controller on disconnection. It relies on
+ * platforms with custom connection notification to start the controller
+ * again.
+ */
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
dwc3_runtime_idle)
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index c71240e8f7c7..eab81dfdcc35 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1150,6 +1150,8 @@ struct dwc3_scratchpad_array {
* @sys_wakeup: set if the device may do system wakeup.
* @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2.
+ * @susphy_state: state of DWC3_GUSB2PHYCFG_SUSPHY + DWC3_GUSB3PIPECTL_SUSPHY
+ * before PM suspend.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
* @max_cfg_eps: current max number of IN eps used across all USB configs.
@@ -1382,6 +1384,7 @@ struct dwc3 {
unsigned sys_wakeup:1;
unsigned wakeup_configured:1;
unsigned suspended:1;
+ unsigned susphy_state:1;
u16 imod_interval;
@@ -1675,7 +1678,6 @@ static inline void dwc3_otg_host_init(struct dwc3 *dwc)
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_suspend(struct dwc3 *dwc);
int dwc3_gadget_resume(struct dwc3 *dwc);
-void dwc3_gadget_process_pending_events(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
{
@@ -1687,9 +1689,6 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
return 0;
}
-static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
-{
-}
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 291bc549935b..4959c26d3b71 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -438,6 +438,10 @@ skip_status:
dwc3_gadget_ep_get_transfer_index(dep);
}
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
+ !(cmd & DWC3_DEPCMD_CMDIOC))
+ mdelay(1);
+
if (saved_config) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg |= saved_config;
@@ -1715,12 +1719,10 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
WARN_ON_ONCE(ret);
dep->resource_index = 0;
- if (!interrupt) {
- mdelay(1);
+ if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- } else if (!ret) {
+ else if (!ret)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
- }
dep->flags &= ~DWC3_EP_DELAY_STOP;
return ret;
@@ -4728,14 +4730,3 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
return dwc3_gadget_soft_connect(dwc);
}
-
-void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
-{
- if (dwc->pending_events) {
- dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf);
- dwc3_thread_interrupt(dwc->irq_gadget, dwc->ev_buf);
- pm_runtime_put(dwc->dev);
- dwc->pending_events = false;
- enable_irq(dwc->irq_gadget);
- }
-}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 57/64] usb: dwc3: import from kernel v6.13
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (55 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 56/64] usb: dwc3: import from kernel v6.12 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 58/64] usb: dwc3: import from kernel v6.14 Jens Wiklander
` (10 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.12 to v6.13.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.12..v6.13
Commits imported:
625e70ccb7bb usb: dwc3-am62: Disable autosuspend during remove
01ea6bf5cb58 usb: dwc3: gadget: fix writing NYET threshold
a48f744bef9e usb: dwc3: xilinx: make sure pipe clock is deselected in usb2 only mode
a4faee01179a usb: dwc3: imx8mp: fix software node kernel dump
6a34dfa15d6e Merge tag 'kbuild-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild
e33a6d83e178 Merge tag 'usb-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
f5313c8b418c usb: dwc3: gadget: Remove dwc3_request->needs_extra_trb
61440628a4ff usb: dwc3: gadget: Cleanup SG handling
b7fc65f5141c usb: dwc3: gadget: Fix looping of queued SG entries
02a6982b0ccf usb: dwc3: gadget: Fix checking for number of TRBs left
5d2fb074dea2 usb: dwc3: ep0: Don't clear ep0 DWC3_EP_TRANSFER_STARTED
ce25e2a8d82d usb: dwc3: core: Set force_gen1 bit for all applicable SuperSpeed ports
61eb055cd304 usb: dwc3: gadget: Add missing check for single port RAM in TxFIFO resizing logic
6ff78df5b3d0 usb: Use (of|device)_property_present() for non-boolean properties
b95d0899c8bf usb: use "prompt" instead of "bool" for choice prompts
fad16c823e66 usb: dwc3: gadget: Refine the logic for resizing Tx FIFOs
c6d9e43954bf Merge 6.12-rc4 into usb-next
64f3b5a6bc49 Merge 6.12-rc3 into usb-next
9a0749d61a9a usb: Switch back to struct platform_driver::remove()
57d7a6b93822 usb: dwc3: Correct some typos in comments
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 16 ++--
drivers/usb/dwc3/core.h | 15 ++--
drivers/usb/dwc3/dwc3-am62.c | 3 +-
drivers/usb/dwc3/ep0.c | 4 +-
drivers/usb/dwc3/gadget.c | 156 +++++++++++++++++++++--------------
5 files changed, 114 insertions(+), 80 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 98114c2827c0..f219c82e9619 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1409,7 +1409,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
/*
* When configured in HOST mode, after issuing U3/L2 exit controller
- * fails to send proper CRC checksum in CRC5 feild. Because of this
+ * fails to send proper CRC checksum in CRC5 field. Because of this
* behaviour Transaction Error is generated, resulting in reset and
* re-enumeration of usb device attached. All the termsel, xcvrsel,
* opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1
@@ -1470,9 +1470,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET &&
(DWC3_IP_IS(DWC31)) &&
dwc->maximum_speed == USB_SPEED_SUPER) {
- reg = dwc3_readl(dwc->regs, DWC3_LLUCTL);
- reg |= DWC3_LLUCTL_FORCE_GEN1;
- dwc3_writel(dwc->regs, DWC3_LLUCTL, reg);
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i));
+ reg |= DWC3_LLUCTL_FORCE_GEN1;
+ dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg);
+ }
}
return 0;
@@ -1941,7 +1945,7 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
struct extcon_dev *edev = NULL;
const char *name;
- if (device_property_read_bool(dev, "extcon"))
+ if (device_property_present(dev, "extcon"))
return extcon_get_edev_by_phandle(dev, 0);
/*
@@ -2651,7 +2655,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
- .remove_new = dwc3_remove,
+ .remove = dwc3_remove,
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index eab81dfdcc35..f11570c8ffd0 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -81,7 +81,7 @@
#define DWC3_GSNPSREV_MASK 0xffff
#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)
-/* DWC3 registers memory space boundries */
+/* DWC3 registers memory space boundaries */
#define DWC3_XHCI_REGS_START 0x0
#define DWC3_XHCI_REGS_END 0x7fff
#define DWC3_GLOBALS_REGS_START 0xc100
@@ -179,7 +179,7 @@
#define DWC3_OEVTEN 0xcc0C
#define DWC3_OSTS 0xcc10
-#define DWC3_LLUCTL 0xd024
+#define DWC3_LLUCTL(n) (0xd024 + ((n) * 0x80))
/* Bit fields */
@@ -464,6 +464,7 @@
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_NYET_THRES_MASK (0xf << 20)
#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20)
#define DWC3_DCTL_KEEP_CONNECT BIT(19)
@@ -915,6 +916,7 @@ struct dwc3_hwparams {
#define DWC3_MODE(n) ((n) & 0x7)
/* HWPARAMS1 */
+#define DWC3_SPRAM_TYPE(n) (((n) >> 23) & 1)
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
/* HWPARAMS3 */
@@ -925,6 +927,9 @@ struct dwc3_hwparams {
#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \
(DWC3_NUM_IN_EPS_MASK)) >> 18)
+/* HWPARAMS6 */
+#define DWC3_RAM0_DEPTH(n) (((n) & (0xffff0000)) >> 16)
+
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
@@ -937,18 +942,14 @@ struct dwc3_hwparams {
* @request: struct usb_request to be transferred
* @list: a list_head used for request queueing
* @dep: struct dwc3_ep owning this request
- * @sg: pointer to first incomplete sg
* @start_sg: pointer to the sg which should be queued next
* @num_pending_sgs: counter to pending sgs
- * @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining
* @status: internal dwc3 request status tracking
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
* @num_trbs: number of TRBs used by this request
- * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
- * or unaligned OUT)
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
*/
@@ -960,7 +961,6 @@ struct dwc3_request {
struct scatterlist *start_sg;
unsigned int num_pending_sgs;
- unsigned int num_queued_sgs;
unsigned int remaining;
unsigned int status;
@@ -978,7 +978,6 @@ struct dwc3_request {
unsigned int num_trbs;
- unsigned int needs_extra_trb:1;
unsigned int direction:1;
unsigned int mapped:1;
};
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index fad151e78fd6..7d43da5f2897 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -309,6 +309,7 @@ static void dwc3_ti_remove(struct platform_device *pdev)
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
pm_runtime_set_suspended(dev);
}
@@ -377,7 +378,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
static struct platform_driver dwc3_ti_driver = {
.probe = dwc3_ti_probe,
- .remove_new = dwc3_ti_remove,
+ .remove = dwc3_ti_remove,
.driver = {
.name = "dwc3-am62",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index c9533a99e47c..666ac432f52d 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -145,7 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* Unfortunately we have uncovered a limitation wrt the Data Phase.
*
* Section 9.4 says we can wait for the XferNotReady(DATA) event to
- * come before issueing Start Transfer command, but if we do, we will
+ * come before issuing Start Transfer command, but if we do, we will
* miss situations where the host starts another SETUP phase instead of
* the DATA phase. Such cases happen at least on TD.7.6 of the Link
* Layer Compliance Suite.
@@ -232,7 +232,7 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
/* stall is always issued on EP0 */
dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1, false);
- dep->flags &= DWC3_EP_RESOURCE_ALLOCATED;
+ dep->flags &= DWC3_EP_RESOURCE_ALLOCATED | DWC3_EP_TRANSFER_STARTED;
dep->flags |= DWC3_EP_ENABLED;
dwc->delayed_status = false;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 4959c26d3b71..31a654c6f15b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -197,7 +197,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
list_del(&req->list);
req->remaining = 0;
- req->needs_extra_trb = false;
req->num_trbs = 0;
if (req->request.status == -EINPROGRESS)
@@ -687,6 +686,44 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
return fifo_size;
}
+/**
+ * dwc3_gadget_calc_ram_depth - calculates the ram depth for txfifo
+ * @dwc: pointer to the DWC3 context
+ */
+static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc)
+{
+ int ram_depth;
+ int fifo_0_start;
+ bool is_single_port_ram;
+
+ /* Check supporting RAM type by HW */
+ is_single_port_ram = DWC3_SPRAM_TYPE(dwc->hwparams.hwparams1);
+
+ /*
+ * If a single port RAM is utilized, then allocate TxFIFOs from
+ * RAM0. otherwise, allocate them from RAM1.
+ */
+ ram_depth = is_single_port_ram ? DWC3_RAM0_DEPTH(dwc->hwparams.hwparams6) :
+ DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+
+ /*
+ * In a single port RAM configuration, the available RAM is shared
+ * between the RX and TX FIFOs. This means that the txfifo can begin
+ * at a non-zero address.
+ */
+ if (is_single_port_ram) {
+ u32 reg;
+
+ /* Check if TXFIFOs start at non-zero addr */
+ reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg);
+
+ ram_depth -= (fifo_0_start >> 16);
+ }
+
+ return ram_depth;
+}
+
/**
* dwc3_gadget_clear_tx_fifos - Clears txfifo allocation
* @dwc: pointer to the DWC3 context
@@ -753,7 +790,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
int fifo_0_start;
- int ram1_depth;
+ int ram_depth;
int fifo_size;
int min_depth;
int num_in_ep;
@@ -773,17 +810,32 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
return 0;
- ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+ ram_depth = dwc3_gadget_calc_ram_depth(dwc);
- if ((dep->endpoint.maxburst > 1 &&
- usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
- num_fifos = 3;
-
- if (dep->endpoint.maxburst > 6 &&
- (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
- usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31))
- num_fifos = dwc->tx_fifo_resize_max_num;
+ switch (dwc->gadget->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER:
+ if (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ num_fifos = min_t(unsigned int,
+ dep->endpoint.maxburst,
+ dwc->tx_fifo_resize_max_num);
+ break;
+ case USB_SPEED_HIGH:
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ num_fifos = min_t(unsigned int,
+ usb_endpoint_maxp_mult(dep->endpoint.desc) + 1,
+ dwc->tx_fifo_resize_max_num);
+ break;
+ }
+ fallthrough;
+ case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_bulk(dep->endpoint.desc))
+ num_fifos = 2;
+ break;
+ default:
+ break;
+ }
/* FIFO size for a single buffer */
fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
@@ -794,7 +846,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
/* Reserve at least one FIFO for the number of IN EPs */
min_depth = num_in_ep * (fifo + 1);
- remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
+ remaining = ram_depth - min_depth - dwc->last_fifo_depth;
remaining = max_t(int, 0, remaining);
/*
* We've already reserved 1 FIFO per EP, so check what we can fit in
@@ -820,9 +872,9 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
/* Check fifo size allocation doesn't exceed available RAM size. */
- if (dwc->last_fifo_depth >= ram1_depth) {
+ if (dwc->last_fifo_depth >= ram_depth) {
dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
- dwc->last_fifo_depth, ram1_depth,
+ dwc->last_fifo_depth, ram_depth,
dep->endpoint.name, fifo_size);
if (DWC3_IP_IS(DWC3))
fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
@@ -1177,11 +1229,14 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
* pending to be processed by the driver.
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
+ struct dwc3_request *req;
+
/*
- * If there is any request remained in the started_list at
- * this point, that means there is no TRB available.
+ * If there is any request remained in the started_list with
+ * active TRBs at this point, then there is no TRB available.
*/
- if (!list_empty(&dep->started_list))
+ req = next_request(&dep->started_list);
+ if (req && req->num_trbs)
return 0;
return DWC3_TRB_NUM - 1;
@@ -1384,6 +1439,7 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = req->request.length % maxp;
unsigned int num_trbs = 1;
+ bool needs_extra_trb;
if (dwc3_needs_extra_trb(dep, req))
num_trbs++;
@@ -1391,15 +1447,15 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
if (dwc3_calc_trbs_left(dep) < num_trbs)
return 0;
- req->needs_extra_trb = num_trbs > 1;
+ needs_extra_trb = num_trbs > 1;
/* Prepare a normal TRB */
if (req->direction || req->request.length)
dwc3_prepare_one_trb(dep, req, entry_length,
- req->needs_extra_trb, node, false, false);
+ needs_extra_trb, node, false, false);
/* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */
- if ((!req->direction && !req->request.length) || req->needs_extra_trb)
+ if ((!req->direction && !req->request.length) || needs_extra_trb)
dwc3_prepare_one_trb(dep, req,
req->direction ? 0 : maxp - rem,
false, 1, true, false);
@@ -1414,8 +1470,8 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
struct scatterlist *s;
int i;
unsigned int length = req->request.length;
- unsigned int remaining = req->request.num_mapped_sgs
- - req->num_queued_sgs;
+ unsigned int remaining = req->num_pending_sgs;
+ unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining;
unsigned int num_trbs = req->num_trbs;
bool needs_extra_trb = dwc3_needs_extra_trb(dep, req);
@@ -1423,7 +1479,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
* If we resume preparing the request, then get the remaining length of
* the request and resume where we left off.
*/
- for_each_sg(req->request.sg, s, req->num_queued_sgs, i)
+ for_each_sg(req->request.sg, s, num_queued_sgs, i)
length -= sg_dma_len(s);
for_each_sg(sg, s, remaining, i) {
@@ -1488,7 +1544,6 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
if (!last_sg)
req->start_sg = sg_next(s);
- req->num_queued_sgs++;
req->num_pending_sgs--;
/*
@@ -1569,9 +1624,7 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep)
if (ret)
return ret;
- req->sg = req->request.sg;
- req->start_sg = req->sg;
- req->num_queued_sgs = 0;
+ req->start_sg = req->request.sg;
req->num_pending_sgs = req->request.num_mapped_sgs;
if (req->num_pending_sgs > 0) {
@@ -3075,7 +3128,7 @@ static int dwc3_gadget_check_config(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g);
struct usb_ep *ep;
int fifo_size = 0;
- int ram1_depth;
+ int ram_depth;
int ep_num = 0;
if (!dwc->do_fifo_resize)
@@ -3098,8 +3151,8 @@ static int dwc3_gadget_check_config(struct usb_gadget *g)
fifo_size += dwc->max_cfg_eps;
/* Check if we can fit a single fifo per endpoint */
- ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
- if (fifo_size > ram1_depth)
+ ram_depth = dwc3_gadget_calc_ram_depth(dwc);
+ if (fifo_size > ram_depth)
return -ENOMEM;
return 0;
@@ -3416,20 +3469,16 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
int status)
{
struct dwc3_trb *trb;
- struct scatterlist *sg = req->sg;
- struct scatterlist *s;
- unsigned int num_queued = req->num_queued_sgs;
+ unsigned int num_completed_trbs = req->num_trbs;
unsigned int i;
int ret = 0;
- for_each_sg(sg, s, num_queued, i) {
+ for (i = 0; i < num_completed_trbs; i++) {
trb = &dep->trb_pool[dep->trb_dequeue];
- req->sg = sg_next(s);
- req->num_queued_sgs--;
-
ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req,
- trb, event, status, true);
+ trb, event, status,
+ !!(trb->ctrl & DWC3_TRB_CTRL_CHN));
if (ret)
break;
}
@@ -3437,19 +3486,9 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
return ret;
}
-static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
- struct dwc3_request *req, const struct dwc3_event_depevt *event,
- int status)
-{
- struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
-
- return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb,
- event, status, false);
-}
-
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
{
- return req->num_pending_sgs == 0 && req->num_queued_sgs == 0;
+ return req->num_pending_sgs == 0 && req->num_trbs == 0;
}
static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
@@ -3459,24 +3498,13 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
int request_status;
int ret;
- if (req->request.num_mapped_sgs)
- ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event,
- status);
- else
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
- status);
+ ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event, status);
req->request.actual = req->request.length - req->remaining;
if (!dwc3_gadget_ep_request_completed(req))
goto out;
- if (req->needs_extra_trb) {
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
- status);
- req->needs_extra_trb = false;
- }
-
/*
* The event status only reflects the status of the TRB with IOC set.
* For the requests that don't set interrupt on completion, the driver
@@ -4167,8 +4195,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
- if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
+ if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A)) {
+ reg &= ~DWC3_DCTL_NYET_THRES_MASK;
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
+ }
dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 58/64] usb: dwc3: import from kernel v6.14
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (56 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 57/64] usb: dwc3: import from kernel v6.13 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 59/64] usb: dwc3: import from kernel v6.15 Jens Wiklander
` (9 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.13 to v6.14.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.13..v6.14
Commits imported:
cc5bfc4e16fc usb: dwc3: Set SUSPENDENABLE soon after phy init
69c58deec196 usb: dwc3: gadget: Prevent irq storm when TH re-executes
d3a8c28426fc usb: dwc3: Fix timeout issue during controller enter/exit from halt state
1ed3af5a2aae usb: dwc3: Document nostream_work
66e0ea341a2a usb: dwc3: core: Defer the probe until USB power supply ready
dcfe437492e2 usb: dwc3: gadget: Reinitiate stream for all host NoStream behavior
2919c4a3d883 Merge 6.13-rc7 into usb-next
7d3934884bab usb: dwc3: omap: Fix devm_regulator_get_optional() error handling
e84a7da8c5d6 usb: dwc3: st: Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr()
a266462b937b usb: dwc3-am62: Fix an OF node leak in phy_syscon_pll_refclk()
e3a9bd247cdd usb: dwc3: Skip resume if pm_runtime_set_active() fails
533561a8aad5 usb: dwc3: omap: Use devm_regulator_get_optional()
1ff24d40b3c3 usb: dwc3: gadget: Fix incorrect UDC state after manual deconfiguration
31d500c2d0d4 usb: dwc3: dwc3-am62: Re-initialize controller if lost power in PM suspend
362a7993ed01 Merge 6.13-rc3 into usb-next
04d5b4c23f3b usb: dwc3: core: Disable USB2 retry for DWC_usb31 1.80a and prior
686d4a2c26b4 usb: dwc3: remove unused sg struct member
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 140 ++++++++++++++++++++-----------
drivers/usb/dwc3/core.h | 8 +-
drivers/usb/dwc3/dwc3-am62.c | 83 +++++++++++++------
drivers/usb/dwc3/gadget.c | 155 ++++++++++++++++++++++-------------
4 files changed, 254 insertions(+), 132 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f219c82e9619..66a08b527165 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -131,11 +131,24 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
}
}
-void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
{
+ unsigned int hw_mode;
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+
+ /*
+ * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
+ * GUSB2PHYCFG.SUSPHY should be cleared during mode switching,
+ * and they can be set after core initialization.
+ */
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !ignore_susphy) {
+ if (DWC3_GCTL_PRTCAP(reg) != mode)
+ dwc3_enable_susphy(dwc, false);
+ }
+
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -216,7 +229,7 @@ static void __dwc3_set_mode(struct work_struct *work)
spin_lock_irqsave(&dwc->lock, flags);
- dwc3_set_prtcap(dwc, desired_dr_role);
+ dwc3_set_prtcap(dwc, desired_dr_role, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -658,16 +671,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
*/
reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
- /*
- * Above DWC_usb3.0 1.94a, it is recommended to set
- * DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration.
- * So default value will be '0' when the core is reset. Application
- * needs to set it to '1' after the core initialization is completed.
- *
- * Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be
- * cleared after power-on reset, and it can be set after core
- * initialization.
- */
+ /* Ensure the GUSB3PIPECTL.SUSPENDENABLE is cleared prior to phy init. */
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
if (dwc->u2ss_inp3_quirk)
@@ -747,15 +751,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
break;
}
- /*
- * Above DWC_usb3.0 1.94a, it is recommended to set
- * DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration.
- * So default value will be '0' when the core is reset. Application
- * needs to set it to '1' after the core initialization is completed.
- *
- * Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared
- * after power-on reset, and it can be set after core initialization.
- */
+ /* Ensure the GUSB2PHYCFG.SUSPHY is cleared prior to phy init. */
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_enblslpm_quirk)
@@ -830,6 +826,25 @@ static int dwc3_phy_init(struct dwc3 *dwc)
goto err_exit_usb3_phy;
}
+ /*
+ * Above DWC_usb3.0 1.94a, it is recommended to set
+ * DWC3_GUSB3PIPECTL_SUSPHY and DWC3_GUSB2PHYCFG_SUSPHY to '0' during
+ * coreConsultant configuration. So default value will be '0' when the
+ * core is reset. Application needs to set it to '1' after the core
+ * initialization is completed.
+ *
+ * Certain phy requires to be in P0 power state during initialization.
+ * Make sure GUSB3PIPECTL.SUSPENDENABLE and GUSB2PHYCFG.SUSPHY are clear
+ * prior to phy init to maintain in the P0 state.
+ *
+ * After phy initialization, some phy operations can only be executed
+ * while in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and
+ * GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid
+ * blocking phy ops.
+ */
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
+ dwc3_enable_susphy(dwc, true);
+
return 0;
err_exit_usb3_phy:
@@ -1479,6 +1494,26 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
}
+ /*
+ * STAR 9001346572: This issue affects DWC_usb31 versions 1.80a and
+ * prior. When an active endpoint not currently cached in the host
+ * controller is chosen to be cached to the same index as an endpoint
+ * receiving NAKs, the endpoint receiving NAKs enters continuous
+ * retry mode. This prevents it from being evicted from the host
+ * controller cache, blocking the new endpoint from being cached and
+ * serviced.
+ *
+ * To resolve this, for controller versions 1.70a and 1.80a, set the
+ * GUCTL3 bit[16] (USB2.0 Internal Retry Disable) to 1. This bit
+ * disables the USB2.0 internal retry feature. The GUCTL3[16] register
+ * function is available only from version 1.70a.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
+ dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ }
+
return 0;
err_power_off_phy:
@@ -1568,7 +1603,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false);
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
@@ -1580,7 +1615,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
return dev_err_probe(dev, ret, "failed to initialize gadget\n");
break;
case USB_DR_MODE_HOST:
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false);
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
@@ -1625,7 +1660,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
}
/* de-assert DRVVBUS for HOST and OTG mode */
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
}
static void dwc3_get_software_properties(struct dwc3 *dwc)
@@ -1664,8 +1699,6 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 tx_thr_num_pkt_prd = 0;
u8 tx_max_burst_prd = 0;
u8 tx_fifo_resize_max_num;
- const char *usb_psy_name;
- int ret;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xf;
@@ -1700,13 +1733,6 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
- ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
- if (ret >= 0) {
- dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
- if (!dwc->usb_psy)
- dev_err(dev, "couldn't get usb power supply\n");
- }
-
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
device_property_read_u8(dev, "snps,lpm-nyet-threshold",
@@ -1824,8 +1850,6 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
dwc->tx_max_burst_prd = tx_max_burst_prd;
- dwc->imod_interval = 0;
-
dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
}
@@ -1843,21 +1867,19 @@ static void dwc3_check_params(struct dwc3 *dwc)
unsigned int hwparam_gen =
DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
- /* Check for proper value of imod_interval */
- if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
- dev_warn(dwc->dev, "Interrupt moderation not supported\n");
- dwc->imod_interval = 0;
- }
-
/*
+ * Enable IMOD for all supporting controllers.
+ *
+ * Particularly, DWC_usb3 v3.00a must enable this feature for
+ * the following reason:
+ *
* Workaround for STAR 9000961433 which affects only version
* 3.00a of the DWC_usb3 core. This prevents the controller
* interrupt from being masked while handling events. IMOD
* allows us to work around this issue. Enable it for the
* affected version.
*/
- if (!dwc->imod_interval &&
- DWC3_VER_IS(DWC3, 300A))
+ if (dwc3_has_imod((dwc)))
dwc->imod_interval = 1;
/* Check the maximum_speed parameter */
@@ -2109,6 +2131,23 @@ static int dwc3_get_num_ports(struct dwc3 *dwc)
return 0;
}
+static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
+{
+ struct power_supply *usb_psy;
+ const char *usb_psy_name;
+ int ret;
+
+ ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name);
+ if (ret < 0)
+ return NULL;
+
+ usb_psy = power_supply_get_by_name(usb_psy_name);
+ if (!usb_psy)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return usb_psy;
+}
+
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -2165,6 +2204,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_software_properties(dwc);
+ dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
+ if (IS_ERR(dwc->usb_psy))
+ return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
@@ -2425,7 +2468,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
dwc3_gadget_resume(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
@@ -2433,7 +2476,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true);
break;
}
/* Restore GUSB2PHYCFG bits that were modified in suspend */
@@ -2462,7 +2505,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;
- dwc3_set_prtcap(dwc, dwc->current_dr_role);
+ dwc3_set_prtcap(dwc, dwc->current_dr_role, true);
dwc3_otg_init(dwc);
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
@@ -2589,12 +2632,15 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ goto out;
ret = dwc3_resume_common(dwc, PMSG_RESUME);
if (ret)
pm_runtime_set_suspended(dev);
+out:
pm_runtime_enable(dev);
return ret;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f11570c8ffd0..aaa39e663f60 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -425,6 +425,7 @@
/* Global User Control Register 3 */
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
+#define DWC3_GUCTL3_USB20_RETRY_DISABLE BIT(16)
/* Device Configuration Register */
#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */
@@ -716,6 +717,7 @@ struct dwc3_event_buffer {
/**
* struct dwc3_ep - device side endpoint representation
* @endpoint: usb endpoint
+ * @nostream_work: work for handling bulk NoStream
* @cancelled_list: list of cancelled requests for this endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
@@ -742,6 +744,7 @@ struct dwc3_event_buffer {
*/
struct dwc3_ep {
struct usb_ep endpoint;
+ struct delayed_work nostream_work;
struct list_head cancelled_list;
struct list_head pending_list;
struct list_head started_list;
@@ -764,7 +767,7 @@ struct dwc3_ep {
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
-#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
+#define DWC3_EP_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13)
@@ -957,7 +960,6 @@ struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
- struct scatterlist *sg;
struct scatterlist *start_sg;
unsigned int num_pending_sgs;
@@ -1556,7 +1558,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
-void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 7d43da5f2897..c158364bc03e 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -108,6 +108,9 @@
#define DWC3_AM62_AUTOSUSPEND_DELAY 100
+#define USBSS_DEBUG_CFG_OFF 0x0
+#define USBSS_DEBUG_CFG_DISABLED 0x7
+
struct dwc3_am62 {
struct device *dev;
void __iomem *usbss;
@@ -117,6 +120,7 @@ struct dwc3_am62 {
unsigned int offset;
unsigned int vbus_divider;
u32 wakeup_stat;
+ void __iomem *phy_regs;
};
static const int dwc3_ti_rate_table[] = { /* in KHZ */
@@ -166,6 +170,7 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
if (ret)
return ret;
+ of_node_put(args.np);
am62->offset = args.args[0];
/* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
@@ -184,15 +189,47 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
return 0;
}
+static int dwc3_ti_init(struct dwc3_am62 *am62)
+{
+ int ret;
+ u32 reg;
+
+ /* Read the syscon property and set the rate code */
+ ret = phy_syscon_pll_refclk(am62);
+ if (ret)
+ return ret;
+
+ /* Workaround Errata i2409 */
+ if (am62->phy_regs) {
+ reg = readl(am62->phy_regs + USB_PHY_PLL_REG12);
+ reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
+ writel(reg, am62->phy_regs + USB_PHY_PLL_REG12);
+ }
+
+ /* VBUS divider select */
+ reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
+ if (am62->vbus_divider)
+ reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
+
+ dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
+
+ clk_prepare_enable(am62->usb2_refclk);
+
+ /* Set mode valid bit to indicate role is valid */
+ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
+ reg |= USBSS_MODE_VALID;
+ dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
+
+ return 0;
+}
+
static int dwc3_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct dwc3_am62 *am62;
unsigned long rate;
- void __iomem *phy;
int i, ret;
- u32 reg;
am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
if (!am62)
@@ -228,29 +265,17 @@ static int dwc3_ti_probe(struct platform_device *pdev)
am62->rate_code = i;
- /* Read the syscon property and set the rate code */
- ret = phy_syscon_pll_refclk(am62);
- if (ret)
- return ret;
-
- /* Workaround Errata i2409 */
- phy = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(phy)) {
+ am62->phy_regs = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(am62->phy_regs)) {
dev_err(dev, "can't map PHY IOMEM resource. Won't apply i2409 fix.\n");
- phy = NULL;
- } else {
- reg = readl(phy + USB_PHY_PLL_REG12);
- reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
- writel(reg, phy + USB_PHY_PLL_REG12);
+ am62->phy_regs = NULL;
}
- /* VBUS divider select */
am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
- reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
- if (am62->vbus_divider)
- reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
- dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
+ ret = dwc3_ti_init(am62);
+ if (ret)
+ return ret;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -258,7 +283,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
* Don't ignore its dependencies with its children
*/
pm_suspend_ignore_children(dev, false);
- clk_prepare_enable(am62->usb2_refclk);
pm_runtime_get_noresume(dev);
ret = of_platform_populate(node, NULL, NULL, dev);
@@ -267,11 +291,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- /* Set mode valid bit to indicate role is valid */
- reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
- reg |= USBSS_MODE_VALID;
- dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
-
/* Device has capability to wakeup system from sleep */
device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev);
@@ -339,6 +358,9 @@ static int dwc3_ti_suspend_common(struct device *dev)
dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
}
+ /* just to track if module resets on suspend */
+ dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_DISABLED);
+
clk_disable_unprepare(am62->usb2_refclk);
return 0;
@@ -349,7 +371,14 @@ static int dwc3_ti_resume_common(struct device *dev)
struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg;
- clk_prepare_enable(am62->usb2_refclk);
+ reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG);
+ if (reg != USBSS_DEBUG_CFG_DISABLED) {
+ /* lost power/context */
+ dwc3_ti_init(am62);
+ } else {
+ dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF);
+ clk_prepare_enable(am62->usb2_refclk);
+ }
if (device_may_wakeup(dev)) {
/* Clear wakeup config enable bits */
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 31a654c6f15b..89a4dc8ebf94 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -996,8 +996,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
/*
* All stream eps will reinitiate stream on NoStream
- * rejection until we can determine that the host can
- * prime after the first transfer.
+ * rejection.
*
* However, if the controller is capable of
* TXF_FLUSH_BYPASS, then IN direction endpoints will
@@ -2630,10 +2629,38 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
{
u32 reg;
u32 timeout = 2000;
+ u32 saved_config = 0;
if (pm_runtime_suspended(dwc->dev))
return 0;
+ /*
+ * When operating in USB 2.0 speeds (HS/FS), ensure that
+ * GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY are cleared before starting
+ * or stopping the controller. This resolves timeout issues that occur
+ * during frequent role switches between host and device modes.
+ *
+ * Save and clear these settings, then restore them after completing the
+ * controller start or stop sequence.
+ *
+ * This solution was discovered through experimentation as it is not
+ * mentioned in the dwc3 programming guide. It has been tested on an
+ * Exynos platforms.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
+ saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ }
+
+ if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
+ saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ }
+
+ if (saved_config)
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
@@ -2661,6 +2688,12 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg));
+ if (saved_config) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= saved_config;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
if (!timeout)
return -ETIMEDOUT;
@@ -2740,6 +2773,8 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
__dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+
return ret;
}
@@ -3298,6 +3333,50 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
return dwc3_alloc_trb_pool(dep);
}
+#define nostream_work_to_dep(w) (container_of(to_delayed_work(w), struct dwc3_ep, nostream_work))
+static void dwc3_nostream_work(struct work_struct *work)
+{
+ struct dwc3_ep *dep = nostream_work_to_dep(work);
+ struct dwc3 *dwc = dep->dwc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (dep->flags & DWC3_EP_STREAM_PRIMED)
+ goto out;
+
+ if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
+ (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
+ !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
+ goto out;
+ /*
+ * If the host rejects a stream due to no active stream, by the
+ * USB and xHCI spec, the endpoint will be put back to idle
+ * state. When the host is ready (buffer added/updated), it will
+ * prime the endpoint to inform the usb device controller. This
+ * triggers the device controller to issue ERDY to restart the
+ * stream. However, some hosts don't follow this and keep the
+ * endpoint in the idle state. No prime will come despite host
+ * streams are updated, and the device controller will not be
+ * triggered to generate ERDY to move the next stream data. To
+ * workaround this and maintain compatibility with various
+ * hosts, force to reinitiate the stream until the host is ready
+ * instead of waiting for the host to prime the endpoint.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
+ unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
+
+ dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
+ } else {
+ dep->flags |= DWC3_EP_DELAY_START;
+ dwc3_stop_active_transfer(dep, true, true);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+out:
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
{
struct dwc3_ep *dep;
@@ -3343,6 +3422,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
INIT_LIST_HEAD(&dep->cancelled_list);
+ INIT_DELAYED_WORK(&dep->nostream_work, dwc3_nostream_work);
dwc3_debugfs_create_endpoint_dir(dep);
@@ -3742,66 +3822,27 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
- struct dwc3 *dwc = dep->dwc;
-
if (event->status == DEPEVT_STREAMEVT_FOUND) {
- dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
- goto out;
+ cancel_delayed_work(&dep->nostream_work);
+ dep->flags |= DWC3_EP_STREAM_PRIMED;
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+ return;
}
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
switch (event->parameters) {
case DEPEVT_STREAM_PRIME:
- /*
- * If the host can properly transition the endpoint state from
- * idle to prime after a NoStream rejection, there's no need to
- * force restarting the endpoint to reinitiate the stream. To
- * simplify the check, assume the host follows the USB spec if
- * it primed the endpoint more than once.
- */
- if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
- if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
- dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
- else
- dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
- }
-
+ cancel_delayed_work(&dep->nostream_work);
+ dep->flags |= DWC3_EP_STREAM_PRIMED;
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
break;
case DEPEVT_STREAM_NOSTREAM:
- if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
- !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
- (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
- !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
- break;
-
- /*
- * If the host rejects a stream due to no active stream, by the
- * USB and xHCI spec, the endpoint will be put back to idle
- * state. When the host is ready (buffer added/updated), it will
- * prime the endpoint to inform the usb device controller. This
- * triggers the device controller to issue ERDY to restart the
- * stream. However, some hosts don't follow this and keep the
- * endpoint in the idle state. No prime will come despite host
- * streams are updated, and the device controller will not be
- * triggered to generate ERDY to move the next stream data. To
- * workaround this and maintain compatibility with various
- * hosts, force to reinitiate the stream until the host is ready
- * instead of waiting for the host to prime the endpoint.
- */
- if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
- unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
-
- dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
- } else {
- dep->flags |= DWC3_EP_DELAY_START;
- dwc3_stop_active_transfer(dep, true, true);
- return;
- }
+ dep->flags &= ~DWC3_EP_STREAM_PRIMED;
+ if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
+ queue_delayed_work(system_wq, &dep->nostream_work,
+ msecs_to_jiffies(100));
break;
}
-
-out:
- dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -4460,14 +4501,18 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length));
+ evt->flags &= ~DWC3_EVENT_PENDING;
+ /*
+ * Add an explicit write memory barrier to make sure that the update of
+ * clearing DWC3_EVENT_PENDING is observed in dwc3_check_event_buf()
+ */
+ wmb();
+
if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
}
- /* Keep the clearing of DWC3_EVENT_PENDING at the end */
- evt->flags &= ~DWC3_EVENT_PENDING;
-
return ret;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 59/64] usb: dwc3: import from kernel v6.15
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (57 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 58/64] usb: dwc3: import from kernel v6.14 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 60/64] usb: dwc3: import from kernel v6.16-rc7 Jens Wiklander
` (8 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.14 to v6.15.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.14..v6.15
Commits imported:
2372f1caeca4 usb: dwc3: gadget: Make gadget_wakeup asynchronous
63ccd26cd1f6 usb: dwc3: gadget: check that event count does not exceed event buffer length
38d6e60b6f3a usb: dwc3: xilinx: Prevent spike in reset signal
042c3dd1f2d3 usb: dwc3: Don't use %pK through printk
525b139fb403 Merge v6.14-rc6 into usb-next
811d22141369 usb: dwc3: exynos: add support for exynos7870
f8da37e46253 Merge 6.14-rc3 into usb-next
07959ad5775f USB: dwc3: Use syscon_regmap_lookup_by_phandle_args
461f24bff868 usb: dwc3: gadget: Avoid using reserved endpoints on Intel Merrifield
5425191f85fe usb: dwc3: gadget: Add support for snps,reserved-endpoints property
eafba0205426 usb: dwc3: gadget: Refactor loop to avoid NULL endpoints
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.h | 4 ++
drivers/usb/dwc3/dwc3-am62.c | 12 +---
drivers/usb/dwc3/gadget.c | 135 +++++++++++++++++++++++------------
3 files changed, 96 insertions(+), 55 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index aaa39e663f60..27eae4cf223d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1164,6 +1164,9 @@ struct dwc3_scratchpad_array {
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
* DATWRREQINFO, and DESWRREQINFO value passed from
* glue driver.
+ * @wakeup_pending_funcs: Indicates whether any interface has requested for
+ * function wakeup in bitmap format where bit position
+ * represents interface_id.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1394,6 +1397,7 @@ struct dwc3 {
int num_ep_resized;
struct dentry *debug_root;
u32 gsbuscfg0_reqinfo;
+ u32 wakeup_pending_funcs;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index c158364bc03e..9db8f3ca493d 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -153,11 +153,11 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
{
struct device *dev = am62->dev;
struct device_node *node = dev->of_node;
- struct of_phandle_args args;
struct regmap *syscon;
int ret;
- syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
+ syscon = syscon_regmap_lookup_by_phandle_args(node, "ti,syscon-phy-pll-refclk",
+ 1, &am62->offset);
if (IS_ERR(syscon)) {
dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
return PTR_ERR(syscon);
@@ -165,14 +165,6 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
am62->syscon = syscon;
- ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
- 0, &args);
- if (ret)
- return ret;
-
- of_node_put(args.np);
- am62->offset = args.args[0];
-
/* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0);
if (ret) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 89a4dc8ebf94..321361288935 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -276,8 +276,6 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
return ret;
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
-
/**
* dwc3_send_gadget_ep_cmd - issue an endpoint command
* @dep: the endpoint to which the command is going to be issued
@@ -547,6 +545,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
u32 cmd;
int i;
int ret;
@@ -563,8 +562,13 @@ int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
return ret;
/* Reset resource allocation flags */
- for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
- dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
+ for (i = resource_index; i < dwc->num_eps; i++) {
+ dep = dwc->eps[i];
+ if (!dep)
+ continue;
+
+ dep->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
+ }
return 0;
}
@@ -751,9 +755,11 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
dwc->last_fifo_depth = fifo_depth;
/* Clear existing TXFIFO for all IN eps except ep0 */
- for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
- num += 2) {
+ for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); num += 2) {
dep = dwc->eps[num];
+ if (!dep)
+ continue;
+
/* Don't change TXFRAMNUM on usb31 version */
size = DWC3_IP_IS(DWC3) ? 0 :
dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
@@ -1971,12 +1977,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return -ESHUTDOWN;
}
- if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
&req->request, req->dep->name))
return -EINVAL;
if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
- "%s: request %pK already in flight\n",
+ "%s: request %p already in flight\n",
dep->name, &req->request))
return -EINVAL;
@@ -2165,7 +2171,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
}
- dev_err(dwc->dev, "request %pK was not queued to %s\n",
+ dev_err(dwc->dev, "request %p was not queued to %s\n",
request, ep->name);
ret = -EINVAL;
out:
@@ -2351,10 +2357,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return __dwc3_gadget_get_frame(dwc);
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
- int retries;
-
int ret;
u32 reg;
@@ -2382,8 +2386,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
return -EINVAL;
}
- if (async)
- dwc3_gadget_enable_linksts_evts(dwc, true);
+ dwc3_gadget_enable_linksts_evts(dwc, true);
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
@@ -2402,27 +2405,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
/*
* Since link status change events are enabled we will receive
- * an U0 event when wakeup is successful. So bail out.
+ * an U0 event when wakeup is successful.
*/
- if (async)
- return 0;
-
- /* poll until Link State changes to ON */
- retries = 20000;
-
- while (retries--) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-
- /* in HS, means ON */
- if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
- break;
- }
-
- if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
- dev_err(dwc->dev, "failed to send remote wakeup\n");
- return -EINVAL;
- }
-
return 0;
}
@@ -2443,7 +2427,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
spin_unlock_irqrestore(&dwc->lock, flags);
return -EINVAL;
}
- ret = __dwc3_gadget_wakeup(dwc, true);
+ ret = __dwc3_gadget_wakeup(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2471,14 +2455,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
*/
link_state = dwc3_gadget_get_link_state(dwc);
if (link_state == DWC3_LINK_STATE_U3) {
- ret = __dwc3_gadget_wakeup(dwc, false);
- if (ret) {
- spin_unlock_irqrestore(&dwc->lock, flags);
- return -EINVAL;
- }
- dwc3_resume_gadget(dwc);
- dwc->suspended = false;
- dwc->link_state = DWC3_LINK_STATE_U0;
+ dwc->wakeup_pending_funcs |= BIT(intf_id);
+ ret = __dwc3_gadget_wakeup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return ret;
}
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
@@ -3429,14 +3409,53 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
return 0;
}
+static int dwc3_gadget_get_reserved_endpoints(struct dwc3 *dwc, const char *propname,
+ u8 *eps, u8 num)
+{
+ u8 count;
+ int ret;
+
+ if (!device_property_present(dwc->dev, propname))
+ return 0;
+
+ ret = device_property_count_u8(dwc->dev, propname);
+ if (ret < 0)
+ return ret;
+ count = ret;
+
+ ret = device_property_read_u8_array(dwc->dev, propname, eps, min(num, count));
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
{
+ const char *propname = "snps,reserved-endpoints";
u8 epnum;
+ u8 reserved_eps[DWC3_ENDPOINTS_NUM];
+ u8 count;
+ u8 num;
+ int ret;
INIT_LIST_HEAD(&dwc->gadget->ep_list);
+ ret = dwc3_gadget_get_reserved_endpoints(dwc, propname,
+ reserved_eps, ARRAY_SIZE(reserved_eps));
+ if (ret < 0) {
+ dev_err(dwc->dev, "failed to read %s\n", propname);
+ return ret;
+ }
+ count = ret;
+
for (epnum = 0; epnum < total; epnum++) {
- int ret;
+ for (num = 0; num < count; num++) {
+ if (epnum == reserved_eps[num])
+ break;
+ }
+ if (num < count)
+ continue;
ret = dwc3_gadget_init_endpoint(dwc, epnum);
if (ret)
@@ -3703,6 +3722,8 @@ out:
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
dep = dwc->eps[i];
+ if (!dep)
+ continue;
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -3852,6 +3873,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
u8 epnum = event->endpoint_number;
dep = dwc->eps[epnum];
+ if (!dep) {
+ dev_warn(dwc->dev, "spurious event, endpoint %u is not allocated\n", epnum);
+ return;
+ }
if (!(dep->flags & DWC3_EP_ENABLED)) {
if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
@@ -4300,6 +4325,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
{
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
unsigned int pwropt;
+ int ret;
+ int intf_id;
/*
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
@@ -4375,7 +4402,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
switch (next) {
case DWC3_LINK_STATE_U0:
- if (dwc->gadget->wakeup_armed) {
+ if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
dwc3_gadget_enable_linksts_evts(dwc, false);
dwc3_resume_gadget(dwc);
dwc->suspended = false;
@@ -4398,6 +4425,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
}
dwc->link_state = next;
+
+ /* Proceed with func wakeup if any interfaces that has requested */
+ while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) {
+ intf_id = ffs(dwc->wakeup_pending_funcs) - 1;
+ ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
+ DWC3_DGCMDPAR_DN_FUNC_WAKE |
+ DWC3_DGCMDPAR_INTF_SEL(intf_id));
+ if (ret)
+ dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id);
+
+ dwc->wakeup_pending_funcs &= ~BIT(intf_id);
+ }
}
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
@@ -4564,6 +4603,12 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (!count)
return IRQ_NONE;
+ if (count > evt->length) {
+ dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
+ count, evt->length);
+ return IRQ_NONE;
+ }
+
evt->count = count;
evt->flags |= DWC3_EVENT_PENDING;
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 60/64] usb: dwc3: import from kernel v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (58 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 59/64] usb: dwc3: import from kernel v6.15 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 61/64] usb: host: re-import xhci-ext-caps.h " Jens Wiklander
` (7 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jens Wiklander
Sync Linux kernel dwc3 changes from v6.15 to v6.16-rc7.
The following files are preserved accross the import:
Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
dwc3-layerscape.c ti_usb_phy.c
Skipping unused files:
debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
Note that this is a raw import and doesn't build.
A fixup commit at the end of the series fixes that.
List of commits: git log --oneline v6.15..v6.16-rc7
Commits imported:
ef8abc0ba49c usb: dwc3: qcom: Don't leave BCR asserted
80e083943775 usb: dwc3: gadget: Fix TRB reclaim logic for short transfers and ZLPs
630a1dec3b0e usb: dwc3: Abort suspend on soft disconnect failure
657bfcbbfa38 usb: dwc3-exynos: add support for ExynosAutov920
e33ebb133a24 usb: dwc3: qcom: Use bulk clock API and devres
ab6dc9a6c721 Merge 6.15-rc6 into usb-next
615dca38c2ea Merge 6.15-rc4 into usb-next
4c0fca65d105 usb: dwc3: qcom: Fix error handling in probe
45ebb7baf235 usb: dwc3: qcom: use modern PM macros
1881a32fe14d usb: dwc3: qcom: Transition to flattened model
2dc9f137e194 usb: dwc3: qcom: Don't rely on drvdata during probe
170940f7e685 usb: dwc3: core: Don't touch resets and clocks
613a2e655d4d usb: dwc3: core: Expose core driver as library
b958b03c82d2 usb: dwc3: qcom: Snapshot driver for backwards compatibilty
00b157f8f64b usb: dwc3: exynos: add support for Exynos2200 variant
850e634006f4 usb: dwc3: core: Avoid redundant system suspend/resume callbacks
8da7644493b4 usb: dwc3: Specify maximum number of XHCI interrupters
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/dwc3/core.c | 206 ++++++++++++++++++++++++++++----------
drivers/usb/dwc3/core.h | 2 +
drivers/usb/dwc3/gadget.c | 24 ++---
drivers/usb/dwc3/glue.h | 36 +++++++
4 files changed, 199 insertions(+), 69 deletions(-)
create mode 100644 drivers/usb/dwc3/glue.h
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 66a08b527165..8002c23a5a02 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -26,6 +26,7 @@
#include <linux/of_graph.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/devinfo.h>
#include <linux/reset.h>
#include <linux/bitfield.h>
@@ -36,6 +37,7 @@
#include "core.h"
#include "gadget.h"
+#include "glue.h"
#include "io.h"
#include "debug.h"
@@ -1699,6 +1701,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 tx_thr_num_pkt_prd = 0;
u8 tx_max_burst_prd = 0;
u8 tx_fifo_resize_max_num;
+ u16 num_hc_interrupters;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xf;
@@ -1719,6 +1722,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
*/
tx_fifo_resize_max_num = 6;
+ /* default to a single XHCI interrupter */
+ num_hc_interrupters = 1;
+
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
@@ -1765,6 +1771,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&tx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,tx-max-burst-prd",
&tx_max_burst_prd);
+ device_property_read_u16(dev, "num-hc-interrupters",
+ &num_hc_interrupters);
+ /* DWC3 core allowed to have a max of 8 interrupters */
+ if (num_hc_interrupters > 8)
+ num_hc_interrupters = 8;
+
dwc->do_fifo_resize = device_property_read_bool(dev,
"tx-fifo-resize");
if (dwc->do_fifo_resize)
@@ -1851,6 +1863,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_max_burst_prd = tx_max_burst_prd;
dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
+
+ dwc->num_hc_interrupters = num_hc_interrupters;
}
/* check whether the core supports IMOD */
@@ -2148,27 +2162,16 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
return usb_psy;
}
-static int dwc3_probe(struct platform_device *pdev)
+int dwc3_core_probe(const struct dwc3_probe_data *data)
{
- struct device *dev = &pdev->dev;
- struct resource *res, dwc_res;
+ struct dwc3 *dwc = data->dwc;
+ struct device *dev = dwc->dev;
+ struct resource dwc_res;
unsigned int hw_mode;
void __iomem *regs;
- struct dwc3 *dwc;
+ struct resource *res = data->res;
int ret;
- dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
- if (!dwc)
- return -ENOMEM;
-
- dwc->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "missing memory resource\n");
- return -ENODEV;
- }
-
dwc->xhci_resources[0].start = res->start;
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
DWC3_XHCI_REGS_END;
@@ -2208,15 +2211,17 @@ static int dwc3_probe(struct platform_device *pdev)
if (IS_ERR(dwc->usb_psy))
return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
- dwc->reset = devm_reset_control_array_get_optional_shared(dev);
- if (IS_ERR(dwc->reset)) {
- ret = PTR_ERR(dwc->reset);
- goto err_put_psy;
- }
+ if (!data->ignore_clocks_and_resets) {
+ dwc->reset = devm_reset_control_array_get_optional_shared(dev);
+ if (IS_ERR(dwc->reset)) {
+ ret = PTR_ERR(dwc->reset);
+ goto err_put_psy;
+ }
- ret = dwc3_get_clocks(dwc);
- if (ret)
- goto err_put_psy;
+ ret = dwc3_get_clocks(dwc);
+ if (ret)
+ goto err_put_psy;
+ }
ret = reset_control_deassert(dwc->reset);
if (ret)
@@ -2232,7 +2237,7 @@ static int dwc3_probe(struct platform_device *pdev)
goto err_disable_clks;
}
- platform_set_drvdata(pdev, dwc);
+ dev_set_drvdata(dev, dwc);
dwc3_cache_hwparams(dwc);
if (!dwc->sysdev_is_parent &&
@@ -2327,12 +2332,35 @@ err_put_psy:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_core_probe);
-static void dwc3_remove(struct platform_device *pdev)
+static int dwc3_probe(struct platform_device *pdev)
{
- struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct dwc3_probe_data probe_data = {};
+ struct resource *res;
+ struct dwc3 *dwc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc = devm_kzalloc(&pdev->dev, sizeof(*dwc), GFP_KERNEL);
+ if (!dwc)
+ return -ENOMEM;
- pm_runtime_get_sync(&pdev->dev);
+ dwc->dev = &pdev->dev;
+
+ probe_data.dwc = dwc;
+ probe_data.res = res;
+
+ return dwc3_core_probe(&probe_data);
+}
+
+void dwc3_core_remove(struct dwc3 *dwc)
+{
+ pm_runtime_get_sync(dwc->dev);
dwc3_core_exit_mode(dwc);
dwc3_debugfs_exit(dwc);
@@ -2340,22 +2368,28 @@ static void dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
- pm_runtime_allow(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- pm_runtime_dont_use_autosuspend(&pdev->dev);
- pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_allow(dwc->dev);
+ pm_runtime_disable(dwc->dev);
+ pm_runtime_dont_use_autosuspend(dwc->dev);
+ pm_runtime_put_noidle(dwc->dev);
/*
* HACK: Clear the driver data, which is currently accessed by parent
* glue drivers, before allowing the parent to suspend.
*/
- platform_set_drvdata(pdev, NULL);
- pm_runtime_set_suspended(&pdev->dev);
+ dev_set_drvdata(dwc->dev, NULL);
+ pm_runtime_set_suspended(dwc->dev);
dwc3_free_event_buffers(dwc);
if (dwc->usb_psy)
power_supply_put(dwc->usb_psy);
}
+EXPORT_SYMBOL_GPL(dwc3_core_remove);
+
+static void dwc3_remove(struct platform_device *pdev)
+{
+ dwc3_core_remove(platform_get_drvdata(pdev));
+}
#ifdef CONFIG_PM
static int dwc3_core_init_for_resume(struct dwc3 *dwc)
@@ -2388,6 +2422,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
u32 reg;
int i;
+ int ret;
if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
@@ -2406,7 +2441,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
case DWC3_GCTL_PRTCAP_DEVICE:
if (pm_runtime_suspended(dwc->dev))
break;
- dwc3_gadget_suspend(dwc);
+ ret = dwc3_gadget_suspend(dwc);
+ if (ret)
+ return ret;
synchronize_irq(dwc->irq_gadget);
dwc3_core_exit(dwc);
break;
@@ -2441,7 +2478,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
break;
if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- dwc3_gadget_suspend(dwc);
+ ret = dwc3_gadget_suspend(dwc);
+ if (ret)
+ return ret;
synchronize_irq(dwc->irq_gadget);
}
@@ -2544,9 +2583,8 @@ static int dwc3_runtime_checks(struct dwc3 *dwc)
return 0;
}
-static int dwc3_runtime_suspend(struct device *dev)
+int dwc3_runtime_suspend(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
if (dwc3_runtime_checks(dwc))
@@ -2558,10 +2596,11 @@ static int dwc3_runtime_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_runtime_suspend);
-static int dwc3_runtime_resume(struct device *dev)
+int dwc3_runtime_resume(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
int ret;
ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
@@ -2571,7 +2610,7 @@ static int dwc3_runtime_resume(struct device *dev)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
if (dwc->pending_events) {
- pm_runtime_put(dwc->dev);
+ pm_runtime_put(dev);
dwc->pending_events = false;
enable_irq(dwc->irq_gadget);
}
@@ -2586,10 +2625,11 @@ static int dwc3_runtime_resume(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_runtime_resume);
-static int dwc3_runtime_idle(struct device *dev)
+int dwc3_runtime_idle(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -2607,12 +2647,28 @@ static int dwc3_runtime_idle(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_runtime_idle);
+
+static int dwc3_plat_runtime_suspend(struct device *dev)
+{
+ return dwc3_runtime_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_runtime_resume(struct device *dev)
+{
+ return dwc3_runtime_resume(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
-static int dwc3_suspend(struct device *dev)
+int dwc3_pm_suspend(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
int ret;
ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
@@ -2623,10 +2679,11 @@ static int dwc3_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_pm_suspend);
-static int dwc3_resume(struct device *dev)
+int dwc3_pm_resume(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
int ret = 0;
pinctrl_pm_select_default_state(dev);
@@ -2645,10 +2702,10 @@ out:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_pm_resume);
-static void dwc3_complete(struct device *dev)
+void dwc3_pm_complete(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
@@ -2658,21 +2715,60 @@ static void dwc3_complete(struct device *dev)
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
}
}
+EXPORT_SYMBOL_GPL(dwc3_pm_complete);
+
+int dwc3_pm_prepare(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+
+ /*
+ * Indicate to the PM core that it may safely leave the device in
+ * runtime suspend if runtime-suspended already in device mode.
+ */
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE &&
+ pm_runtime_suspended(dev) &&
+ !dev_pinctrl(dev))
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dwc3_pm_prepare);
+
+static int dwc3_plat_suspend(struct device *dev)
+{
+ return dwc3_pm_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_resume(struct device *dev)
+{
+ return dwc3_pm_resume(dev_get_drvdata(dev));
+}
+
+static void dwc3_plat_complete(struct device *dev)
+{
+ dwc3_pm_complete(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_prepare(struct device *dev)
+{
+ return dwc3_pm_prepare(dev_get_drvdata(dev));
+}
#else
-#define dwc3_complete NULL
+#define dwc3_plat_complete NULL
+#define dwc3_plat_prepare NULL
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dwc3_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
- .complete = dwc3_complete,
-
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_plat_suspend, dwc3_plat_resume)
+ .complete = dwc3_plat_complete,
+ .prepare = dwc3_plat_prepare,
/*
* Runtime suspend halts the controller on disconnection. It relies on
* platforms with custom connection notification to start the controller
* again.
*/
- SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
- dwc3_runtime_idle)
+ SET_RUNTIME_PM_OPS(dwc3_plat_runtime_suspend, dwc3_plat_runtime_resume,
+ dwc3_plat_runtime_idle)
};
#ifdef CONFIG_OF
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 27eae4cf223d..d5b985fa12f4 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1083,6 +1083,7 @@ struct dwc3_scratchpad_array {
* @tx_max_burst_prd: max periodic ESS transmit burst size
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
* @clear_stall_protocol: endpoint number that requires a delayed status phase
+ * @num_hc_interrupters: number of host controller interrupters
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @softconnect: true when gadget connect is called, false when disconnect runs
@@ -1333,6 +1334,7 @@ struct dwc3 {
u8 tx_max_burst_prd;
u8 tx_fifo_resize_max_num;
u8 clear_stall_protocol;
+ u16 num_hc_interrupters;
const char *hsphy_interface;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 321361288935..74968f93d4a3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -3516,7 +3516,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
* We're going to do that here to avoid problems of HW trying
* to use bogus TRBs for transfers.
*/
- if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
+ if (trb->ctrl & DWC3_TRB_CTRL_HWO)
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
/*
@@ -4821,26 +4821,22 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
int ret;
ret = dwc3_gadget_soft_disconnect(dwc);
- if (ret)
- goto err;
-
- spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver)
- dwc3_disconnect_gadget(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return 0;
-
-err:
/*
* Attempt to reset the controller's state. Likely no
* communication can be established until the host
* performs a port reset.
*/
- if (dwc->softconnect)
+ if (ret && dwc->softconnect) {
dwc3_gadget_soft_connect(dwc);
+ return -EAGAIN;
+ }
- return ret;
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->gadget_driver)
+ dwc3_disconnect_gadget(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
}
int dwc3_gadget_resume(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h
new file mode 100644
index 000000000000..2efd00e763be
--- /dev/null
+++ b/drivers/usb/dwc3/glue.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * glue.h - DesignWare USB3 DRD glue header
+ */
+
+#ifndef __DRIVERS_USB_DWC3_GLUE_H
+#define __DRIVERS_USB_DWC3_GLUE_H
+
+#include <linux/types.h>
+#include "core.h"
+
+/**
+ * dwc3_probe_data: Initialization parameters passed to dwc3_core_probe()
+ * @dwc: Reference to dwc3 context structure
+ * @res: resource for the DWC3 core mmio region
+ * @ignore_clocks_and_resets: clocks and resets defined for the device should
+ * be ignored by the DWC3 core, as they are managed by the glue
+ */
+struct dwc3_probe_data {
+ struct dwc3 *dwc;
+ struct resource *res;
+ bool ignore_clocks_and_resets;
+};
+
+int dwc3_core_probe(const struct dwc3_probe_data *data);
+void dwc3_core_remove(struct dwc3 *dwc);
+
+int dwc3_runtime_suspend(struct dwc3 *dwc);
+int dwc3_runtime_resume(struct dwc3 *dwc);
+int dwc3_runtime_idle(struct dwc3 *dwc);
+int dwc3_pm_suspend(struct dwc3 *dwc);
+int dwc3_pm_resume(struct dwc3 *dwc);
+void dwc3_pm_complete(struct dwc3 *dwc);
+int dwc3_pm_prepare(struct dwc3 *dwc);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 61/64] usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (59 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 60/64] usb: dwc3: import from kernel v6.16-rc7 Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 62/64] usb: gadget: re-import epautoconf.c " Jens Wiklander
` (6 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jerome Forissier, Jens Wiklander
From: Jerome Forissier <jerome.forissier@linaro.org>
Import drivers/usb/host/xhci-ext-caps.h from Linux v6.16-rc7. The file
is needed by the updated DWC3 code.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/host/xhci-ext-caps.h | 158 +++++++++++++++++++++++++++++++
1 file changed, 158 insertions(+)
create mode 100644 drivers/usb/host/xhci-ext-caps.h
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
new file mode 100644
index 000000000000..fd23eda6f616
--- /dev/null
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Imported from Linux v6.16-rc7 */
+/*
+ * xHCI host controller driver
+ *
+ * Copyright (C) 2008 Intel Corp.
+ *
+ * Author: Sarah Sharp
+ * Some code borrowed from the Linux EHCI driver.
+ */
+
+/* HC should halt within 16 ms, but use 32 ms as some hosts take longer */
+#define XHCI_MAX_HALT_USEC (32 * 1000)
+/* HC not running - set to 1 when run/stop bit is cleared. */
+#define XHCI_STS_HALT (1<<0)
+
+/* HCCPARAMS offset from PCI base address */
+#define XHCI_HCC_PARAMS_OFFSET 0x10
+/* HCCPARAMS contains the first extended capability pointer */
+#define XHCI_HCC_EXT_CAPS(p) (((p)>>16)&0xffff)
+
+/* Command and Status registers offset from the Operational Registers address */
+#define XHCI_CMD_OFFSET 0x00
+#define XHCI_STS_OFFSET 0x04
+
+#define XHCI_MAX_EXT_CAPS 50
+
+/* Capability Register */
+/* bits 7:0 - how long is the Capabilities register */
+#define XHCI_HC_LENGTH(p) (((p)>>00)&0x00ff)
+
+/* Extended capability register fields */
+#define XHCI_EXT_CAPS_ID(p) (((p)>>0)&0xff)
+#define XHCI_EXT_CAPS_NEXT(p) (((p)>>8)&0xff)
+#define XHCI_EXT_CAPS_VAL(p) ((p)>>16)
+/* Extended capability IDs - ID 0 reserved */
+#define XHCI_EXT_CAPS_LEGACY 1
+#define XHCI_EXT_CAPS_PROTOCOL 2
+#define XHCI_EXT_CAPS_PM 3
+#define XHCI_EXT_CAPS_VIRT 4
+#define XHCI_EXT_CAPS_ROUTE 5
+/* IDs 6-9 reserved */
+#define XHCI_EXT_CAPS_DEBUG 10
+/* Vendor caps */
+#define XHCI_EXT_CAPS_VENDOR_INTEL 192
+#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206
+/* USB Legacy Support Capability - section 7.1.1 */
+#define XHCI_HC_BIOS_OWNED (1 << 16)
+#define XHCI_HC_OS_OWNED (1 << 24)
+
+/* USB Legacy Support Capability - section 7.1.1 */
+/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
+#define XHCI_LEGACY_SUPPORT_OFFSET (0x00)
+
+/* USB Legacy Support Control and Status Register - section 7.1.2 */
+/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
+#define XHCI_LEGACY_CONTROL_OFFSET (0x04)
+/* bits 1:3, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */
+#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17))
+#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29)
+
+/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */
+#define XHCI_L1C (1 << 16)
+
+/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */
+#define XHCI_HLC (1 << 19)
+#define XHCI_BLC (1 << 20)
+
+/* Intel SPR shadow capability */
+#define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */
+#define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */
+
+/* command register values to disable interrupts and halt the HC */
+/* start/stop HC execution - do not write unless HC is halted*/
+#define XHCI_CMD_RUN (1 << 0)
+/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */
+#define XHCI_CMD_EIE (1 << 2)
+/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */
+#define XHCI_CMD_HSEIE (1 << 3)
+/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
+#define XHCI_CMD_EWE (1 << 10)
+
+#define XHCI_IRQS (XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE)
+
+/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
+#define XHCI_STS_CNR (1 << 11)
+
+/**
+ * struct xhci_protocol_caps
+ * @revision: major revision, minor revision, capability ID,
+ * and next capability pointer.
+ * @name_string: Four ASCII characters to say which spec this xHC
+ * follows, typically "USB ".
+ * @port_info: Port offset, count, and protocol-defined information.
+ */
+struct xhci_protocol_caps {
+ u32 revision;
+ u32 name_string;
+ u32 port_info;
+};
+
+#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff)
+#define XHCI_EXT_PORT_MINOR(x) (((x) >> 16) & 0xff)
+#define XHCI_EXT_PORT_PSIC(x) (((x) >> 28) & 0x0f)
+#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff)
+#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff)
+
+#define XHCI_EXT_PORT_PSIV(x) (((x) >> 0) & 0x0f)
+#define XHCI_EXT_PORT_PSIE(x) (((x) >> 4) & 0x03)
+#define XHCI_EXT_PORT_PLT(x) (((x) >> 6) & 0x03)
+#define XHCI_EXT_PORT_PFD(x) (((x) >> 8) & 0x01)
+#define XHCI_EXT_PORT_LP(x) (((x) >> 14) & 0x03)
+#define XHCI_EXT_PORT_PSIM(x) (((x) >> 16) & 0xffff)
+
+#include <linux/io.h>
+
+/**
+ * Find the offset of the extended capabilities with capability ID id.
+ *
+ * @base PCI MMIO registers base address.
+ * @start address at which to start looking, (0 or HCC_PARAMS to start at
+ * beginning of list)
+ * @id Extended capability ID to search for, or 0 for the next
+ * capability
+ *
+ * Returns the offset of the next matching extended capability structure.
+ * Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL,
+ * and this provides a way to find them all.
+ */
+
+static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
+{
+ u32 val;
+ u32 next;
+ u32 offset;
+
+ offset = start;
+ if (!start || start == XHCI_HCC_PARAMS_OFFSET) {
+ val = readl(base + XHCI_HCC_PARAMS_OFFSET);
+ if (val == ~0)
+ return 0;
+ offset = XHCI_HCC_EXT_CAPS(val) << 2;
+ if (!offset)
+ return 0;
+ }
+ do {
+ val = readl(base + offset);
+ if (val == ~0)
+ return 0;
+ if (offset != start && (id == 0 || XHCI_EXT_CAPS_ID(val) == id))
+ return offset;
+
+ next = XHCI_EXT_CAPS_NEXT(val);
+ offset += next << 2;
+ } while (next);
+
+ return 0;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 62/64] usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (60 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 61/64] usb: host: re-import xhci-ext-caps.h " Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 63/64] usb: udc: re-import udc-core.c " Jens Wiklander
` (5 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jerome Forissier, Jens Wiklander
From: Jerome Forissier <jerome.forissier@linaro.org>
Replace drivers/usb/gadget/epautoconf.c with the one from the Linux
kernel. Fixes will come later.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/gadget/epautoconf.c | 291 +++++++++++++++-----------------
1 file changed, 132 insertions(+), 159 deletions(-)
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 495460473e9a..30016b805bfd 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -3,170 +3,120 @@
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
- *
- * Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and
- * Remy Bohmer <linux@bohmer.net>
*/
-#include <linux/usb/ch9.h>
-#include <linux/errno.h>
-#include <linux/usb/gadget.h>
-#include <asm/unaligned.h>
-
-#define isdigit(c) ('0' <= (c) && (c) <= '9')
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
-/* we must assign addresses for configurable endpoints (like net2280) */
-static unsigned epnum;
+#include <linux/ctype.h>
+#include <linux/string.h>
-/* #define MANY_ENDPOINTS */
-#ifdef MANY_ENDPOINTS
-/* more than 15 configurable endpoints */
-static unsigned in_epnum;
-#endif
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
-/*
- * This should work with endpoints from controller drivers sharing the
- * same endpoint naming convention. By example:
+/**
+ * usb_ep_autoconfig_ss() - choose an endpoint matching the ep
+ * descriptor and ep companion descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on
+ * success.
+ * @ep_comp: Endpoint companion descriptor, with the required
+ * number of streams. Will be modified when the chosen EP
+ * supports a different number of streams.
+ *
+ * This routine replaces the usb_ep_autoconfig when needed
+ * superspeed enhancments. If such enhancemnets are required,
+ * the FD should call usb_ep_autoconfig_ss directly and provide
+ * the additional ep_comp parameter.
+ *
+ * By choosing an endpoint to use with the specified descriptor,
+ * this routine simplifies writing gadget drivers that work with
+ * multiple USB device controllers. The endpoint would be
+ * passed later to usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
*
- * - ep1, ep2, ... address is fixed, not direction or type
- * - ep1in, ep2out, ... address and direction are fixed, not type
- * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
- * - ep1in-bulk, ep2out-iso, ... all three are fixed
- * - ep-* ... no functionality restrictions
+ * Be sure to examine and test the results of autoconfiguration
+ * on your hardware. This code may not make the best choices
+ * about how to use the USB controller, and it can't know all
+ * the restrictions that may apply. Some combinations of driver
+ * and hardware won't be able to autoconfigure.
*
- * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
- * Less common restrictions are implied by gadget_is_*().
+ * On success, this returns an claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed and
+ * the bmAttribute field in the ep companion descriptor is
+ * updated with the assigned number of streams if it is
+ * different from the original value. To prevent the endpoint
+ * from being returned by a later autoconfig call, claims it by
+ * assigning ep->claimed to true.
*
- * NOTE: each endpoint is unidirectional, as specified by its USB
- * descriptor; and isn't specific to a configuration or altsetting.
+ * On failure, this returns a null endpoint descriptor.
*/
-static int ep_matches(
+struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
- struct usb_ep *ep,
- struct usb_endpoint_descriptor *desc
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
)
{
- u8 type;
- const char *tmp;
- u16 max;
-
- /* endpoint already claimed? */
- if (NULL != ep->driver_data)
- return 0;
-
- /* only support ep0 for portable CONTROL traffic */
- type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
- if (USB_ENDPOINT_XFER_CONTROL == type)
- return 0;
-
- /* some other naming convention */
- if ('e' != ep->name[0])
- return 0;
+ struct usb_ep *ep;
- /* type-restriction: "-iso", "-bulk", or "-int".
- * direction-restriction: "in", "out".
- */
- if ('-' != ep->name[2]) {
- tmp = strrchr(ep->name, '-');
- if (tmp) {
- switch (type) {
- case USB_ENDPOINT_XFER_INT:
- /* bulk endpoints handle interrupt transfers,
- * except the toggle-quirky iso-synch kind
- */
- if ('s' == tmp[2]) /* == "-iso" */
- return 0;
- break;
- case USB_ENDPOINT_XFER_BULK:
- if ('b' != tmp[1]) /* != "-bulk" */
- return 0;
- break;
- case USB_ENDPOINT_XFER_ISOC:
- if ('s' != tmp[2]) /* != "-iso" */
- return 0;
- }
- } else {
- tmp = ep->name + strlen(ep->name);
- }
-
- /* direction-restriction: "..in-..", "out-.." */
- tmp--;
- if (!isdigit(*tmp)) {
- if (desc->bEndpointAddress & USB_DIR_IN) {
- if ('n' != *tmp)
- return 0;
- } else {
- if ('t' != *tmp)
- return 0;
- }
- }
+ if (gadget->ops->match_ep) {
+ ep = gadget->ops->match_ep(gadget, desc, ep_comp);
+ if (ep)
+ goto found_ep;
}
- /* endpoint maxpacket size is an input parameter, except for bulk
- * where it's an output parameter representing the full speed limit.
- * the usb spec fixes high speed bulk maxpacket at 512 bytes.
- */
- max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
- switch (type) {
- case USB_ENDPOINT_XFER_INT:
- /* INT: limit 64 bytes full speed, 1024 high speed */
- if (!gadget->is_dualspeed && max > 64)
- return 0;
- /* FALLTHROUGH */
-
- case USB_ENDPOINT_XFER_ISOC:
- /* ISO: limit 1023 bytes full speed, 1024 high speed */
- if (ep->maxpacket < max)
- return 0;
- if (!gadget->is_dualspeed && max > 1023)
- return 0;
-
- /* BOTH: "high bandwidth" works only at high speed */
- if ((get_unaligned(&desc->wMaxPacketSize) &
- __constant_cpu_to_le16(3<<11))) {
- if (!gadget->is_dualspeed)
- return 0;
- /* configure your hardware with enough buffering!! */
- }
- break;
+ /* Second, look at endpoints until an unclaimed one looks usable */
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp))
+ goto found_ep;
}
- /* MATCH!! */
+ /* Fail */
+ return NULL;
+found_ep:
+
+ /*
+ * If the protocol driver hasn't yet decided on wMaxPacketSize
+ * and wants to know the maximum possible, provide the info.
+ */
+ if (desc->wMaxPacketSize == 0)
+ desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);
/* report address */
+ desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit(ep->name[2])) {
- u8 num = dectoul(&ep->name[2], NULL);
+ u8 num = simple_strtoul(&ep->name[2], NULL, 10);
desc->bEndpointAddress |= num;
-#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
- if (++in_epnum > 15)
- return 0;
- desc->bEndpointAddress = USB_DIR_IN | in_epnum;
-#endif
+ if (++gadget->in_epnum > 15)
+ return NULL;
+ desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
} else {
- if (++epnum > 15)
- return 0;
- desc->bEndpointAddress |= epnum;
+ if (++gadget->out_epnum > 15)
+ return NULL;
+ desc->bEndpointAddress |= gadget->out_epnum;
}
- /* report (variable) full speed bulk maxpacket */
- if (USB_ENDPOINT_XFER_BULK == type) {
- int size = ep->maxpacket;
-
- /* min() doesn't work on bitfields with gcc-3.5 */
- if (size > 64)
- size = 64;
- put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
- }
-
- if (gadget->ops->ep_conf)
- return gadget->ops->ep_conf(gadget, ep, desc);
-
- return 1;
+ ep->address = desc->bEndpointAddress;
+ ep->desc = NULL;
+ ep->comp_desc = NULL;
+ ep->claimed = true;
+ return ep;
}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
/**
- * usb_ep_autoconfig - choose an endpoint matching the descriptor
+ * usb_ep_autoconfig() - choose an endpoint matching the
+ * descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
@@ -187,11 +137,12 @@ static int ep_matches(
* USB controller, and it can't know all the restrictions that may apply.
* Some combinations of driver and hardware won't be able to autoconfigure.
*
- * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * On success, this returns an claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
- * is initialized as if the endpoint were used at full speed. To prevent
- * the endpoint from being returned by a later autoconfig call, claim it
- * by assigning ep->driver_data to some non-null value.
+ * is initialized as if the endpoint were used at full speed. Because of
+ * that the users must consider adjusting the autoconfigured descriptor.
+ * To prevent the endpoint from being returned by a later autoconfig call,
+ * claims it by assigning ep->claimed to true.
*
* On failure, this returns a null endpoint descriptor.
*/
@@ -200,23 +151,45 @@ struct usb_ep *usb_ep_autoconfig(
struct usb_endpoint_descriptor *desc
)
{
- struct usb_ep *ep;
+ struct usb_ep *ep;
+ u8 type;
- if (gadget->ops->match_ep) {
- ep = gadget->ops->match_ep(gadget, desc, NULL);
- if (ep && ep_matches(gadget, ep, desc))
- return ep;
- }
+ ep = usb_ep_autoconfig_ss(gadget, desc, NULL);
+ if (!ep)
+ return NULL;
- /* Second, look at endpoints until an unclaimed one looks usable */
- list_for_each_entry(ep, &gadget->ep_list, ep_list) {
- if (ep_matches(gadget, ep, desc))
- return ep;
+ type = usb_endpoint_type(desc);
+
+ /* report (variable) full speed bulk maxpacket */
+ if (type == USB_ENDPOINT_XFER_BULK) {
+ int size = ep->maxpacket_limit;
+
+ /* min() doesn't work on bitfields with gcc-3.5 */
+ if (size > 64)
+ size = 64;
+ desc->wMaxPacketSize = cpu_to_le16(size);
}
- /* Fail */
- return NULL;
+ return ep;
+}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
+
+/**
+ * usb_ep_autoconfig_release - releases endpoint and set it to initial state
+ * @ep: endpoint which should be released
+ *
+ * This function can be used during function bind for endpoints obtained
+ * from usb_ep_autoconfig(). It unclaims endpoint claimed by
+ * usb_ep_autoconfig() to make it available for other functions. Endpoint
+ * which was released is no longer valid and shouldn't be used in
+ * context of function which released it.
+ */
+void usb_ep_autoconfig_release(struct usb_ep *ep)
+{
+ ep->claimed = false;
+ ep->driver_data = NULL;
}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_release);
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
@@ -224,18 +197,18 @@ struct usb_ep *usb_ep_autoconfig(
*
* Use this for devices where one configuration may need to assign
* endpoint resources very differently from the next one. It clears
- * state such as ep->driver_data and the record of assigned endpoints
+ * state such as ep->claimed and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
-void usb_ep_autoconfig_reset(struct usb_gadget *gadget)
+void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
{
struct usb_ep *ep;
- list_for_each_entry(ep, &gadget->ep_list, ep_list) {
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ ep->claimed = false;
ep->driver_data = NULL;
}
-#ifdef MANY_ENDPOINTS
- in_epnum = 0;
-#endif
- epnum = 0;
+ gadget->in_epnum = 0;
+ gadget->out_epnum = 0;
}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 63/64] usb: udc: re-import udc-core.c from kernel v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (61 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 62/64] usb: gadget: re-import epautoconf.c " Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 64/64] usb: fix build after resync of DWC3 with " Jens Wiklander
` (4 subsequent siblings)
67 siblings, 0 replies; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jerome Forissier, Jens Wiklander
From: Jerome Forissier <jerome.forissier@linaro.org>
Delete drivers/usb/gadget/udc/udc-core.c which was imported from kernel
v3.19-rc1 and replace it with drivers/usb/gadget/udc/core.c from kernel
v6.16-rc7. Keep the name udc-core.c for now.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/gadget/udc/udc-core.c | 1889 ++++++++++++++++++++++++++---
1 file changed, 1715 insertions(+), 174 deletions(-)
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index 6bb419ae2abc..d709e24c1fd4 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -1,35 +1,51 @@
// SPDX-License-Identifier: GPL-2.0
-/**
- * udc-core.c - Core UDC Framework
- *
- * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
+/*
+ * udc.c - Core UDC Framework
*
+ * Copyright (C) 2010 Texas Instruments
* Author: Felipe Balbi <balbi@ti.com>
- *
- * Taken from Linux Kernel v3.19-rc1 (drivers/usb/gadget/udc-core.c) and ported
- * to uboot.
- *
- * commit 02e8c96627 : usb: gadget: udc: core: prepend udc_attach_driver with
- * usb_
*/
-#include <dm/device_compat.h>
-#include <dm/devres.h>
-#include <linux/compat.h>
-#include <malloc.h>
-#include <asm/cache.h>
+#define pr_fmt(fmt) "UDC core: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+#include <linux/err.h>
#include <linux/dma-mapping.h>
-#include <dm.h>
-#include <dm/device-internal.h>
+#include <linux/sched/task_stack.h>
+#include <linux/workqueue.h>
+
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+
+#include "trace.h"
+
+static DEFINE_IDA(gadget_id_numbers);
+
+static const struct bus_type gadget_bus_type;
/**
* struct usb_udc - describes one usb device controller
- * @driver - the gadget driver pointer. For use by the class code
- * @dev - the child device to the actual controller
- * @gadget - the gadget. For use by the class code
- * @list - for use by the udc class driver
+ * @driver: the gadget driver pointer. For use by the class code
+ * @dev: the child device to the actual controller
+ * @gadget: the gadget. For use by the class code
+ * @list: for use by the udc class driver
+ * @vbus: for udcs who care about vbus status, this value is real vbus status;
+ * for udcs who do not care about vbus status, this value is always true
+ * @started: the UDC's started state. True if the UDC had started.
+ * @allow_connect: Indicates whether UDC is allowed to be pulled up.
+ * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
+ * unbound.
+ * @vbus_work: work routine to handle VBUS status change notifications.
+ * @connect_lock: protects udc->started, gadget->connect,
+ * gadget->allow_connect and gadget->deactivate. The routines
+ * usb_gadget_connect_locked(), usb_gadget_disconnect_locked(),
+ * usb_udc_connect_control_locked(), usb_gadget_udc_start_locked() and
+ * usb_gadget_udc_stop_locked() are called with this lock held.
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
@@ -39,43 +55,935 @@ struct usb_udc {
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
+ bool vbus;
+ bool started;
+ bool allow_connect;
+ struct work_struct vbus_work;
+ struct mutex connect_lock;
};
-static struct class *udc_class;
+static const struct class udc_class;
static LIST_HEAD(udc_list);
-DEFINE_MUTEX(udc_lock);
+
+/* Protects udc_list, udc->driver, driver->is_bound, and related calls */
+static DEFINE_MUTEX(udc_lock);
/* ------------------------------------------------------------------------- */
-int usb_gadget_map_request(struct usb_gadget *gadget,
+/**
+ * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint
+ * @ep:the endpoint being configured
+ * @maxpacket_limit:value of maximum packet size limit
+ *
+ * This function should be used only in UDC drivers to initialize endpoint
+ * (usually in probe function).
+ */
+void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
+ unsigned maxpacket_limit)
+{
+ ep->maxpacket_limit = maxpacket_limit;
+ ep->maxpacket = maxpacket_limit;
+
+ trace_usb_ep_set_maxpacket_limit(ep, 0);
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
+
+/**
+ * usb_ep_enable - configure endpoint, making it usable
+ * @ep:the endpoint being configured. may not be the endpoint named "ep0".
+ * drivers discover endpoints through the ep_list of a usb_gadget.
+ *
+ * When configurations are set, or when interface settings change, the driver
+ * will enable or disable the relevant endpoints. while it is enabled, an
+ * endpoint may be used for i/o until the driver receives a disconnect() from
+ * the host or until the endpoint is disabled.
+ *
+ * the ep0 implementation (which calls this routine) must ensure that the
+ * hardware capabilities of each endpoint match the descriptor provided
+ * for it. for example, an endpoint named "ep2in-bulk" would be usable
+ * for interrupt transfers as well as bulk, but it likely couldn't be used
+ * for iso transfers or for endpoint 14. some endpoints are fully
+ * configurable, with more generic names like "ep-a". (remember that for
+ * USB, "in" means "towards the USB host".)
+ *
+ * This routine may be called in an atomic (interrupt) context.
+ *
+ * returns zero, or a negative error code.
+ */
+int usb_ep_enable(struct usb_ep *ep)
+{
+ int ret = 0;
+
+ if (ep->enabled)
+ goto out;
+
+ /* UDC drivers can't handle endpoints with maxpacket size 0 */
+ if (!ep->desc || usb_endpoint_maxp(ep->desc) == 0) {
+ WARN_ONCE(1, "%s: ep%d (%s) has %s\n", __func__, ep->address, ep->name,
+ (!ep->desc) ? "NULL descriptor" : "maxpacket 0");
+
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ep->ops->enable(ep, ep->desc);
+ if (ret)
+ goto out;
+
+ ep->enabled = true;
+
+out:
+ trace_usb_ep_enable(ep, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_enable);
+
+/**
+ * usb_ep_disable - endpoint is no longer usable
+ * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0".
+ *
+ * no other task may be using this endpoint when this is called.
+ * any pending and uncompleted requests will complete with status
+ * indicating disconnect (-ESHUTDOWN) before this call returns.
+ * gadget drivers must call usb_ep_enable() again before queueing
+ * requests to the endpoint.
+ *
+ * This routine may be called in an atomic (interrupt) context.
+ *
+ * returns zero, or a negative error code.
+ */
+int usb_ep_disable(struct usb_ep *ep)
+{
+ int ret = 0;
+
+ if (!ep->enabled)
+ goto out;
+
+ ret = ep->ops->disable(ep);
+ if (ret)
+ goto out;
+
+ ep->enabled = false;
+
+out:
+ trace_usb_ep_disable(ep, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_disable);
+
+/**
+ * usb_ep_alloc_request - allocate a request object to use with this endpoint
+ * @ep:the endpoint to be used with with the request
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Request objects must be allocated with this call, since they normally
+ * need controller-specific setup and may even need endpoint-specific
+ * resources such as allocation of DMA descriptors.
+ * Requests may be submitted with usb_ep_queue(), and receive a single
+ * completion callback. Free requests with usb_ep_free_request(), when
+ * they are no longer needed.
+ *
+ * Returns the request, or null if one could not be allocated.
+ */
+struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct usb_request *req = NULL;
+
+ req = ep->ops->alloc_request(ep, gfp_flags);
+
+ trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM);
+
+ return req;
+}
+EXPORT_SYMBOL_GPL(usb_ep_alloc_request);
+
+/**
+ * usb_ep_free_request - frees a request object
+ * @ep:the endpoint associated with the request
+ * @req:the request being freed
+ *
+ * Reverses the effect of usb_ep_alloc_request().
+ * Caller guarantees the request is not queued, and that it will
+ * no longer be requeued (or otherwise used).
+ */
+void usb_ep_free_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ trace_usb_ep_free_request(ep, req, 0);
+ ep->ops->free_request(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_ep_free_request);
+
+/**
+ * usb_ep_queue - queues (submits) an I/O request to an endpoint.
+ * @ep:the endpoint associated with the request
+ * @req:the request being submitted
+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
+ * pre-allocate all necessary memory with the request.
+ *
+ * This tells the device controller to perform the specified request through
+ * that endpoint (reading or writing a buffer). When the request completes,
+ * including being canceled by usb_ep_dequeue(), the request's completion
+ * routine is called to return the request to the driver. Any endpoint
+ * (except control endpoints like ep0) may have more than one transfer
+ * request queued; they complete in FIFO order. Once a gadget driver
+ * submits a request, that request may not be examined or modified until it
+ * is given back to that driver through the completion callback.
+ *
+ * Each request is turned into one or more packets. The controller driver
+ * never merges adjacent requests into the same packet. OUT transfers
+ * will sometimes use data that's already buffered in the hardware.
+ * Drivers can rely on the fact that the first byte of the request's buffer
+ * always corresponds to the first byte of some USB packet, for both
+ * IN and OUT transfers.
+ *
+ * Bulk endpoints can queue any amount of data; the transfer is packetized
+ * automatically. The last packet will be short if the request doesn't fill it
+ * out completely. Zero length packets (ZLPs) should be avoided in portable
+ * protocols since not all usb hardware can successfully handle zero length
+ * packets. (ZLPs may be explicitly written, and may be implicitly written if
+ * the request 'zero' flag is set.) Bulk endpoints may also be used
+ * for interrupt transfers; but the reverse is not true, and some endpoints
+ * won't support every interrupt transfer. (Such as 768 byte packets.)
+ *
+ * Interrupt-only endpoints are less functional than bulk endpoints, for
+ * example by not supporting queueing or not handling buffers that are
+ * larger than the endpoint's maxpacket size. They may also treat data
+ * toggle differently.
+ *
+ * Control endpoints ... after getting a setup() callback, the driver queues
+ * one response (even if it would be zero length). That enables the
+ * status ack, after transferring data as specified in the response. Setup
+ * functions may return negative error codes to generate protocol stalls.
+ * (Note that some USB device controllers disallow protocol stall responses
+ * in some cases.) When control responses are deferred (the response is
+ * written after the setup callback returns), then usb_ep_set_halt() may be
+ * used on ep0 to trigger protocol stalls. Depending on the controller,
+ * it may not be possible to trigger a status-stage protocol stall when the
+ * data stage is over, that is, from within the response's completion
+ * routine.
+ *
+ * For periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+ * have queued some data to transfer at that time.
+ *
+ * Note that @req's ->complete() callback must never be called from
+ * within usb_ep_queue() as that can create deadlock situations.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code. Endpoints that are not enabled
+ * report errors; errors will also be
+ * reported when the usb peripheral is disconnected.
+ *
+ * If and only if @req is successfully queued (the return value is zero),
+ * @req->complete() will be called exactly once, when the Gadget core and
+ * UDC are finished with the request. When the completion function is called,
+ * control of the request is returned to the device driver which submitted it.
+ * The completion handler may then immediately free or reuse @req.
+ */
+int usb_ep_queue(struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ int ret = 0;
+
+ if (!ep->enabled && ep->address) {
+ pr_debug("USB gadget: queue request to disabled ep 0x%x (%s)\n",
+ ep->address, ep->name);
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ ret = ep->ops->queue(ep, req, gfp_flags);
+
+out:
+ trace_usb_ep_queue(ep, req, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_queue);
+
+/**
+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
+ * @ep:the endpoint associated with the request
+ * @req:the request being canceled
+ *
+ * If the request is still active on the endpoint, it is dequeued and
+ * eventually its completion routine is called (with status -ECONNRESET);
+ * else a negative error code is returned. This routine is asynchronous,
+ * that is, it may return before the completion routine runs.
+ *
+ * Note that some hardware can't clear out write fifos (to unlink the request
+ * at the head of the queue) except as part of disconnecting from usb. Such
+ * restrictions prevent drivers from supporting configuration changes,
+ * even to configuration zero (a "chapter 9" requirement).
+ *
+ * This routine may be called in interrupt context.
+ */
+int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ int ret;
+
+ ret = ep->ops->dequeue(ep, req);
+ trace_usb_ep_dequeue(ep, req, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_dequeue);
+
+/**
+ * usb_ep_set_halt - sets the endpoint halt feature.
+ * @ep: the non-isochronous endpoint being stalled
+ *
+ * Use this to stall an endpoint, perhaps as an error report.
+ * Except for control endpoints,
+ * the endpoint stays halted (will not stream any data) until the host
+ * clears this feature; drivers may need to empty the endpoint's request
+ * queue first, to make sure no inappropriate transfers happen.
+ *
+ * Note that while an endpoint CLEAR_FEATURE will be invisible to the
+ * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the
+ * current altsetting, see usb_ep_clear_halt(). When switching altsettings,
+ * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code. On success, this call sets
+ * underlying hardware state that blocks data transfers.
+ * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
+ * transfer requests are still queued, or if the controller hardware
+ * (usually a FIFO) still holds bytes that the host hasn't collected.
+ */
+int usb_ep_set_halt(struct usb_ep *ep)
+{
+ int ret;
+
+ ret = ep->ops->set_halt(ep, 1);
+ trace_usb_ep_set_halt(ep, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_halt);
+
+/**
+ * usb_ep_clear_halt - clears endpoint halt, and resets toggle
+ * @ep:the bulk or interrupt endpoint being reset
+ *
+ * Use this when responding to the standard usb "set interface" request,
+ * for endpoints that aren't reconfigured, after clearing any other state
+ * in the endpoint's i/o queue.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code. On success, this call clears
+ * the underlying hardware state reflecting endpoint halt and data toggle.
+ * Note that some hardware can't support this request (like pxa2xx_udc),
+ * and accordingly can't correctly implement interface altsettings.
+ */
+int usb_ep_clear_halt(struct usb_ep *ep)
+{
+ int ret;
+
+ ret = ep->ops->set_halt(ep, 0);
+ trace_usb_ep_clear_halt(ep, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_clear_halt);
+
+/**
+ * usb_ep_set_wedge - sets the halt feature and ignores clear requests
+ * @ep: the endpoint being wedged
+ *
+ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
+ * requests. If the gadget driver clears the halt status, it will
+ * automatically unwedge the endpoint.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_ep_set_wedge(struct usb_ep *ep)
+{
+ int ret;
+
+ if (ep->ops->set_wedge)
+ ret = ep->ops->set_wedge(ep);
+ else
+ ret = ep->ops->set_halt(ep, 1);
+
+ trace_usb_ep_set_wedge(ep, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_wedge);
+
+/**
+ * usb_ep_fifo_status - returns number of bytes in fifo, or error
+ * @ep: the endpoint whose fifo status is being checked.
+ *
+ * FIFO endpoints may have "unclaimed data" in them in certain cases,
+ * such as after aborted transfers. Hosts may not have collected all
+ * the IN data written by the gadget driver (and reported by a request
+ * completion). The gadget driver may not have collected all the data
+ * written OUT to it by the host. Drivers that need precise handling for
+ * fault reporting or recovery may need to use this call.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * This returns the number of such bytes in the fifo, or a negative
+ * errno if the endpoint doesn't use a FIFO or doesn't support such
+ * precise handling.
+ */
+int usb_ep_fifo_status(struct usb_ep *ep)
+{
+ int ret;
+
+ if (ep->ops->fifo_status)
+ ret = ep->ops->fifo_status(ep);
+ else
+ ret = -EOPNOTSUPP;
+
+ trace_usb_ep_fifo_status(ep, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_fifo_status);
+
+/**
+ * usb_ep_fifo_flush - flushes contents of a fifo
+ * @ep: the endpoint whose fifo is being flushed.
+ *
+ * This call may be used to flush the "unclaimed data" that may exist in
+ * an endpoint fifo after abnormal transaction terminations. The call
+ * must never be used except when endpoint is not being used for any
+ * protocol translation.
+ *
+ * This routine may be called in interrupt context.
+ */
+void usb_ep_fifo_flush(struct usb_ep *ep)
+{
+ if (ep->ops->fifo_flush)
+ ep->ops->fifo_flush(ep);
+
+ trace_usb_ep_fifo_flush(ep, 0);
+}
+EXPORT_SYMBOL_GPL(usb_ep_fifo_flush);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_frame_number - returns the current frame number
+ * @gadget: controller that reports the frame number
+ *
+ * Returns the usb frame number, normally eleven bits from a SOF packet,
+ * or negative errno if this device doesn't support this capability.
+ */
+int usb_gadget_frame_number(struct usb_gadget *gadget)
+{
+ int ret;
+
+ ret = gadget->ops->get_frame(gadget);
+
+ trace_usb_gadget_frame_number(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_frame_number);
+
+/**
+ * usb_gadget_wakeup - tries to wake up the host connected to this gadget
+ * @gadget: controller used to wake up the host
+ *
+ * Returns zero on success, else negative error code if the hardware
+ * doesn't support such attempts, or its support has not been enabled
+ * by the usb host. Drivers must return device descriptors that report
+ * their ability to support this, or hosts won't enable it.
+ *
+ * This may also try to use SRP to wake the host and start enumeration,
+ * even if OTG isn't otherwise in use. OTG devices may also start
+ * remote wakeup even when hosts don't explicitly enable it.
+ */
+int usb_gadget_wakeup(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->wakeup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->wakeup(gadget);
+
+out:
+ trace_usb_gadget_wakeup(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_wakeup);
+
+/**
+ * usb_gadget_set_remote_wakeup - configures the device remote wakeup feature.
+ * @gadget:the device being configured for remote wakeup
+ * @set:value to be configured.
+ *
+ * set to one to enable remote wakeup feature and zero to disable it.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_set_remote_wakeup(struct usb_gadget *gadget, int set)
+{
+ int ret = 0;
+
+ if (!gadget->ops->set_remote_wakeup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->set_remote_wakeup(gadget, set);
+
+out:
+ trace_usb_gadget_set_remote_wakeup(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_remote_wakeup);
+
+/**
+ * usb_gadget_set_selfpowered - sets the device selfpowered feature.
+ * @gadget:the device being declared as self-powered
+ *
+ * this affects the device status reported by the hardware driver
+ * to reflect that it now has a local power supply.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->set_selfpowered) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->set_selfpowered(gadget, 1);
+
+out:
+ trace_usb_gadget_set_selfpowered(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered);
+
+/**
+ * usb_gadget_clear_selfpowered - clear the device selfpowered feature.
+ * @gadget:the device being declared as bus-powered
+ *
+ * this affects the device status reported by the hardware driver.
+ * some hardware may not support bus-powered operation, in which
+ * case this feature's value can never change.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->set_selfpowered) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->set_selfpowered(gadget, 0);
+
+out:
+ trace_usb_gadget_clear_selfpowered(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered);
+
+/**
+ * usb_gadget_vbus_connect - Notify controller that VBUS is powered
+ * @gadget:The device which now has VBUS power.
+ * Context: can sleep
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session starting. Common responses include
+ * resuming the controller, activating the D+ (or D-) pullup to let the
+ * host detect that a USB device is attached, and starting to draw power
+ * (8mA or possibly more, especially after SET_CONFIGURATION).
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_connect(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->vbus_session) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->vbus_session(gadget, 1);
+
+out:
+ trace_usb_gadget_vbus_connect(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect);
+
+/**
+ * usb_gadget_vbus_draw - constrain controller's VBUS power usage
+ * @gadget:The device whose VBUS usage is being described
+ * @mA:How much current to draw, in milliAmperes. This should be twice
+ * the value listed in the configuration descriptor bMaxPower field.
+ *
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume. For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ int ret = 0;
+
+ if (!gadget->ops->vbus_draw) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->vbus_draw(gadget, mA);
+ if (!ret)
+ gadget->mA = mA;
+
+out:
+ trace_usb_gadget_vbus_draw(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw);
+
+/**
+ * usb_gadget_vbus_disconnect - notify controller about VBUS session end
+ * @gadget:the device whose VBUS supply is being described
+ * Context: can sleep
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session ending. Common responses include
+ * reversing everything done in usb_gadget_vbus_connect().
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ if (!gadget->ops->vbus_session) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = gadget->ops->vbus_session(gadget, 0);
+
+out:
+ trace_usb_gadget_vbus_disconnect(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
+
+static int usb_gadget_connect_locked(struct usb_gadget *gadget)
+ __must_hold(&gadget->udc->connect_lock)
+{
+ int ret = 0;
+
+ if (!gadget->ops->pullup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) {
+ /*
+ * If the gadget isn't usable (because it is deactivated,
+ * unbound, or not yet started), we only save the new state.
+ * The gadget will be connected automatically when it is
+ * activated/bound/started.
+ */
+ gadget->connected = true;
+ goto out;
+ }
+
+ ret = gadget->ops->pullup(gadget, 1);
+ if (!ret)
+ gadget->connected = 1;
+
+out:
+ trace_usb_gadget_connect(gadget, ret);
+
+ return ret;
+}
+
+/**
+ * usb_gadget_connect - software-controlled connect to USB host
+ * @gadget:the peripheral being connected
+ *
+ * Enables the D+ (or potentially D-) pullup. The host will start
+ * enumerating this gadget when the pullup is active and a VBUS session
+ * is active (the link is powered).
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_connect(struct usb_gadget *gadget)
+{
+ int ret;
+
+ mutex_lock(&gadget->udc->connect_lock);
+ ret = usb_gadget_connect_locked(gadget);
+ mutex_unlock(&gadget->udc->connect_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_connect);
+
+static int usb_gadget_disconnect_locked(struct usb_gadget *gadget)
+ __must_hold(&gadget->udc->connect_lock)
+{
+ int ret = 0;
+
+ if (!gadget->ops->pullup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!gadget->connected)
+ goto out;
+
+ if (gadget->deactivated || !gadget->udc->started) {
+ /*
+ * If gadget is deactivated we only save new state.
+ * Gadget will stay disconnected after activation.
+ */
+ gadget->connected = false;
+ goto out;
+ }
+
+ ret = gadget->ops->pullup(gadget, 0);
+ if (!ret)
+ gadget->connected = 0;
+
+ mutex_lock(&udc_lock);
+ if (gadget->udc->driver)
+ gadget->udc->driver->disconnect(gadget);
+ mutex_unlock(&udc_lock);
+
+out:
+ trace_usb_gadget_disconnect(gadget, ret);
+
+ return ret;
+}
+
+/**
+ * usb_gadget_disconnect - software-controlled disconnect from USB host
+ * @gadget:the peripheral being disconnected
+ *
+ * Disables the D+ (or potentially D-) pullup, which the host may see
+ * as a disconnect (when a VBUS session is active). Not all systems
+ * support software pullup controls.
+ *
+ * Following a successful disconnect, invoke the ->disconnect() callback
+ * for the current gadget driver so that UDC drivers don't need to.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_disconnect(struct usb_gadget *gadget)
+{
+ int ret;
+
+ mutex_lock(&gadget->udc->connect_lock);
+ ret = usb_gadget_disconnect_locked(gadget);
+ mutex_unlock(&gadget->udc->connect_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_disconnect);
+
+/**
+ * usb_gadget_deactivate - deactivate function which is not ready to work
+ * @gadget: the peripheral being deactivated
+ *
+ * This routine may be used during the gadget driver bind() call to prevent
+ * the peripheral from ever being visible to the USB host, unless later
+ * usb_gadget_activate() is called. For example, user mode components may
+ * need to be activated before the system can talk to hosts.
+ *
+ * This routine may sleep; it must not be called in interrupt context
+ * (such as from within a gadget driver's disconnect() callback).
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_deactivate(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ mutex_lock(&gadget->udc->connect_lock);
+ if (gadget->deactivated)
+ goto unlock;
+
+ if (gadget->connected) {
+ ret = usb_gadget_disconnect_locked(gadget);
+ if (ret)
+ goto unlock;
+
+ /*
+ * If gadget was being connected before deactivation, we want
+ * to reconnect it in usb_gadget_activate().
+ */
+ gadget->connected = true;
+ }
+ gadget->deactivated = true;
+
+unlock:
+ mutex_unlock(&gadget->udc->connect_lock);
+ trace_usb_gadget_deactivate(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_deactivate);
+
+/**
+ * usb_gadget_activate - activate function which is not ready to work
+ * @gadget: the peripheral being activated
+ *
+ * This routine activates gadget which was previously deactivated with
+ * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed.
+ *
+ * This routine may sleep; it must not be called in interrupt context.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_activate(struct usb_gadget *gadget)
+{
+ int ret = 0;
+
+ mutex_lock(&gadget->udc->connect_lock);
+ if (!gadget->deactivated)
+ goto unlock;
+
+ gadget->deactivated = false;
+
+ /*
+ * If gadget has been connected before deactivation, or became connected
+ * while it was being deactivated, we call usb_gadget_connect().
+ */
+ if (gadget->connected)
+ ret = usb_gadget_connect_locked(gadget);
+
+unlock:
+ mutex_unlock(&gadget->udc->connect_lock);
+ trace_usb_gadget_activate(gadget, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_activate);
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef CONFIG_HAS_DMA
+
+int usb_gadget_map_request_by_dev(struct device *dev,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return 0;
- req->dma = dma_map_single(req->buf, req->length,
- is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (req->sg_was_mapped) {
+ req->num_mapped_sgs = req->num_sgs;
+ return 0;
+ }
+
+ if (req->num_sgs) {
+ int mapped;
+
+ mapped = dma_map_sg(dev, req->sg, req->num_sgs,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (mapped == 0) {
+ dev_err(dev, "failed to map SGs\n");
+ return -EFAULT;
+ }
+
+ req->num_mapped_sgs = mapped;
+ } else {
+ if (is_vmalloc_addr(req->buf)) {
+ dev_err(dev, "buffer is not dma capable\n");
+ return -EFAULT;
+ } else if (object_is_on_stack(req->buf)) {
+ dev_err(dev, "buffer is on stack\n");
+ return -EFAULT;
+ }
+
+ req->dma = dma_map_single(dev, req->buf, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, req->dma)) {
+ dev_err(dev, "failed to map buffer\n");
+ return -EFAULT;
+ }
+
+ req->dma_mapped = 1;
+ }
return 0;
}
+EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev);
+
+int usb_gadget_map_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
+{
+ return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in);
+}
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
-void usb_gadget_unmap_request(struct usb_gadget *gadget,
+void usb_gadget_unmap_request_by_dev(struct device *dev,
struct usb_request *req, int is_in)
{
- if (req->length == 0)
+ if (req->length == 0 || req->sg_was_mapped)
return;
- dma_unmap_single(req->dma, req->length,
- is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (req->num_mapped_sgs) {
+ dma_unmap_sg(dev, req->sg, req->num_sgs,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ req->num_mapped_sgs = 0;
+ } else if (req->dma_mapped) {
+ dma_unmap_single(dev, req->dma, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->dma_mapped = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
+
+void usb_gadget_unmap_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
+{
+ usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in);
}
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
+#endif /* CONFIG_HAS_DMA */
+
/* ------------------------------------------------------------------------- */
/**
* usb_gadget_giveback_request - give the request back to the gadget layer
- * Context: in_interrupt()
+ * @ep: the endpoint to be used with with the request
+ * @req: the request being given back
*
* This is called by device controller drivers in order to return the
* completed request back to the gadget layer.
@@ -83,21 +991,191 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req)
{
+ if (likely(req->status == 0))
+ usb_led_activity(USB_LED_EVENT_GADGET);
+
+ trace_usb_gadget_giveback_request(ep, req, 0);
+
req->complete(ep, req);
}
EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
/* ------------------------------------------------------------------------- */
+/**
+ * gadget_find_ep_by_name - returns ep whose name is the same as sting passed
+ * in second parameter or NULL if searched endpoint not found
+ * @g: controller to check for quirk
+ * @name: name of searched endpoint
+ */
+struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name)
+{
+ struct usb_ep *ep;
+
+ gadget_for_each_ep(ep, g) {
+ if (!strcmp(ep->name, name))
+ return ep;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gadget_find_ep_by_name);
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
+ struct usb_ep *ep, struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp)
+{
+ u8 type;
+ u16 max;
+ int num_req_streams = 0;
+
+ /* endpoint already claimed? */
+ if (ep->claimed)
+ return 0;
+
+ type = usb_endpoint_type(desc);
+ max = usb_endpoint_maxp(desc);
+
+ if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
+ return 0;
+ if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
+ return 0;
+
+ if (max > ep->maxpacket_limit)
+ return 0;
+
+ /* "high bandwidth" works only at high speed */
+ if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1)
+ return 0;
+
+ switch (type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* only support ep0 for portable CONTROL traffic */
+ return 0;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!ep->caps.type_iso)
+ return 0;
+ /* ISO: limit 1023 bytes full speed, 1024 high/super speed */
+ if (!gadget_is_dualspeed(gadget) && max > 1023)
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if (!ep->caps.type_bulk)
+ return 0;
+ if (ep_comp && gadget_is_superspeed(gadget)) {
+ /* Get the number of required streams from the
+ * EP companion descriptor and see if the EP
+ * matches it
+ */
+ num_req_streams = ep_comp->bmAttributes & 0x1f;
+ if (num_req_streams > ep->max_streams)
+ return 0;
+ }
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ /* Bulk endpoints handle interrupt transfers,
+ * except the toggle-quirky iso-synch kind
+ */
+ if (!ep->caps.type_int && !ep->caps.type_bulk)
+ return 0;
+ /* INT: limit 64 bytes full speed, 1024 high/super speed */
+ if (!gadget_is_dualspeed(gadget) && max > 64)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
+
+/**
+ * usb_gadget_check_config - checks if the UDC can support the binded
+ * configuration
+ * @gadget: controller to check the USB configuration
+ *
+ * Ensure that a UDC is able to support the requested resources by a
+ * configuration, and that there are no resource limitations, such as
+ * internal memory allocated to all requested endpoints.
+ *
+ * Returns zero on success, else a negative errno.
+ */
+int usb_gadget_check_config(struct usb_gadget *gadget)
+{
+ if (gadget->ops->check_config)
+ return gadget->ops->check_config(gadget);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_check_config);
+
+/* ------------------------------------------------------------------------- */
+
+static void usb_gadget_state_work(struct work_struct *work)
+{
+ struct usb_gadget *gadget = work_to_gadget(work);
+ struct usb_udc *udc = gadget->udc;
+
+ if (udc)
+ sysfs_notify(&udc->dev.kobj, NULL, "state");
+}
+
void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state)
{
gadget->state = state;
+ schedule_work(&gadget->work);
}
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
/* ------------------------------------------------------------------------- */
+/* Acquire connect_lock before calling this function. */
+static int usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock)
+{
+ if (udc->vbus)
+ return usb_gadget_connect_locked(udc->gadget);
+ else
+ return usb_gadget_disconnect_locked(udc->gadget);
+}
+
+static void vbus_event_work(struct work_struct *work)
+{
+ struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work);
+
+ mutex_lock(&udc->connect_lock);
+ usb_udc_connect_control_locked(udc);
+ mutex_unlock(&udc->connect_lock);
+}
+
+/**
+ * usb_udc_vbus_handler - updates the udc core vbus status, and try to
+ * connect or disconnect gadget
+ * @gadget: The gadget which vbus change occurs
+ * @status: The vbus status
+ *
+ * The udc driver calls it when it wants to connect or disconnect gadget
+ * according to vbus status.
+ *
+ * This function can be invoked from interrupt context by irq handlers of
+ * the gadget drivers, however, usb_udc_connect_control() has to run in
+ * non-atomic context due to the following:
+ * a. Some of the gadget driver implementations expect the ->pullup
+ * callback to be invoked in non-atomic context.
+ * b. usb_gadget_disconnect() acquires udc_lock which is a mutex.
+ * Hence offload invocation of usb_udc_connect_control() to workqueue.
+ */
+void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
+{
+ struct usb_udc *udc = gadget->udc;
+
+ if (udc) {
+ udc->vbus = status;
+ schedule_work(&udc->vbus_work);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
+
/**
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which bus reset occurs
@@ -107,47 +1185,143 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
* reset occurs, it notifies the gadget driver that the bus reset occurs as
* well as updates gadget state.
*/
-void usb_gadget_udc_reset(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ driver->reset(gadget);
+ usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
+
+/**
+ * usb_gadget_udc_start_locked - tells usb device controller to start up
+ * @udc: The UDC to be started
+ *
+ * This call is issued by the UDC Class driver when it's about
+ * to register a gadget driver to the device controller, before
+ * calling gadget driver's bind() method.
+ *
+ * It allows the controller to be powered off until strictly
+ * necessary to have it powered on.
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller should acquire connect_lock before invoking this function.
+ */
+static inline int usb_gadget_udc_start_locked(struct usb_udc *udc)
+ __must_hold(&udc->connect_lock)
+{
+ int ret;
+
+ if (udc->started) {
+ dev_err(&udc->dev, "UDC had already started\n");
+ return -EBUSY;
+ }
+
+ ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver);
+ if (!ret)
+ udc->started = true;
+
+ return ret;
+}
+
+/**
+ * usb_gadget_udc_stop_locked - tells usb device controller we don't need it anymore
+ * @udc: The UDC to be stopped
+ *
+ * This call is issued by the UDC Class driver after calling
+ * gadget driver's unbind() method.
+ *
+ * The details are implementation specific, but it can go as
+ * far as powering off UDC completely and disable its data
+ * line pullups.
+ *
+ * Caller should acquire connect lock before invoking this function.
+ */
+static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc)
+ __must_hold(&udc->connect_lock)
+{
+ if (!udc->started) {
+ dev_err(&udc->dev, "UDC had already stopped\n");
+ return;
+ }
+
+ udc->gadget->ops->udc_stop(udc->gadget);
+ udc->started = false;
+}
+
+/**
+ * usb_gadget_udc_set_speed - tells usb device controller speed supported by
+ * current driver
+ * @udc: The device we want to set maximum speed
+ * @speed: The maximum speed to allowed to run
+ *
+ * This call is issued by the UDC Class driver before calling
+ * usb_gadget_udc_start() in order to make sure that we don't try to
+ * connect on speeds the gadget driver doesn't support.
+ */
+static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
+ enum usb_device_speed speed)
{
- driver->reset(gadget);
- usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+ struct usb_gadget *gadget = udc->gadget;
+ enum usb_device_speed s;
+
+ if (speed == USB_SPEED_UNKNOWN)
+ s = gadget->max_speed;
+ else
+ s = min(speed, gadget->max_speed);
+
+ if (s == USB_SPEED_SUPER_PLUS && gadget->ops->udc_set_ssp_rate)
+ gadget->ops->udc_set_ssp_rate(gadget, gadget->max_ssp_rate);
+ else if (gadget->ops->udc_set_speed)
+ gadget->ops->udc_set_speed(gadget, s);
}
-EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
/**
- * usb_gadget_udc_start - tells usb device controller to start up
- * @udc: The UDC to be started
- *
- * This call is issued by the UDC Class driver when it's about
- * to register a gadget driver to the device controller, before
- * calling gadget driver's bind() method.
+ * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks
+ * @udc: The UDC which should enable async callbacks
*
- * It allows the controller to be powered off until strictly
- * necessary to have it powered on.
+ * This routine is used when binding gadget drivers. It undoes the effect
+ * of usb_gadget_disable_async_callbacks(); the UDC driver should enable IRQs
+ * (if necessary) and resume issuing callbacks.
*
- * Returns zero on success, else negative errno.
+ * This routine will always be called in process context.
*/
-static inline int usb_gadget_udc_start(struct usb_udc *udc)
+static inline void usb_gadget_enable_async_callbacks(struct usb_udc *udc)
{
- return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
+ struct usb_gadget *gadget = udc->gadget;
+
+ if (gadget->ops->udc_async_callbacks)
+ gadget->ops->udc_async_callbacks(gadget, true);
}
/**
- * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
- * @gadget: The device we want to stop activity
- * @driver: The driver to unbind from @gadget
+ * usb_gadget_disable_async_callbacks - tell usb device controller to disable asynchronous callbacks
+ * @udc: The UDC which should disable async callbacks
*
- * This call is issued by the UDC Class driver after calling
- * gadget driver's unbind() method.
+ * This routine is used when unbinding gadget drivers. It prevents a race:
+ * The UDC driver doesn't know when the gadget driver's ->unbind callback
+ * runs, so unless it is told to disable asynchronous callbacks, it might
+ * issue a callback (such as ->disconnect) after the unbind has completed.
*
- * The details are implementation specific, but it can go as
- * far as powering off UDC completely and disable its data
- * line pullups.
+ * After this function runs, the UDC driver must suppress all ->suspend,
+ * ->resume, ->disconnect, ->reset, and ->setup callbacks to the gadget driver
+ * until async callbacks are again enabled. A simple-minded but effective
+ * way to accomplish this is to tell the UDC hardware not to generate any
+ * more IRQs.
+ *
+ * Request completion callbacks must still be issued. However, it's okay
+ * to defer them until the request is cancelled, since the pull-up will be
+ * turned off during the time period when async callbacks are disabled.
+ *
+ * This routine will always be called in process context.
*/
-static inline void usb_gadget_udc_stop(struct usb_udc *udc)
+static inline void usb_gadget_disable_async_callbacks(struct usb_udc *udc)
{
- udc->gadget->ops->udc_stop(udc->gadget);
+ struct usb_gadget *gadget = udc->gadget;
+
+ if (gadget->ops->udc_async_callbacks)
+ gadget->ops->udc_async_callbacks(gadget, false);
}
/**
@@ -162,51 +1336,177 @@ static void usb_udc_release(struct device *dev)
struct usb_udc *udc;
udc = container_of(dev, struct usb_udc, dev);
+ dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
kfree(udc);
}
+static const struct attribute_group *usb_udc_attr_groups[];
+
+static void usb_udc_nop_release(struct device *dev)
+{
+ dev_vdbg(dev, "%s\n", __func__);
+}
+
/**
- * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * usb_initialize_gadget - initialize a gadget and its embedded struct device
* @parent: the parent device to this udc. Usually the controller driver's
* device.
- * @gadget: the gadget to be added to the list.
+ * @gadget: the gadget to be initialized.
* @release: a gadget release function.
+ */
+void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
+{
+ INIT_WORK(&gadget->work, usb_gadget_state_work);
+ gadget->dev.parent = parent;
+
+ if (release)
+ gadget->dev.release = release;
+ else
+ gadget->dev.release = usb_udc_nop_release;
+
+ device_initialize(&gadget->dev);
+ gadget->dev.bus = &gadget_bus_type;
+}
+EXPORT_SYMBOL_GPL(usb_initialize_gadget);
+
+/**
+ * usb_add_gadget - adds a new gadget to the udc class driver list
+ * @gadget: the gadget to be added to the list.
*
* Returns zero on success, negative errno otherwise.
+ * Does not do a final usb_put_gadget() if an error occurs.
*/
-int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
- void (*release)(struct device *dev))
+int usb_add_gadget(struct usb_gadget *gadget)
{
struct usb_udc *udc;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
- goto err1;
-
- dev_set_name(&gadget->dev, "gadget");
- gadget->dev.parent = parent;
+ goto error;
+ device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
- udc->dev.class = udc_class;
- udc->dev.parent = parent;
+ udc->dev.class = &udc_class;
+ udc->dev.groups = usb_udc_attr_groups;
+ udc->dev.parent = gadget->dev.parent;
+ ret = dev_set_name(&udc->dev, "%s",
+ kobject_name(&gadget->dev.parent->kobj));
+ if (ret)
+ goto err_put_udc;
udc->gadget = gadget;
+ gadget->udc = udc;
+ mutex_init(&udc->connect_lock);
+
+ udc->started = false;
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
+ mutex_unlock(&udc_lock);
+ INIT_WORK(&udc->vbus_work, vbus_event_work);
+
+ ret = device_add(&udc->dev);
+ if (ret)
+ goto err_unlist_udc;
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+ udc->vbus = true;
- mutex_unlock(&udc_lock);
+ ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL);
+ if (ret < 0)
+ goto err_del_udc;
+ gadget->id_number = ret;
+ dev_set_name(&gadget->dev, "gadget.%d", ret);
+
+ ret = device_add(&gadget->dev);
+ if (ret)
+ goto err_free_id;
+
+ ret = sysfs_create_link(&udc->dev.kobj,
+ &gadget->dev.kobj, "gadget");
+ if (ret)
+ goto err_del_gadget;
return 0;
-err1:
+ err_del_gadget:
+ device_del(&gadget->dev);
+
+ err_free_id:
+ ida_free(&gadget_id_numbers, gadget->id_number);
+
+ err_del_udc:
+ flush_work(&gadget->work);
+ device_del(&udc->dev);
+
+ err_unlist_udc:
+ mutex_lock(&udc_lock);
+ list_del(&udc->list);
+ mutex_unlock(&udc_lock);
+
+ err_put_udc:
+ put_device(&udc->dev);
+
+ error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget);
+
+/**
+ * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller driver's
+ * device.
+ * @gadget: the gadget to be added to the list.
+ * @release: a gadget release function.
+ *
+ * Returns zero on success, negative errno otherwise.
+ * Calls the gadget release function in the latter case.
+ */
+int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
+{
+ int ret;
+
+ usb_initialize_gadget(parent, gadget, release);
+ ret = usb_add_gadget(gadget);
+ if (ret)
+ usb_put_gadget(gadget);
return ret;
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
+/**
+ * usb_get_gadget_udc_name - get the name of the first UDC controller
+ * This functions returns the name of the first UDC controller in the system.
+ * Please note that this interface is usefull only for legacy drivers which
+ * assume that there is only one UDC controller in the system and they need to
+ * get its name before initialization. There is no guarantee that the UDC
+ * of the returned name will be still available, when gadget driver registers
+ * itself.
+ *
+ * Returns pointer to string with UDC controller name on success, NULL
+ * otherwise. Caller should kfree() returned string.
+ */
+char *usb_get_gadget_udc_name(void)
+{
+ struct usb_udc *udc;
+ char *name = NULL;
+
+ /* For now we take the first available UDC */
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list) {
+ if (!udc->driver) {
+ name = kstrdup(udc->gadget->name, GFP_KERNEL);
+ break;
+ }
+ }
+ mutex_unlock(&udc_lock);
+ return name;
+}
+EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name);
+
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
@@ -221,168 +1521,409 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
-static void usb_gadget_remove_driver(struct usb_udc *udc)
-{
- dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
- udc->driver->function);
-
- usb_gadget_disconnect(udc->gadget);
- udc->driver->disconnect(udc->gadget);
- udc->driver->unbind(udc->gadget);
- usb_gadget_udc_stop(udc);
-
- udc->driver = NULL;
-}
-
/**
- * usb_del_gadget_udc - deletes @udc from udc_list
- * @gadget: the gadget to be removed.
+ * usb_del_gadget - deletes a gadget and unregisters its udc
+ * @gadget: the gadget to be deleted.
*
- * This, will call usb_gadget_unregister_driver() if
- * the @udc is still busy.
+ * This will unbind @gadget, if it is bound.
+ * It will not do a final usb_put_gadget().
*/
-void usb_del_gadget_udc(struct usb_gadget *gadget)
+void usb_del_gadget(struct usb_gadget *gadget)
{
- struct usb_udc *udc = NULL;
-
- mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list)
- if (udc->gadget == gadget)
- goto found;
-
- dev_err(gadget->dev.parent, "gadget not registered.\n");
- mutex_unlock(&udc_lock);
+ struct usb_udc *udc = gadget->udc;
- return;
+ if (!udc)
+ return;
-found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+ mutex_lock(&udc_lock);
list_del(&udc->list);
mutex_unlock(&udc_lock);
- if (udc->driver)
- usb_gadget_remove_driver(udc);
+ kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
+ sysfs_remove_link(&udc->dev.kobj, "gadget");
+ device_del(&gadget->dev);
+ flush_work(&gadget->work);
+ ida_free(&gadget_id_numbers, gadget->id_number);
+ cancel_work_sync(&udc->vbus_work);
+ device_unregister(&udc->dev);
}
-EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
-
-/* ------------------------------------------------------------------------- */
+EXPORT_SYMBOL_GPL(usb_del_gadget);
/**
- * usb_gadget_udc_set_speed - tells usb device controller speed supported by
- * current driver
- * @udc: The device we want to set maximum speed
- * @speed: The maximum speed to allowed to run
+ * usb_del_gadget_udc - unregisters a gadget
+ * @gadget: the gadget to be unregistered.
*
- * This call is issued by the UDC Class driver before calling
- * usb_gadget_udc_start() in order to make sure that we don't try to
- * connect on speeds the gadget driver doesn't support.
+ * Calls usb_del_gadget() and does a final usb_put_gadget().
*/
-static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
- enum usb_device_speed speed)
+void usb_del_gadget_udc(struct usb_gadget *gadget)
{
- if (udc->gadget->ops->udc_set_speed) {
- enum usb_device_speed s;
-
- s = min(speed, udc->gadget->max_speed);
- udc->gadget->ops->udc_set_speed(udc->gadget, s);
- }
+ usb_del_gadget(gadget);
+ usb_put_gadget(gadget);
}
+EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
+
+/* ------------------------------------------------------------------------- */
-static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
+static int gadget_match_driver(struct device *dev, const struct device_driver *drv)
{
- int ret;
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ const struct usb_gadget_driver *driver = container_of(drv,
+ struct usb_gadget_driver, driver);
- dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
- driver->function);
+ /* If the driver specifies a udc_name, it must match the UDC's name */
+ if (driver->udc_name &&
+ strcmp(driver->udc_name, dev_name(&udc->dev)) != 0)
+ return 0;
+
+ /* If the driver is already bound to a gadget, it doesn't match */
+ if (driver->is_bound)
+ return 0;
+
+ /* Otherwise any gadget driver matches any UDC */
+ return 1;
+}
+
+static int gadget_bind_driver(struct device *dev)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ struct usb_gadget_driver *driver = container_of(dev->driver,
+ struct usb_gadget_driver, driver);
+ int ret = 0;
+ mutex_lock(&udc_lock);
+ if (driver->is_bound) {
+ mutex_unlock(&udc_lock);
+ return -ENXIO; /* Driver binds to only one gadget */
+ }
+ driver->is_bound = true;
udc->driver = driver;
+ mutex_unlock(&udc_lock);
- usb_gadget_udc_set_speed(udc, driver->speed);
+ dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function);
- ret = driver->bind(udc->gadget);
+ usb_gadget_udc_set_speed(udc, driver->max_speed);
+
+ ret = driver->bind(udc->gadget, driver);
if (ret)
- goto err1;
- ret = usb_gadget_udc_start(udc);
+ goto err_bind;
+
+ mutex_lock(&udc->connect_lock);
+ ret = usb_gadget_udc_start_locked(udc);
if (ret) {
- driver->unbind(udc->gadget);
- goto err1;
+ mutex_unlock(&udc->connect_lock);
+ goto err_start;
}
- usb_gadget_connect(udc->gadget);
+ usb_gadget_enable_async_callbacks(udc);
+ udc->allow_connect = true;
+ ret = usb_udc_connect_control_locked(udc);
+ if (ret)
+ goto err_connect_control;
+
+ mutex_unlock(&udc->connect_lock);
+ kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;
-err1:
+
+ err_connect_control:
+ udc->allow_connect = false;
+ usb_gadget_disable_async_callbacks(udc);
+ if (gadget->irq)
+ synchronize_irq(gadget->irq);
+ usb_gadget_udc_stop_locked(udc);
+ mutex_unlock(&udc->connect_lock);
+
+ err_start:
+ driver->unbind(udc->gadget);
+
+ err_bind:
if (ret != -EISNAM)
dev_err(&udc->dev, "failed to start %s: %d\n",
- udc->driver->function, ret);
+ driver->function, ret);
+
+ mutex_lock(&udc_lock);
udc->driver = NULL;
+ driver->is_bound = false;
+ mutex_unlock(&udc_lock);
+
return ret;
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
+static void gadget_unbind_driver(struct device *dev)
{
- struct usb_udc *udc = NULL;
- unsigned int udc_count = 0;
- int ret;
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ struct usb_gadget_driver *driver = udc->driver;
- if (!driver || !driver->bind || !driver->setup)
- return -EINVAL;
+ dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
+
+ udc->allow_connect = false;
+ cancel_work_sync(&udc->vbus_work);
+ mutex_lock(&udc->connect_lock);
+ usb_gadget_disconnect_locked(gadget);
+ usb_gadget_disable_async_callbacks(udc);
+ if (gadget->irq)
+ synchronize_irq(gadget->irq);
+ mutex_unlock(&udc->connect_lock);
+
+ udc->driver->unbind(gadget);
+
+ mutex_lock(&udc->connect_lock);
+ usb_gadget_udc_stop_locked(udc);
+ mutex_unlock(&udc->connect_lock);
mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list) {
- udc_count++;
+ driver->is_bound = false;
+ udc->driver = NULL;
+ mutex_unlock(&udc_lock);
+
+ kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
+ struct module *owner, const char *mod_name)
+{
+ int ret;
+
+ if (!driver || !driver->bind || !driver->setup)
+ return -EINVAL;
- /* For now we take the first one */
- if (!udc->driver)
- goto found;
+ driver->driver.bus = &gadget_bus_type;
+ driver->driver.owner = owner;
+ driver->driver.mod_name = mod_name;
+ driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
+ ret = driver_register(&driver->driver);
+ if (ret) {
+ pr_warn("%s: driver registration failed: %d\n",
+ driver->function, ret);
+ return ret;
}
- if (!udc_count)
- printf("No UDC available in the system\n");
- else
- /* When this happens, users should 'unbind <class> <index>'
- * using the output of 'dm tree' and looking at the line right
- * after the USB peripheral/device controller.
- */
- printf("All UDCs in use (%d available), use the unbind command\n",
- udc_count);
- mutex_unlock(&udc_lock);
- return -ENODEV;
-found:
- ret = udc_bind_to_driver(udc, driver);
+ mutex_lock(&udc_lock);
+ if (!driver->is_bound) {
+ if (driver->match_existing_only) {
+ pr_warn("%s: couldn't find an available UDC or it's busy\n",
+ driver->function);
+ ret = -EBUSY;
+ } else {
+ pr_info("%s: couldn't find an available UDC\n",
+ driver->function);
+ ret = 0;
+ }
+ }
mutex_unlock(&udc_lock);
+
+ if (ret)
+ driver_unregister(&driver->driver);
return ret;
}
-EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
+EXPORT_SYMBOL_GPL(usb_gadget_register_driver_owner);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ if (!driver || !driver->unbind)
+ return -EINVAL;
+
+ driver_unregister(&driver->driver);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+/* ------------------------------------------------------------------------- */
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+static ssize_t srp_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
{
- return usb_gadget_probe_driver(driver);
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+
+ if (sysfs_streq(buf, "1"))
+ usb_gadget_wakeup(udc->gadget);
+
+ return n;
}
-EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
+static DEVICE_ATTR_WO(srp);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static ssize_t soft_connect_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
{
- struct usb_udc *udc = NULL;
- int ret = -ENODEV;
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ ssize_t ret;
- if (!driver || !driver->unbind)
- return -EINVAL;
+ device_lock(&udc->gadget->dev);
+ if (!udc->driver) {
+ dev_err(dev, "soft-connect without a gadget driver\n");
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (sysfs_streq(buf, "connect")) {
+ mutex_lock(&udc->connect_lock);
+ usb_gadget_udc_start_locked(udc);
+ usb_gadget_connect_locked(udc->gadget);
+ mutex_unlock(&udc->connect_lock);
+ } else if (sysfs_streq(buf, "disconnect")) {
+ mutex_lock(&udc->connect_lock);
+ usb_gadget_disconnect_locked(udc->gadget);
+ usb_gadget_udc_stop_locked(udc);
+ mutex_unlock(&udc->connect_lock);
+ } else {
+ dev_err(dev, "unsupported command '%s'\n", buf);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = n;
+out:
+ device_unlock(&udc->gadget->dev);
+ return ret;
+}
+static DEVICE_ATTR_WO(soft_connect);
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ struct usb_gadget *gadget = udc->gadget;
+
+ return sprintf(buf, "%s\n", usb_state_string(gadget->state));
+}
+static DEVICE_ATTR_RO(state);
+
+static ssize_t function_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ struct usb_gadget_driver *drv;
+ int rc = 0;
mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list)
- if (udc->driver == driver) {
- usb_gadget_remove_driver(udc);
- usb_gadget_set_state(udc->gadget,
- USB_STATE_NOTATTACHED);
- ret = 0;
- break;
- }
+ drv = udc->driver;
+ if (drv && drv->function)
+ rc = scnprintf(buf, PAGE_SIZE, "%s\n", drv->function);
+ mutex_unlock(&udc_lock);
+ return rc;
+}
+static DEVICE_ATTR_RO(function);
+
+#define USB_UDC_SPEED_ATTR(name, param) \
+ssize_t name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
+ return scnprintf(buf, PAGE_SIZE, "%s\n", \
+ usb_speed_string(udc->gadget->param)); \
+} \
+static DEVICE_ATTR_RO(name)
+
+static USB_UDC_SPEED_ATTR(current_speed, speed);
+static USB_UDC_SPEED_ATTR(maximum_speed, max_speed);
+
+#define USB_UDC_ATTR(name) \
+ssize_t name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
+ struct usb_gadget *gadget = udc->gadget; \
+ \
+ return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
+} \
+static DEVICE_ATTR_RO(name)
+
+static USB_UDC_ATTR(is_otg);
+static USB_UDC_ATTR(is_a_peripheral);
+static USB_UDC_ATTR(b_hnp_enable);
+static USB_UDC_ATTR(a_hnp_support);
+static USB_UDC_ATTR(a_alt_hnp_support);
+static USB_UDC_ATTR(is_selfpowered);
+
+static struct attribute *usb_udc_attrs[] = {
+ &dev_attr_srp.attr,
+ &dev_attr_soft_connect.attr,
+ &dev_attr_state.attr,
+ &dev_attr_function.attr,
+ &dev_attr_current_speed.attr,
+ &dev_attr_maximum_speed.attr,
+ &dev_attr_is_otg.attr,
+ &dev_attr_is_a_peripheral.attr,
+ &dev_attr_b_hnp_enable.attr,
+ &dev_attr_a_hnp_support.attr,
+ &dev_attr_a_alt_hnp_support.attr,
+ &dev_attr_is_selfpowered.attr,
+ NULL,
+};
+
+static const struct attribute_group usb_udc_attr_group = {
+ .attrs = usb_udc_attrs,
+};
+
+static const struct attribute_group *usb_udc_attr_groups[] = {
+ &usb_udc_attr_group,
+ NULL,
+};
+
+static int usb_udc_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+ const struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ int ret;
+
+ ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name);
+ if (ret) {
+ dev_err(dev, "failed to add uevent USB_UDC_NAME\n");
+ return ret;
+ }
+
+ mutex_lock(&udc_lock);
+ if (udc->driver)
+ ret = add_uevent_var(env, "USB_UDC_DRIVER=%s",
+ udc->driver->function);
mutex_unlock(&udc_lock);
- return ret;
+ if (ret) {
+ dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
+ return ret;
+ }
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+static const struct class udc_class = {
+ .name = "udc",
+ .dev_uevent = usb_udc_uevent,
+};
+
+static const struct bus_type gadget_bus_type = {
+ .name = "gadget",
+ .probe = gadget_bind_driver,
+ .remove = gadget_unbind_driver,
+ .match = gadget_match_driver,
+};
+
+static int __init usb_udc_init(void)
+{
+ int rc;
+
+ rc = class_register(&udc_class);
+ if (rc)
+ return rc;
+
+ rc = bus_register(&gadget_bus_type);
+ if (rc)
+ class_unregister(&udc_class);
+ return rc;
+}
+subsys_initcall(usb_udc_init);
+
+static void __exit usb_udc_exit(void)
+{
+ bus_unregister(&gadget_bus_type);
+ class_unregister(&udc_class);
+}
+module_exit(usb_udc_exit);
MODULE_DESCRIPTION("UDC Framework");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [RFC PATCH v2 64/64] usb: fix build after resync of DWC3 with kernel v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (62 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 63/64] usb: udc: re-import udc-core.c " Jens Wiklander
@ 2026-05-07 9:28 ` Jens Wiklander
2026-05-11 8:25 ` Anshul Dalal
2026-05-08 15:42 ` [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Simon Glass
` (3 subsequent siblings)
67 siblings, 1 reply; 102+ messages in thread
From: Jens Wiklander @ 2026-05-07 9:28 UTC (permalink / raw)
To: u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jerome Forissier, Jens Wiklander
From: Jerome Forissier <jerome.forissier@linaro.org>
Fix build errors after the re-sync of the DWC3 driver with the kernel.
U-Boot has different needs than the kernel: buses, interrupts, internal
APIs (DMA, traces, DT...) so many adaptations are required. This commit
re-introduces many of the changes that were done locally after the
initial import of the DWC3 code from kernel 3.19-rc1 11 years ago, as
well as other fixes. This is compile-tested only.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
drivers/usb/cdns3/ep0.c | 8 +-
drivers/usb/common/common.c | 23 +
drivers/usb/dwc3/Makefile | 57 +-
drivers/usb/dwc3/core.c | 1761 ++++++------------------
drivers/usb/dwc3/core.h | 39 +-
drivers/usb/dwc3/dwc3-am62.c | 424 +-----
drivers/usb/dwc3/dwc3-omap.c | 2 -
drivers/usb/dwc3/ep0.c | 110 +-
drivers/usb/dwc3/gadget.c | 786 +++--------
drivers/usb/dwc3/gadget.h | 4 +-
drivers/usb/dwc3/io.h | 28 +-
drivers/usb/dwc3/ti_usb_phy.c | 2 -
drivers/usb/gadget/at91_udc.c | 46 -
drivers/usb/gadget/atmel_usba_udc.c | 102 +-
drivers/usb/gadget/ci_udc.c | 419 ------
drivers/usb/gadget/composite.c | 2 +-
drivers/usb/gadget/dwc2_udc_otg.c | 174 +--
drivers/usb/gadget/epautoconf.c | 2 -
drivers/usb/gadget/ether.c | 21 +-
drivers/usb/gadget/f_acm.c | 20 +-
drivers/usb/gadget/f_fastboot.c | 10 +-
drivers/usb/gadget/f_mass_storage.c | 5 +-
drivers/usb/gadget/f_rockusb.c | 11 +-
drivers/usb/gadget/f_sdp.c | 12 +-
drivers/usb/gadget/f_thor.c | 19 +-
drivers/usb/gadget/udc/Makefile | 1 +
drivers/usb/gadget/udc/udc-core.c | 1021 +++-----------
drivers/usb/host/xhci-dwc3.c | 4 +-
drivers/usb/host/xhci-exynos5.c | 2 +-
drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
drivers/usb/musb-new/musb_uboot.c | 39 -
include/dm/device_compat.h | 13 +
include/dm/read.h | 46 +
include/linux/compat.h | 15 +
include/linux/usb/ch9.h | 25 +-
include/linux/usb/gadget.h | 520 ++-----
include/linux/usb/otg.h | 10 +
include/linux/usb/phy.h | 56 +
39 files changed, 1347 insertions(+), 4532 deletions(-)
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
index acff79ae1ca1..40282cbc3234 100644
--- a/drivers/usb/cdns3/ep0.c
+++ b/drivers/usb/cdns3/ep0.c
@@ -341,10 +341,10 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
return -EINVAL;
switch (tmode >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
cdns3_ep0_complete_setup(priv_dev, 0, 1);
/**
* Little delay to give the controller some time
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 13e9a61072a9..7c1a413a9450 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -66,6 +66,13 @@ static const char *const speed_names[] = {
[USB_SPEED_SUPER_PLUS] = "super-speed-plus",
};
+static const char *const ssp_rate[] = {
+ [USB_SSP_GEN_UNKNOWN] = "UNKNOWN",
+ [USB_SSP_GEN_2x1] = "super-speed-plus-gen2x1",
+ [USB_SSP_GEN_1x2] = "super-speed-plus-gen1x2",
+ [USB_SSP_GEN_2x2] = "super-speed-plus-gen2x2",
+};
+
const char *usb_speed_string(enum usb_device_speed speed)
{
if (speed < 0 || speed >= ARRAY_SIZE(speed_names))
@@ -91,6 +98,22 @@ enum usb_device_speed usb_get_maximum_speed(ofnode node)
return USB_SPEED_UNKNOWN;
}
+enum usb_ssp_rate usb_get_maximum_ssp_rate(ofnode node)
+{
+ const char *maximum_speed;
+ int i;
+
+ maximum_speed = ofnode_read_string(node, "maximum-speed");
+ if (!maximum_speed)
+ return USB_SSP_GEN_UNKNOWN;
+
+ for (i = 0; i < ARRAY_SIZE(ssp_rate); i++)
+ if (!strcmp(maximum_speed, ssp_rate[i]))
+ return i;
+
+ return USB_SSP_GEN_UNKNOWN;
+}
+
#if CONFIG_IS_ENABLED(DM_USB)
static const char *const usbphy_modes[] = {
[USBPHY_INTERFACE_MODE_UNKNOWN] = "",
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index a619cd374fb4..ae1725756c0e 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -1,18 +1,59 @@
-# SPDX-License-Identifier: GPL-2.0+
-
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
-obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o
+ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
+ dwc3-y += host.o
+endif
+
+ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
+ dwc3-y += gadget.o ep0.o
+endif
+
+ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
+ dwc3-y += drd.o
+endif
+
+ifneq ($(CONFIG_USB_DWC3_ULPI),)
+ dwc3-y += ulpi.o
+endif
+
+ifneq ($(CONFIG_DEBUG_FS),)
+ dwc3-y += debugfs.o
+endif
+
+##
+# Platform-specific glue layers go here
+#
+# NOTICE: Make sure your glue layer doesn't depend on anything
+# which is arch-specific and that it compiles on all situations.
+#
+# We want to keep this requirement in order to be able to compile
+# the entire driver (with all its glue layers) on several architectures
+# and make sure it compiles fine. This will also help with allmodconfig
+# and allyesconfig builds.
+##
-obj-$(CONFIG_$(PHASE_)USB_DWC3_AM62) += dwc3-am62.o
+obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
+obj-$(CONFIG_$(PHASE_)USB_DWC3_GENERIC) += dwc3-generic.o
+obj-$(CONFIG_USB_DWC3_STI) += dwc3-generic-sti.o
+obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o
+obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
+obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
+obj-$(CONFIG_USB_DWC3_LAYERSCAPE) += dwc3-layerscape.o
obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o
-obj-$(CONFIG_$(PHASE_)USB_DWC3_GENERIC) += dwc3-generic.o
+obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
+obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
+obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o
+obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
+obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o
-obj-$(CONFIG_USB_DWC3_LAYERSCAPE) += dwc3-layerscape.o
+obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o
-obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o
-obj-$(CONFIG_USB_DWC3_STI) += dwc3-generic-sti.o
+obj-$(CONFIG_$(PHASE_)USB_DWC3_GENERIC) += dwc3-generic.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 8002c23a5a02..5b6004a54d9b 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -8,104 +8,40 @@
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
-#include <linux/clk.h>
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <linux/list.h>
+#include <clk.h>
+#include <cpu_func.h>
+#include <malloc.h>
+#include <dwc3-uboot.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
-#include <linux/acpi.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/pinctrl/devinfo.h>
-#include <linux/reset.h>
-#include <linux/bitfield.h>
-
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <dm.h>
+#include <generic-phy.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/usb/of.h>
-#include <linux/usb/otg.h>
+#include <linux/bitfield.h>
+#include <linux/math64.h>
+#include <linux/time.h>
+
+#include <version.h>
#include "core.h"
#include "gadget.h"
#include "glue.h"
#include "io.h"
-#include "debug.h"
#include "../host/xhci-ext-caps.h"
-#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
-
-/**
- * dwc3_get_dr_mode - Validates and sets dr_mode
- * @dwc: pointer to our context structure
- */
-static int dwc3_get_dr_mode(struct dwc3 *dwc)
-{
- enum usb_dr_mode mode;
- struct device *dev = dwc->dev;
- unsigned int hw_mode;
-
- if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
- dwc->dr_mode = USB_DR_MODE_OTG;
-
- mode = dwc->dr_mode;
- hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
-
- switch (hw_mode) {
- case DWC3_GHWPARAMS0_MODE_GADGET:
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
- dev_err(dev,
- "Controller does not support host mode.\n");
- return -EINVAL;
- }
- mode = USB_DR_MODE_PERIPHERAL;
- break;
- case DWC3_GHWPARAMS0_MODE_HOST:
- if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
- dev_err(dev,
- "Controller does not support device mode.\n");
- return -EINVAL;
- }
- mode = USB_DR_MODE_HOST;
- break;
- default:
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
- mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
- mode = USB_DR_MODE_PERIPHERAL;
-
- /*
- * DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG
- * mode. If the controller supports DRD but the dr_mode is not
- * specified or set to OTG, then set the mode to peripheral.
- */
- if (mode == USB_DR_MODE_OTG && !dwc->edev &&
- (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
- !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
- !DWC3_VER_IS_PRIOR(DWC3, 330A))
- mode = USB_DR_MODE_PERIPHERAL;
- }
-
- if (mode != dwc->dr_mode) {
- dev_warn(dev,
- "Configuration mismatch. dr_mode forced to %s\n",
- mode == USB_DR_MODE_HOST ? "host" : "gadget");
+#define msleep(a) udelay(a * 1000)
- dwc->dr_mode = mode;
- }
+#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
- return 0;
-}
+static LIST_HEAD(dwc3_list);
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
{
@@ -158,146 +94,6 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
dwc->current_dr_role = mode;
}
-static void __dwc3_set_mode(struct work_struct *work)
-{
- struct dwc3 *dwc = work_to_dwc(work);
- unsigned long flags;
- int ret;
- u32 reg;
- u32 desired_dr_role;
- int i;
-
- mutex_lock(&dwc->mutex);
- spin_lock_irqsave(&dwc->lock, flags);
- desired_dr_role = dwc->desired_dr_role;
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- pm_runtime_get_sync(dwc->dev);
-
- if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
- dwc3_otg_update(dwc, 0);
-
- if (!desired_dr_role)
- goto out;
-
- if (desired_dr_role == dwc->current_dr_role)
- goto out;
-
- if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
- goto out;
-
- switch (dwc->current_dr_role) {
- case DWC3_GCTL_PRTCAP_HOST:
- dwc3_host_exit(dwc);
- break;
- case DWC3_GCTL_PRTCAP_DEVICE:
- dwc3_gadget_exit(dwc);
- dwc3_event_buffers_cleanup(dwc);
- break;
- case DWC3_GCTL_PRTCAP_OTG:
- dwc3_otg_exit(dwc);
- spin_lock_irqsave(&dwc->lock, flags);
- dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
- spin_unlock_irqrestore(&dwc->lock, flags);
- dwc3_otg_update(dwc, 1);
- break;
- default:
- break;
- }
-
- /*
- * When current_dr_role is not set, there's no role switching.
- * Only perform GCTL.CoreSoftReset when there's DRD role switching.
- */
- if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
- DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
- desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg |= DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
-
- /*
- * Wait for internal clocks to synchronized. DWC_usb31 and
- * DWC_usb32 may need at least 50ms (less for DWC_usb3). To
- * keep it consistent across different IPs, let's wait up to
- * 100ms before clearing GCTL.CORESOFTRESET.
- */
- msleep(100);
-
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg &= ~DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
- }
-
- spin_lock_irqsave(&dwc->lock, flags);
-
- dwc3_set_prtcap(dwc, desired_dr_role, false);
-
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- switch (desired_dr_role) {
- case DWC3_GCTL_PRTCAP_HOST:
- ret = dwc3_host_init(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to initialize host\n");
- } else {
- if (dwc->usb2_phy)
- otg_set_vbus(dwc->usb2_phy->otg, true);
-
- for (i = 0; i < dwc->num_usb2_ports; i++)
- phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
- for (i = 0; i < dwc->num_usb3_ports; i++)
- phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
-
- if (dwc->dis_split_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
- reg |= DWC3_GUCTL3_SPLITDISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
- }
- }
- break;
- case DWC3_GCTL_PRTCAP_DEVICE:
- dwc3_core_soft_reset(dwc);
-
- dwc3_event_buffers_setup(dwc);
-
- if (dwc->usb2_phy)
- otg_set_vbus(dwc->usb2_phy->otg, false);
- phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
- phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
-
- ret = dwc3_gadget_init(dwc);
- if (ret)
- dev_err(dwc->dev, "failed to initialize peripheral\n");
- break;
- case DWC3_GCTL_PRTCAP_OTG:
- dwc3_otg_init(dwc);
- dwc3_otg_update(dwc, 0);
- break;
- default:
- break;
- }
-
-out:
- pm_runtime_mark_last_busy(dwc->dev);
- pm_runtime_put_autosuspend(dwc->dev);
- mutex_unlock(&dwc->mutex);
-}
-
-void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
-{
- unsigned long flags;
-
- if (dwc->dr_mode != USB_DR_MODE_OTG)
- return;
-
- spin_lock_irqsave(&dwc->lock, flags);
- dwc->desired_dr_role = mode;
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- queue_work(system_freezable_wq, &dwc->drd_work);
-}
-
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
{
struct dwc3 *dwc = dep->dwc;
@@ -474,7 +270,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
- dma_free_coherent(dwc->sysdev, evt->length, evt->buf, evt->dma);
+ dma_free_coherent(evt->buf);
}
/**
@@ -490,18 +286,19 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
{
struct dwc3_event_buffer *evt;
- evt = devm_kzalloc(dwc->dev, sizeof(*evt), GFP_KERNEL);
+ evt = devm_kzalloc((struct udevice*)dwc->dev, sizeof(*evt), GFP_KERNEL);
if (!evt)
return ERR_PTR(-ENOMEM);
evt->dwc = dwc;
evt->length = length;
- evt->cache = devm_kzalloc(dwc->dev, length, GFP_KERNEL);
+ evt->cache = devm_kzalloc((struct udevice *)dwc->dev, length,
+ GFP_KERNEL);
if (!evt->cache)
return ERR_PTR(-ENOMEM);
- evt->buf = dma_alloc_coherent(dwc->sysdev, length,
- &evt->dma, GFP_KERNEL);
+ evt->buf = dma_alloc_coherent(length,
+ (unsigned long *)&evt->dma);
if (!evt->buf)
return ERR_PTR(-ENOMEM);
@@ -817,13 +614,13 @@ static int dwc3_phy_init(struct dwc3 *dwc)
usb_phy_init(dwc->usb3_phy);
for (i = 0; i < dwc->num_usb2_ports; i++) {
- ret = phy_init(dwc->usb2_generic_phy[i]);
+ ret = generic_phy_init(dwc->usb2_generic_phy[i]);
if (ret < 0)
goto err_exit_usb2_phy;
}
for (j = 0; j < dwc->num_usb3_ports; j++) {
- ret = phy_init(dwc->usb3_generic_phy[j]);
+ ret = generic_phy_init(dwc->usb3_generic_phy[j]);
if (ret < 0)
goto err_exit_usb3_phy;
}
@@ -851,11 +648,11 @@ static int dwc3_phy_init(struct dwc3 *dwc)
err_exit_usb3_phy:
while (--j >= 0)
- phy_exit(dwc->usb3_generic_phy[j]);
+ generic_phy_exit(dwc->usb3_generic_phy[j]);
err_exit_usb2_phy:
while (--i >= 0)
- phy_exit(dwc->usb2_generic_phy[i]);
+ generic_phy_exit(dwc->usb2_generic_phy[i]);
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
@@ -868,10 +665,10 @@ static void dwc3_phy_exit(struct dwc3 *dwc)
int i;
for (i = 0; i < dwc->num_usb3_ports; i++)
- phy_exit(dwc->usb3_generic_phy[i]);
+ generic_phy_exit(dwc->usb3_generic_phy[i]);
for (i = 0; i < dwc->num_usb2_ports; i++)
- phy_exit(dwc->usb2_generic_phy[i]);
+ generic_phy_exit(dwc->usb2_generic_phy[i]);
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
@@ -887,13 +684,13 @@ static int dwc3_phy_power_on(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 0);
for (i = 0; i < dwc->num_usb2_ports; i++) {
- ret = phy_power_on(dwc->usb2_generic_phy[i]);
+ ret = generic_phy_power_on(dwc->usb2_generic_phy[i]);
if (ret < 0)
goto err_power_off_usb2_phy;
}
for (j = 0; j < dwc->num_usb3_ports; j++) {
- ret = phy_power_on(dwc->usb3_generic_phy[j]);
+ ret = generic_phy_power_on(dwc->usb3_generic_phy[j]);
if (ret < 0)
goto err_power_off_usb3_phy;
}
@@ -902,11 +699,11 @@ static int dwc3_phy_power_on(struct dwc3 *dwc)
err_power_off_usb3_phy:
while (--j >= 0)
- phy_power_off(dwc->usb3_generic_phy[j]);
+ generic_phy_power_off(dwc->usb3_generic_phy[j]);
err_power_off_usb2_phy:
while (--i >= 0)
- phy_power_off(dwc->usb2_generic_phy[i]);
+ generic_phy_power_off(dwc->usb2_generic_phy[i]);
usb_phy_set_suspend(dwc->usb3_phy, 1);
usb_phy_set_suspend(dwc->usb2_phy, 1);
@@ -919,52 +716,15 @@ static void dwc3_phy_power_off(struct dwc3 *dwc)
int i;
for (i = 0; i < dwc->num_usb3_ports; i++)
- phy_power_off(dwc->usb3_generic_phy[i]);
+ generic_phy_power_off(dwc->usb3_generic_phy[i]);
for (i = 0; i < dwc->num_usb2_ports; i++)
- phy_power_off(dwc->usb2_generic_phy[i]);
+ generic_phy_power_off(dwc->usb2_generic_phy[i]);
usb_phy_set_suspend(dwc->usb3_phy, 1);
usb_phy_set_suspend(dwc->usb2_phy, 1);
}
-static int dwc3_clk_enable(struct dwc3 *dwc)
-{
- int ret;
-
- ret = clk_prepare_enable(dwc->bus_clk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(dwc->ref_clk);
- if (ret)
- goto disable_bus_clk;
-
- ret = clk_prepare_enable(dwc->susp_clk);
- if (ret)
- goto disable_ref_clk;
-
- ret = clk_prepare_enable(dwc->utmi_clk);
- if (ret)
- goto disable_susp_clk;
-
- ret = clk_prepare_enable(dwc->pipe_clk);
- if (ret)
- goto disable_utmi_clk;
-
- return 0;
-
-disable_utmi_clk:
- clk_disable_unprepare(dwc->utmi_clk);
-disable_susp_clk:
- clk_disable_unprepare(dwc->susp_clk);
-disable_ref_clk:
- clk_disable_unprepare(dwc->ref_clk);
-disable_bus_clk:
- clk_disable_unprepare(dwc->bus_clk);
- return ret;
-}
-
static void dwc3_clk_disable(struct dwc3 *dwc)
{
clk_disable_unprepare(dwc->pipe_clk);
@@ -983,26 +743,6 @@ static void dwc3_core_exit(struct dwc3 *dwc)
reset_control_assert(dwc->reset);
}
-static bool dwc3_core_is_valid(struct dwc3 *dwc)
-{
- u32 reg;
-
- reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
- dwc->ip = DWC3_GSNPS_ID(reg);
-
- /* This should read as U3 followed by revision number */
- if (DWC3_IP_IS(DWC3)) {
- dwc->revision = reg;
- } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
- dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
- dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
- } else {
- return false;
- }
-
- return true;
-}
-
static void dwc3_core_setup_global_control(struct dwc3 *dwc)
{
unsigned int power_opt;
@@ -1090,13 +830,12 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
-static int dwc3_core_get_phy(struct dwc3 *dwc);
static int dwc3_core_ulpi_init(struct dwc3 *dwc);
/* set global incr burst type configuration registers */
static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
{
- struct device *dev = dwc->dev;
+ struct udevice *dev = dwc->dev;
/* incrx_mode : for INCR burst type. */
bool incrx_mode;
/* incrx_size : for size of INCRX burst. */
@@ -1337,9 +1076,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
/*
* Write Linux Version Code to our GUID register so it's easy to figure
- * out which kernel version a bug was found.
+ * out which U-Boot version a bug was found.
*/
- dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+ dwc3_writel(dwc->regs, DWC3_GUID, U_BOOT_VERSION_NUM);
ret = dwc3_phy_setup(dwc);
if (ret)
@@ -1357,13 +1096,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->ulpi_ready = true;
}
- if (!dwc->phys_ready) {
- ret = dwc3_core_get_phy(dwc);
- if (ret)
- goto err_exit_ulpi;
- dwc->phys_ready = true;
- }
-
ret = dwc3_phy_init(dwc);
if (ret)
goto err_exit_ulpi;
@@ -1528,78 +1260,17 @@ err_exit_ulpi:
return ret;
}
-static int dwc3_core_get_phy(struct dwc3 *dwc)
+int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
{
- struct device *dev = dwc->dev;
- struct device_node *node = dev->of_node;
- char phy_name[9];
- int ret;
- u8 i;
-
- if (node) {
- dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
- dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
- } else {
- dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
- dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
- }
-
- if (IS_ERR(dwc->usb2_phy)) {
- ret = PTR_ERR(dwc->usb2_phy);
- if (ret == -ENXIO || ret == -ENODEV)
- dwc->usb2_phy = NULL;
- else
- return dev_err_probe(dev, ret, "no usb2 phy configured\n");
- }
-
- if (IS_ERR(dwc->usb3_phy)) {
- ret = PTR_ERR(dwc->usb3_phy);
- if (ret == -ENXIO || ret == -ENODEV)
- dwc->usb3_phy = NULL;
- else
- return dev_err_probe(dev, ret, "no usb3 phy configured\n");
- }
-
- for (i = 0; i < dwc->num_usb2_ports; i++) {
- if (dwc->num_usb2_ports == 1)
- snprintf(phy_name, sizeof(phy_name), "usb2-phy");
- else
- snprintf(phy_name, sizeof(phy_name), "usb2-%u", i);
-
- dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name);
- if (IS_ERR(dwc->usb2_generic_phy[i])) {
- ret = PTR_ERR(dwc->usb2_generic_phy[i]);
- if (ret == -ENOSYS || ret == -ENODEV)
- dwc->usb2_generic_phy[i] = NULL;
- else
- return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
- phy_name);
- }
- }
-
- for (i = 0; i < dwc->num_usb3_ports; i++) {
- if (dwc->num_usb3_ports == 1)
- snprintf(phy_name, sizeof(phy_name), "usb3-phy");
- else
- snprintf(phy_name, sizeof(phy_name), "usb3-%u", i);
-
- dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name);
- if (IS_ERR(dwc->usb3_generic_phy[i])) {
- ret = PTR_ERR(dwc->usb3_generic_phy[i]);
- if (ret == -ENOSYS || ret == -ENODEV)
- dwc->usb3_generic_phy[i] = NULL;
- else
- return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
- phy_name);
- }
- }
-
- return 0;
+ /* TODO */
+ return -ENOSYS;
}
+#define phy_set_mode(phy, mode) \
+ phy_set_mode_ext(phy, mode, 0)
static int dwc3_core_init_mode(struct dwc3 *dwc)
{
- struct device *dev = dwc->dev;
+ struct udevice *dev = dwc->dev;
int ret;
int i;
@@ -1607,8 +1278,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
case USB_DR_MODE_PERIPHERAL:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false);
- if (dwc->usb2_phy)
- otg_set_vbus(dwc->usb2_phy->otg, false);
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
@@ -1619,8 +1288,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false);
- if (dwc->usb2_phy)
- otg_set_vbus(dwc->usb2_phy->otg, true);
for (i = 0; i < dwc->num_usb2_ports; i++)
phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
for (i = 0; i < dwc->num_usb3_ports; i++)
@@ -1644,6 +1311,14 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
return 0;
}
+static void dwc3_core_stop(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg & ~(DWC3_DCTL_RUN_STOP));
+}
+
static void dwc3_core_exit_mode(struct dwc3 *dwc)
{
switch (dwc->dr_mode) {
@@ -1665,46 +1340,109 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
}
-static void dwc3_get_software_properties(struct dwc3 *dwc)
+#define DWC3_ALIGN_MASK (16 - 1)
+
+
+/* check whether the core supports IMOD */
+bool dwc3_has_imod(struct dwc3 *dwc)
{
- struct device *tmpdev;
- u16 gsbuscfg0_reqinfo;
- int ret;
+ return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
+ DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
+ DWC3_IP_IS(DWC32);
+}
- dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+static int dwc3_get_num_ports(struct dwc3 *dwc)
+{
+ void __iomem *base;
+ u8 major_revision;
+ u32 offset;
+ u32 val;
/*
- * Iterate over all parent nodes for finding swnode properties
- * and non-DT (non-ABI) properties.
+ * Remap xHCI address space to access XHCI ext cap regs since it is
+ * needed to get information on number of ports present.
*/
- for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) {
- ret = device_property_read_u16(tmpdev,
- "snps,gsbuscfg0-reqinfo",
- &gsbuscfg0_reqinfo);
- if (!ret)
- dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo;
- }
+ base = ioremap(dwc->xhci_resources[0].start,
+ resource_size(&dwc->xhci_resources[0]));
+ if (!base)
+ return -ENOMEM;
+
+ offset = 0;
+ do {
+ offset = xhci_find_next_ext_cap(base, offset,
+ XHCI_EXT_CAPS_PROTOCOL);
+ if (!offset)
+ break;
+
+ val = readl(base + offset);
+ major_revision = XHCI_EXT_PORT_MAJOR(val);
+
+ val = readl(base + offset + 0x08);
+ if (major_revision == 0x03) {
+ dwc->num_usb3_ports += XHCI_EXT_PORT_COUNT(val);
+ } else if (major_revision <= 0x02) {
+ dwc->num_usb2_ports += XHCI_EXT_PORT_COUNT(val);
+ } else {
+ dev_warn(dwc->dev, "unrecognized port major revision %d\n",
+ major_revision);
+ }
+ } while (1);
+
+ dev_dbg(dwc->dev, "hs-ports: %u ss-ports: %u\n",
+ dwc->num_usb2_ports, dwc->num_usb3_ports);
+
+ iounmap(base);
+
+ if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS ||
+ dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS)
+ return -EINVAL;
+
+ return 0;
}
-static void dwc3_get_properties(struct dwc3 *dwc)
+#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB)
+int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys)
{
- struct device *dev = dwc->dev;
- u8 lpm_nyet_threshold;
- u8 tx_de_emphasis;
- u8 hird_threshold;
- u8 rx_thr_num_pkt = 0;
- u8 rx_max_burst = 0;
- u8 tx_thr_num_pkt = 0;
- u8 tx_max_burst = 0;
- u8 rx_thr_num_pkt_prd = 0;
- u8 rx_max_burst_prd = 0;
- u8 tx_thr_num_pkt_prd = 0;
- u8 tx_max_burst_prd = 0;
- u8 tx_fifo_resize_max_num;
- u16 num_hc_interrupters;
+ int ret;
+
+ ret = generic_phy_get_bulk(dev, phys);
+ if (ret)
+ return ret;
+
+ ret = generic_phy_init_bulk(phys);
+ if (ret)
+ return ret;
+
+ ret = generic_phy_power_on_bulk(phys);
+ if (ret)
+ generic_phy_exit_bulk(phys);
+
+ return ret;
+}
+
+int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys)
+{
+ int ret;
+
+ ret = generic_phy_power_off_bulk(phys);
+ ret |= generic_phy_exit_bulk(phys);
+ return ret;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_USB)
+void dwc3_of_parse(struct dwc3 *dwc)
+{
+ const u8 *tmp;
+ struct udevice *dev = dwc->dev;
+ u8 lpm_nyet_threshold;
+ u8 tx_de_emphasis;
+ u8 hird_threshold;
+ u32 val;
+ int i;
/* default to highest possible threshold */
- lpm_nyet_threshold = 0xf;
+ lpm_nyet_threshold = 0xff;
/* default to -3.5dB de-emphasis */
tx_de_emphasis = 1;
@@ -1715,538 +1453,89 @@ static void dwc3_get_properties(struct dwc3 *dwc)
*/
hird_threshold = 12;
- /*
- * default to a TXFIFO size large enough to fit 6 max packets. This
- * allows for systems with larger bus latencies to have some headroom
- * for endpoints that have a large bMaxBurst value.
- */
- tx_fifo_resize_max_num = 6;
-
- /* default to a single XHCI interrupter */
- num_hc_interrupters = 1;
-
- dwc->maximum_speed = usb_get_maximum_speed(dev);
- dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
- dwc->dr_mode = usb_get_dr_mode(dev);
- dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
-
- dwc->sysdev_is_parent = device_property_read_bool(dev,
- "linux,sysdev_is_parent");
- if (dwc->sysdev_is_parent)
- dwc->sysdev = dwc->dev->parent;
- else
- dwc->sysdev = dwc->dev;
-
- dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
+ dwc->hsphy_mode = usb_get_phy_mode(dev_ofnode(dev));
- dwc->has_lpm_erratum = device_property_read_bool(dev,
+ dwc->has_lpm_erratum = dev_read_bool(dev,
"snps,has-lpm-erratum");
- device_property_read_u8(dev, "snps,lpm-nyet-threshold",
- &lpm_nyet_threshold);
- dwc->is_utmi_l1_suspend = device_property_read_bool(dev,
+ tmp = dev_read_u8_array_ptr(dev, "snps,lpm-nyet-threshold", 1);
+ if (tmp)
+ lpm_nyet_threshold = *tmp;
+
+ dwc->is_utmi_l1_suspend = dev_read_bool(dev,
"snps,is-utmi-l1-suspend");
- device_property_read_u8(dev, "snps,hird-threshold",
- &hird_threshold);
- dwc->dis_start_transfer_quirk = device_property_read_bool(dev,
- "snps,dis-start-transfer-quirk");
- dwc->usb3_lpm_capable = device_property_read_bool(dev,
- "snps,usb3_lpm_capable");
- dwc->usb2_lpm_disable = device_property_read_bool(dev,
- "snps,usb2-lpm-disable");
- dwc->usb2_gadget_lpm_disable = device_property_read_bool(dev,
- "snps,usb2-gadget-lpm-disable");
- device_property_read_u8(dev, "snps,rx-thr-num-pkt",
- &rx_thr_num_pkt);
- device_property_read_u8(dev, "snps,rx-max-burst",
- &rx_max_burst);
- device_property_read_u8(dev, "snps,tx-thr-num-pkt",
- &tx_thr_num_pkt);
- device_property_read_u8(dev, "snps,tx-max-burst",
- &tx_max_burst);
- device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
- &rx_thr_num_pkt_prd);
- device_property_read_u8(dev, "snps,rx-max-burst-prd",
- &rx_max_burst_prd);
- device_property_read_u8(dev, "snps,tx-thr-num-pkt-prd",
- &tx_thr_num_pkt_prd);
- device_property_read_u8(dev, "snps,tx-max-burst-prd",
- &tx_max_burst_prd);
- device_property_read_u16(dev, "num-hc-interrupters",
- &num_hc_interrupters);
- /* DWC3 core allowed to have a max of 8 interrupters */
- if (num_hc_interrupters > 8)
- num_hc_interrupters = 8;
-
- dwc->do_fifo_resize = device_property_read_bool(dev,
- "tx-fifo-resize");
- if (dwc->do_fifo_resize)
- device_property_read_u8(dev, "tx-fifo-max-num",
- &tx_fifo_resize_max_num);
-
- dwc->disable_scramble_quirk = device_property_read_bool(dev,
+ tmp = dev_read_u8_array_ptr(dev, "snps,hird-threshold", 1);
+ if (tmp)
+ hird_threshold = *tmp;
+
+ dwc->disable_scramble_quirk = dev_read_bool(dev,
"snps,disable_scramble_quirk");
- dwc->u2exit_lfps_quirk = device_property_read_bool(dev,
+ dwc->u2exit_lfps_quirk = dev_read_bool(dev,
"snps,u2exit_lfps_quirk");
- dwc->u2ss_inp3_quirk = device_property_read_bool(dev,
+ dwc->u2ss_inp3_quirk = dev_read_bool(dev,
"snps,u2ss_inp3_quirk");
- dwc->req_p1p2p3_quirk = device_property_read_bool(dev,
+ dwc->req_p1p2p3_quirk = dev_read_bool(dev,
"snps,req_p1p2p3_quirk");
- dwc->del_p1p2p3_quirk = device_property_read_bool(dev,
+ dwc->del_p1p2p3_quirk = dev_read_bool(dev,
"snps,del_p1p2p3_quirk");
- dwc->del_phy_power_chg_quirk = device_property_read_bool(dev,
+ dwc->del_phy_power_chg_quirk = dev_read_bool(dev,
"snps,del_phy_power_chg_quirk");
- dwc->lfps_filter_quirk = device_property_read_bool(dev,
+ dwc->lfps_filter_quirk = dev_read_bool(dev,
"snps,lfps_filter_quirk");
- dwc->rx_detect_poll_quirk = device_property_read_bool(dev,
+ dwc->rx_detect_poll_quirk = dev_read_bool(dev,
"snps,rx_detect_poll_quirk");
- dwc->dis_u3_susphy_quirk = device_property_read_bool(dev,
+ dwc->dis_u3_susphy_quirk = dev_read_bool(dev,
"snps,dis_u3_susphy_quirk");
- dwc->dis_u2_susphy_quirk = device_property_read_bool(dev,
+ dwc->dis_u2_susphy_quirk = dev_read_bool(dev,
"snps,dis_u2_susphy_quirk");
- dwc->dis_enblslpm_quirk = device_property_read_bool(dev,
- "snps,dis_enblslpm_quirk");
- dwc->dis_u1_entry_quirk = device_property_read_bool(dev,
- "snps,dis-u1-entry-quirk");
- dwc->dis_u2_entry_quirk = device_property_read_bool(dev,
- "snps,dis-u2-entry-quirk");
- dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
- "snps,dis_rxdet_inp3_quirk");
- dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
- "snps,dis-u2-freeclk-exists-quirk");
- dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
+ dwc->dis_del_phy_power_chg_quirk = dev_read_bool(dev,
"snps,dis-del-phy-power-chg-quirk");
- dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
+ dwc->dis_tx_ipgap_linecheck_quirk = dev_read_bool(dev,
"snps,dis-tx-ipgap-linecheck-quirk");
- dwc->resume_hs_terminations = device_property_read_bool(dev,
- "snps,resume-hs-terminations");
- dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev,
- "snps,ulpi-ext-vbus-drv");
- dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
- "snps,parkmode-disable-ss-quirk");
- dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev,
- "snps,parkmode-disable-hs-quirk");
- dwc->gfladj_refclk_lpm_sel = device_property_read_bool(dev,
- "snps,gfladj-refclk-lpm-sel-quirk");
-
- dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
+ dwc->dis_enblslpm_quirk = dev_read_bool(dev,
+ "snps,dis_enblslpm_quirk");
+ dwc->dis_u2_freeclk_exists_quirk = dev_read_bool(dev,
+ "snps,dis-u2-freeclk-exists-quirk");
+ dwc->tx_de_emphasis_quirk = dev_read_bool(dev,
"snps,tx_de_emphasis_quirk");
- device_property_read_u8(dev, "snps,tx_de_emphasis",
- &tx_de_emphasis);
- device_property_read_string(dev, "snps,hsphy_interface",
- &dwc->hsphy_interface);
- device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
- &dwc->fladj);
- device_property_read_u32(dev, "snps,ref-clock-period-ns",
- &dwc->ref_clk_per);
-
- dwc->dis_metastability_quirk = device_property_read_bool(dev,
- "snps,dis_metastability_quirk");
-
- dwc->dis_split_quirk = device_property_read_bool(dev,
- "snps,dis-split-quirk");
+ tmp = dev_read_u8_array_ptr(dev, "snps,tx_de_emphasis", 1);
+ if (tmp)
+ tx_de_emphasis = *tmp;
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
- dwc->hird_threshold = hird_threshold;
-
- dwc->rx_thr_num_pkt = rx_thr_num_pkt;
- dwc->rx_max_burst = rx_max_burst;
-
- dwc->tx_thr_num_pkt = tx_thr_num_pkt;
- dwc->tx_max_burst = tx_max_burst;
-
- dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
- dwc->rx_max_burst_prd = rx_max_burst_prd;
-
- dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
- dwc->tx_max_burst_prd = tx_max_burst_prd;
-
- dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
-
- dwc->num_hc_interrupters = num_hc_interrupters;
-}
-
-/* check whether the core supports IMOD */
-bool dwc3_has_imod(struct dwc3 *dwc)
-{
- return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
- DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
- DWC3_IP_IS(DWC32);
-}
+ dwc->hird_threshold = hird_threshold
+ | (dwc->is_utmi_l1_suspend << 4);
-static void dwc3_check_params(struct dwc3 *dwc)
-{
- struct device *dev = dwc->dev;
- unsigned int hwparam_gen =
- DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
+ dev_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj);
/*
- * Enable IMOD for all supporting controllers.
- *
- * Particularly, DWC_usb3 v3.00a must enable this feature for
- * the following reason:
- *
- * Workaround for STAR 9000961433 which affects only version
- * 3.00a of the DWC_usb3 core. This prevents the controller
- * interrupt from being masked while handling events. IMOD
- * allows us to work around this issue. Enable it for the
- * affected version.
+ * Handle property "snps,incr-burst-type-adjustment".
+ * Get the number of value from this property:
+ * result <= 0, means this property is not supported.
+ * result = 1, means INCRx burst mode supported.
+ * result > 1, means undefined length burst mode supported.
*/
- if (dwc3_has_imod((dwc)))
- dwc->imod_interval = 1;
-
- /* Check the maximum_speed parameter */
- switch (dwc->maximum_speed) {
- case USB_SPEED_FULL:
- case USB_SPEED_HIGH:
- break;
- case USB_SPEED_SUPER:
- if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS)
- dev_warn(dev, "UDC doesn't support Gen 1\n");
- break;
- case USB_SPEED_SUPER_PLUS:
- if ((DWC3_IP_IS(DWC32) &&
- hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) ||
- (!DWC3_IP_IS(DWC32) &&
- hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
- dev_warn(dev, "UDC doesn't support SSP\n");
- break;
- default:
- dev_err(dev, "invalid maximum_speed parameter %d\n",
- dwc->maximum_speed);
- fallthrough;
- case USB_SPEED_UNKNOWN:
- switch (hwparam_gen) {
- case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
- dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
- break;
- case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
- if (DWC3_IP_IS(DWC32))
- dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
- else
- dwc->maximum_speed = USB_SPEED_SUPER;
- break;
- case DWC3_GHWPARAMS3_SSPHY_IFC_DIS:
- dwc->maximum_speed = USB_SPEED_HIGH;
- break;
- default:
- dwc->maximum_speed = USB_SPEED_SUPER;
+ dwc->incrx_mode = INCRX_BURST_MODE;
+ dwc->incrx_size = 0;
+ for (i = 0; i < 8; i++) {
+ if (dev_read_u32_index(dev, "snps,incr-burst-type-adjustment",
+ i, &val))
break;
- }
- break;
- }
- /*
- * Currently the controller does not have visibility into the HW
- * parameter to determine the maximum number of lanes the HW supports.
- * If the number of lanes is not specified in the device property, then
- * set the default to support dual-lane for DWC_usb32 and single-lane
- * for DWC_usb31 for super-speed-plus.
- */
- if (dwc->maximum_speed == USB_SPEED_SUPER_PLUS) {
- switch (dwc->max_ssp_rate) {
- case USB_SSP_GEN_2x1:
- if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_GEN1)
- dev_warn(dev, "UDC only supports Gen 1\n");
- break;
- case USB_SSP_GEN_1x2:
- case USB_SSP_GEN_2x2:
- if (DWC3_IP_IS(DWC31))
- dev_warn(dev, "UDC only supports single lane\n");
- break;
- case USB_SSP_GEN_UNKNOWN:
- default:
- switch (hwparam_gen) {
- case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
- if (DWC3_IP_IS(DWC32))
- dwc->max_ssp_rate = USB_SSP_GEN_2x2;
- else
- dwc->max_ssp_rate = USB_SSP_GEN_2x1;
- break;
- case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
- if (DWC3_IP_IS(DWC32))
- dwc->max_ssp_rate = USB_SSP_GEN_1x2;
- break;
- }
- break;
- }
+ dwc->incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE;
+ dwc->incrx_size = max(dwc->incrx_size, val);
}
}
-static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
-{
- struct device *dev = dwc->dev;
- struct device_node *np_phy;
- struct extcon_dev *edev = NULL;
- const char *name;
-
- if (device_property_present(dev, "extcon"))
- return extcon_get_edev_by_phandle(dev, 0);
-
- /*
- * Device tree platforms should get extcon via phandle.
- * On ACPI platforms, we get the name from a device property.
- * This device property is for kernel internal use only and
- * is expected to be set by the glue code.
- */
- if (device_property_read_string(dev, "linux,extcon-name", &name) == 0)
- return extcon_get_extcon_dev(name);
-
- /*
- * Check explicitly if "usb-role-switch" is used since
- * extcon_find_edev_by_node() can not be used to check the absence of
- * an extcon device. In the absence of an device it will always return
- * EPROBE_DEFER.
- */
- if (IS_ENABLED(CONFIG_USB_ROLE_SWITCH) &&
- device_property_read_bool(dev, "usb-role-switch"))
- return NULL;
-
- /*
- * Try to get an extcon device from the USB PHY controller's "port"
- * node. Check if it has the "port" node first, to avoid printing the
- * error message from underlying code, as it's a valid case: extcon
- * device (and "port" node) may be missing in case of "usb-role-switch"
- * or OTG mode.
- */
- np_phy = of_parse_phandle(dev->of_node, "phys", 0);
- if (of_graph_is_present(np_phy)) {
- struct device_node *np_conn;
-
- np_conn = of_graph_get_remote_node(np_phy, -1, -1);
- if (np_conn)
- edev = extcon_find_edev_by_node(np_conn);
- of_node_put(np_conn);
- }
- of_node_put(np_phy);
-
- return edev;
-}
-
-static int dwc3_get_clocks(struct dwc3 *dwc)
-{
- struct device *dev = dwc->dev;
-
- if (!dev->of_node)
- return 0;
-
- /*
- * Clocks are optional, but new DT platforms should support all clocks
- * as required by the DT-binding.
- * Some devices have different clock names in legacy device trees,
- * check for them to retain backwards compatibility.
- */
- dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
- if (IS_ERR(dwc->bus_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
- "could not get bus clock\n");
- }
-
- if (dwc->bus_clk == NULL) {
- dwc->bus_clk = devm_clk_get_optional(dev, "bus_clk");
- if (IS_ERR(dwc->bus_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
- "could not get bus clock\n");
- }
- }
-
- dwc->ref_clk = devm_clk_get_optional(dev, "ref");
- if (IS_ERR(dwc->ref_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
- "could not get ref clock\n");
- }
-
- if (dwc->ref_clk == NULL) {
- dwc->ref_clk = devm_clk_get_optional(dev, "ref_clk");
- if (IS_ERR(dwc->ref_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
- "could not get ref clock\n");
- }
- }
-
- dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
- if (IS_ERR(dwc->susp_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
- "could not get suspend clock\n");
- }
-
- if (dwc->susp_clk == NULL) {
- dwc->susp_clk = devm_clk_get_optional(dev, "suspend_clk");
- if (IS_ERR(dwc->susp_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
- "could not get suspend clock\n");
- }
- }
-
- /* specific to Rockchip RK3588 */
- dwc->utmi_clk = devm_clk_get_optional(dev, "utmi");
- if (IS_ERR(dwc->utmi_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->utmi_clk),
- "could not get utmi clock\n");
- }
-
- /* specific to Rockchip RK3588 */
- dwc->pipe_clk = devm_clk_get_optional(dev, "pipe");
- if (IS_ERR(dwc->pipe_clk)) {
- return dev_err_probe(dev, PTR_ERR(dwc->pipe_clk),
- "could not get pipe clock\n");
- }
-
- return 0;
-}
-
-static int dwc3_get_num_ports(struct dwc3 *dwc)
-{
- void __iomem *base;
- u8 major_revision;
- u32 offset;
- u32 val;
-
- /*
- * Remap xHCI address space to access XHCI ext cap regs since it is
- * needed to get information on number of ports present.
- */
- base = ioremap(dwc->xhci_resources[0].start,
- resource_size(&dwc->xhci_resources[0]));
- if (!base)
- return -ENOMEM;
-
- offset = 0;
- do {
- offset = xhci_find_next_ext_cap(base, offset,
- XHCI_EXT_CAPS_PROTOCOL);
- if (!offset)
- break;
-
- val = readl(base + offset);
- major_revision = XHCI_EXT_PORT_MAJOR(val);
-
- val = readl(base + offset + 0x08);
- if (major_revision == 0x03) {
- dwc->num_usb3_ports += XHCI_EXT_PORT_COUNT(val);
- } else if (major_revision <= 0x02) {
- dwc->num_usb2_ports += XHCI_EXT_PORT_COUNT(val);
- } else {
- dev_warn(dwc->dev, "unrecognized port major revision %d\n",
- major_revision);
- }
- } while (1);
-
- dev_dbg(dwc->dev, "hs-ports: %u ss-ports: %u\n",
- dwc->num_usb2_ports, dwc->num_usb3_ports);
-
- iounmap(base);
-
- if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS ||
- dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS)
- return -EINVAL;
-
- return 0;
-}
-
-static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
+int dwc3_init(struct dwc3 *dwc)
{
- struct power_supply *usb_psy;
- const char *usb_psy_name;
+ unsigned int hw_mode;
int ret;
+ u32 reg;
- ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name);
- if (ret < 0)
- return NULL;
-
- usb_psy = power_supply_get_by_name(usb_psy_name);
- if (!usb_psy)
- return ERR_PTR(-EPROBE_DEFER);
-
- return usb_psy;
-}
-
-int dwc3_core_probe(const struct dwc3_probe_data *data)
-{
- struct dwc3 *dwc = data->dwc;
- struct device *dev = dwc->dev;
- struct resource dwc_res;
- unsigned int hw_mode;
- void __iomem *regs;
- struct resource *res = data->res;
- int ret;
-
- dwc->xhci_resources[0].start = res->start;
- dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
- DWC3_XHCI_REGS_END;
- dwc->xhci_resources[0].flags = res->flags;
- dwc->xhci_resources[0].name = res->name;
-
- /*
- * Request memory region but exclude xHCI regs,
- * since it will be requested by the xhci-plat driver.
- */
- dwc_res = *res;
- dwc_res.start += DWC3_GLOBALS_REGS_START;
-
- if (dev->of_node) {
- struct device_node *parent = of_get_parent(dev->of_node);
-
- if (of_device_is_compatible(parent, "realtek,rtd-dwc3")) {
- dwc_res.start -= DWC3_GLOBALS_REGS_START;
- dwc_res.start += DWC3_RTK_RTD_GLOBALS_REGS_START;
- }
-
- of_node_put(parent);
- }
-
- regs = devm_ioremap_resource(dev, &dwc_res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- dwc->regs = regs;
- dwc->regs_size = resource_size(&dwc_res);
-
- dwc3_get_properties(dwc);
-
- dwc3_get_software_properties(dwc);
-
- dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
- if (IS_ERR(dwc->usb_psy))
- return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
-
- if (!data->ignore_clocks_and_resets) {
- dwc->reset = devm_reset_control_array_get_optional_shared(dev);
- if (IS_ERR(dwc->reset)) {
- ret = PTR_ERR(dwc->reset);
- goto err_put_psy;
- }
-
- ret = dwc3_get_clocks(dwc);
- if (ret)
- goto err_put_psy;
- }
-
- ret = reset_control_deassert(dwc->reset);
- if (ret)
- goto err_put_psy;
-
- ret = dwc3_clk_enable(dwc);
- if (ret)
- goto err_assert_reset;
-
- if (!dwc3_core_is_valid(dwc)) {
- dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
- ret = -ENODEV;
- goto err_disable_clks;
- }
-
- dev_set_drvdata(dev, dwc);
dwc3_cache_hwparams(dwc);
- if (!dwc->sysdev_is_parent &&
- DWC3_GHWPARAMS0_AWIDTH(dwc->hwparams.hwparams0) == 64) {
- ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
- if (ret)
- goto err_disable_clks;
- }
-
/*
* Currently only DWC3 controllers that are host-only capable
* can have more than one port.
@@ -2255,7 +1544,7 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
ret = dwc3_get_num_ports(dwc);
if (ret)
- goto err_disable_clks;
+ return ret;
} else {
dwc->num_usb2_ports = 1;
dwc->num_usb3_ports = 1;
@@ -2264,551 +1553,275 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
spin_lock_init(&dwc->lock);
mutex_init(&dwc->mutex);
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
- pm_runtime_enable(dev);
-
- pm_runtime_forbid(dev);
-
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
- ret = -ENOMEM;
- goto err_allow_rpm;
- }
-
- dwc->edev = dwc3_get_extcon(dwc);
- if (IS_ERR(dwc->edev)) {
- ret = dev_err_probe(dwc->dev, PTR_ERR(dwc->edev), "failed to get extcon\n");
- goto err_free_event_buffers;
+ return -ENOMEM;
}
- ret = dwc3_get_dr_mode(dwc);
- if (ret)
- goto err_free_event_buffers;
-
ret = dwc3_core_init(dwc);
if (ret) {
- dev_err_probe(dev, ret, "failed to initialize core\n");
- goto err_free_event_buffers;
+ dev_err(dwc->dev, "failed to initialize core\n");
+ goto core_fail;
}
- dwc3_check_params(dwc);
- dwc3_debugfs_init(dwc);
-
- ret = dwc3_core_init_mode(dwc);
- if (ret)
- goto err_exit_debugfs;
-
- pm_runtime_put(dev);
-
- dma_set_max_seg_size(dev, UINT_MAX);
-
- return 0;
+ ret = dwc3_event_buffers_setup(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
+ goto event_fail;
+ }
-err_exit_debugfs:
- dwc3_debugfs_exit(dwc);
- dwc3_event_buffers_cleanup(dwc);
- dwc3_phy_power_off(dwc);
- dwc3_phy_exit(dwc);
- dwc3_ulpi_exit(dwc);
-err_free_event_buffers:
- dwc3_free_event_buffers(dwc);
-err_allow_rpm:
- pm_runtime_allow(dev);
- pm_runtime_disable(dev);
- pm_runtime_dont_use_autosuspend(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_put_noidle(dev);
-err_disable_clks:
- dwc3_clk_disable(dwc);
-err_assert_reset:
- reset_control_assert(dwc->reset);
-err_put_psy:
- if (dwc->usb_psy)
- power_supply_put(dwc->usb_psy);
+ if (dwc->revision >= DWC3_REVISION_250A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
- return ret;
-}
-EXPORT_SYMBOL_GPL(dwc3_core_probe);
+ /*
+ * Enable hardware control of sending remote wakeup
+ * in HS when the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A)
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
-static int dwc3_probe(struct platform_device *pdev)
-{
- struct dwc3_probe_data probe_data = {};
- struct resource *res;
- struct dwc3 *dwc;
+ if (dwc->dis_tx_ipgap_linecheck_quirk)
+ reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "missing memory resource\n");
- return -ENODEV;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
- dwc = devm_kzalloc(&pdev->dev, sizeof(*dwc), GFP_KERNEL);
- if (!dwc)
- return -ENOMEM;
+ if (dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
- dwc->dev = &pdev->dev;
+ reg |= DWC3_GUCTL_HSTINAUTORETRY;
- probe_data.dwc = dwc;
- probe_data.res = res;
+ dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+ }
- return dwc3_core_probe(&probe_data);
-}
+ ret = dwc3_core_init_mode(dwc);
+ if (ret)
+ goto mode_fail;
-void dwc3_core_remove(struct dwc3 *dwc)
-{
- pm_runtime_get_sync(dwc->dev);
+ return 0;
- dwc3_core_exit_mode(dwc);
- dwc3_debugfs_exit(dwc);
+mode_fail:
+ dwc3_event_buffers_cleanup(dwc);
+event_fail:
dwc3_core_exit(dwc);
- dwc3_ulpi_exit(dwc);
-
- pm_runtime_allow(dwc->dev);
- pm_runtime_disable(dwc->dev);
- pm_runtime_dont_use_autosuspend(dwc->dev);
- pm_runtime_put_noidle(dwc->dev);
- /*
- * HACK: Clear the driver data, which is currently accessed by parent
- * glue drivers, before allowing the parent to suspend.
- */
- dev_set_drvdata(dwc->dev, NULL);
- pm_runtime_set_suspended(dwc->dev);
+core_fail:
dwc3_free_event_buffers(dwc);
- if (dwc->usb_psy)
- power_supply_put(dwc->usb_psy);
+ return ret;
}
-EXPORT_SYMBOL_GPL(dwc3_core_remove);
-static void dwc3_remove(struct platform_device *pdev)
+void dwc3_remove(struct dwc3 *dwc)
{
- dwc3_core_remove(platform_get_drvdata(pdev));
+ dwc3_core_exit_mode(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_free_event_buffers(dwc);
+ dwc3_core_stop(dwc);
+ dwc3_core_exit(dwc);
}
+#endif
-#ifdef CONFIG_PM
-static int dwc3_core_init_for_resume(struct dwc3 *dwc)
+/**
+ * dwc3_uboot_init - dwc3 core uboot initialization code
+ * @dwc3_dev: struct dwc3_device containing initialization data
+ *
+ * Entry point for dwc3 driver (equivalent to dwc3_probe in linux
+ * kernel driver). Pointer to dwc3_device should be passed containing
+ * base address and other initialization data. Returns '0' on success and
+ * a negative value on failure.
+ *
+ * Generally called from board_usb_init() implemented in board file.
+ */
+int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
{
- int ret;
-
- ret = reset_control_deassert(dwc->reset);
- if (ret)
- return ret;
-
- ret = dwc3_clk_enable(dwc);
- if (ret)
- goto assert_reset;
-
- ret = dwc3_core_init(dwc);
- if (ret)
- goto disable_clks;
-
- return 0;
-
-disable_clks:
- dwc3_clk_disable(dwc);
-assert_reset:
- reset_control_assert(dwc->reset);
-
- return ret;
-}
+ struct dwc3 *dwc;
+ struct device *dev = NULL;
+ u8 lpm_nyet_threshold;
+ u8 tx_de_emphasis;
+ u8 hird_threshold;
-static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
-{
- u32 reg;
- int i;
- int ret;
+ int ret;
- if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
- dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
- DWC3_GUSB2PHYCFG_SUSPHY) ||
- (dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) &
- DWC3_GUSB3PIPECTL_SUSPHY);
- /*
- * TI AM62 platform requires SUSPHY to be
- * enabled for system suspend to work.
- */
- if (!dwc->susphy_state)
- dwc3_enable_susphy(dwc, true);
- }
+ void *mem;
- switch (dwc->current_dr_role) {
- case DWC3_GCTL_PRTCAP_DEVICE:
- if (pm_runtime_suspended(dwc->dev))
- break;
- ret = dwc3_gadget_suspend(dwc);
- if (ret)
- return ret;
- synchronize_irq(dwc->irq_gadget);
- dwc3_core_exit(dwc);
- break;
- case DWC3_GCTL_PRTCAP_HOST:
- if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
- dwc3_core_exit(dwc);
- break;
- }
+ mem = devm_kzalloc((struct udevice *)dev,
+ sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
- /* Let controller to suspend HSPHY before PHY driver suspends */
- if (dwc->dis_u2_susphy_quirk ||
- dwc->dis_enblslpm_quirk) {
- for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
- reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
- DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
- }
+ dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
+ dwc->mem = mem;
- /* Give some time for USB2 PHY to suspend */
- usleep_range(5000, 6000);
- }
-
- for (i = 0; i < dwc->num_usb2_ports; i++)
- phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]);
- for (i = 0; i < dwc->num_usb3_ports; i++)
- phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]);
- break;
- case DWC3_GCTL_PRTCAP_OTG:
- /* do nothing during runtime_suspend */
- if (PMSG_IS_AUTO(msg))
- break;
+ dwc->regs = (void *)(uintptr_t)(dwc3_dev->base +
+ DWC3_GLOBALS_REGS_START);
- if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- ret = dwc3_gadget_suspend(dwc);
- if (ret)
- return ret;
- synchronize_irq(dwc->irq_gadget);
- }
+ /* default to highest possible threshold */
+ lpm_nyet_threshold = 0xff;
- dwc3_otg_exit(dwc);
- dwc3_core_exit(dwc);
- break;
- default:
- /* do nothing */
- break;
- }
+ /* default to -3.5dB de-emphasis */
+ tx_de_emphasis = 1;
- return 0;
-}
+ /*
+ * default to assert utmi_sleep_n and use maximum allowed HIRD
+ * threshold value of 0b1100
+ */
+ hird_threshold = 12;
-static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
-{
- int ret;
- u32 reg;
- int i;
+ dwc->maximum_speed = dwc3_dev->maximum_speed;
+ dwc->has_lpm_erratum = dwc3_dev->has_lpm_erratum;
+ if (dwc3_dev->lpm_nyet_threshold)
+ lpm_nyet_threshold = dwc3_dev->lpm_nyet_threshold;
+ dwc->is_utmi_l1_suspend = dwc3_dev->is_utmi_l1_suspend;
+ if (dwc3_dev->hird_threshold)
+ hird_threshold = dwc3_dev->hird_threshold;
+
+ dwc->do_fifo_resize = dwc3_dev->tx_fifo_resize;
+ dwc->dr_mode = dwc3_dev->dr_mode;
+
+ dwc->disable_scramble_quirk = dwc3_dev->disable_scramble_quirk;
+ dwc->u2exit_lfps_quirk = dwc3_dev->u2exit_lfps_quirk;
+ dwc->u2ss_inp3_quirk = dwc3_dev->u2ss_inp3_quirk;
+ dwc->req_p1p2p3_quirk = dwc3_dev->req_p1p2p3_quirk;
+ dwc->del_p1p2p3_quirk = dwc3_dev->del_p1p2p3_quirk;
+ dwc->del_phy_power_chg_quirk = dwc3_dev->del_phy_power_chg_quirk;
+ dwc->lfps_filter_quirk = dwc3_dev->lfps_filter_quirk;
+ dwc->rx_detect_poll_quirk = dwc3_dev->rx_detect_poll_quirk;
+ dwc->dis_u3_susphy_quirk = dwc3_dev->dis_u3_susphy_quirk;
+ dwc->dis_u2_susphy_quirk = dwc3_dev->dis_u2_susphy_quirk;
+ dwc->dis_del_phy_power_chg_quirk = dwc3_dev->dis_del_phy_power_chg_quirk;
+ dwc->dis_tx_ipgap_linecheck_quirk = dwc3_dev->dis_tx_ipgap_linecheck_quirk;
+ dwc->dis_enblslpm_quirk = dwc3_dev->dis_enblslpm_quirk;
+ dwc->dis_u2_freeclk_exists_quirk = dwc3_dev->dis_u2_freeclk_exists_quirk;
+
+ dwc->tx_de_emphasis_quirk = dwc3_dev->tx_de_emphasis_quirk;
+ if (dwc3_dev->tx_de_emphasis)
+ tx_de_emphasis = dwc3_dev->tx_de_emphasis;
+
+ /* default to superspeed if no maximum_speed passed */
+ if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
+ dwc->maximum_speed = USB_SPEED_SUPER;
- switch (dwc->current_dr_role) {
- case DWC3_GCTL_PRTCAP_DEVICE:
- ret = dwc3_core_init_for_resume(dwc);
- if (ret)
- return ret;
+ dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+ dwc->tx_de_emphasis = tx_de_emphasis;
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
- dwc3_gadget_resume(dwc);
- break;
- case DWC3_GCTL_PRTCAP_HOST:
- if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
- ret = dwc3_core_init_for_resume(dwc);
- if (ret)
- return ret;
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true);
- break;
- }
- /* Restore GUSB2PHYCFG bits that were modified in suspend */
- for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
- if (dwc->dis_u2_susphy_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc->hird_threshold = hird_threshold
+ | (dwc->is_utmi_l1_suspend << 4);
- if (dwc->dis_enblslpm_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ dwc->hsphy_mode = dwc3_dev->hsphy_mode;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
- }
+ dwc->index = dwc3_dev->index;
- for (i = 0; i < dwc->num_usb2_ports; i++)
- phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]);
- for (i = 0; i < dwc->num_usb3_ports; i++)
- phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]);
- break;
- case DWC3_GCTL_PRTCAP_OTG:
- /* nothing to do on runtime_resume */
- if (PMSG_IS_AUTO(msg))
- break;
+ dwc3_cache_hwparams(dwc);
- ret = dwc3_core_init_for_resume(dwc);
- if (ret)
- return ret;
+ ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
+ if (ret) {
+ dev_err(dwc->dev, "failed to allocate event buffers\n");
+ return -ENOMEM;
+ }
- dwc3_set_prtcap(dwc, dwc->current_dr_role, true);
+ if (!IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ dwc->dr_mode = USB_DR_MODE_HOST;
+ else if (!IS_ENABLED(CONFIG_USB_HOST))
+ dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
- dwc3_otg_init(dwc);
- if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
- dwc3_otg_host_init(dwc);
- } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- dwc3_gadget_resume(dwc);
- }
+ if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+ dwc->dr_mode = USB_DR_MODE_OTG;
- break;
- default:
- /* do nothing */
- break;
+ ret = dwc3_core_init(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to initialize core\n");
+ goto err0;
}
- if (!PMSG_IS_AUTO(msg)) {
- /* restore SUSPHY state to that before system suspend. */
- dwc3_enable_susphy(dwc, dwc->susphy_state);
+ ret = dwc3_event_buffers_setup(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
+ goto err1;
}
- return 0;
-}
+ ret = dwc3_core_init_mode(dwc);
+ if (ret)
+ goto err2;
-static int dwc3_runtime_checks(struct dwc3 *dwc)
-{
- switch (dwc->current_dr_role) {
- case DWC3_GCTL_PRTCAP_DEVICE:
- if (dwc->connected)
- return -EBUSY;
- break;
- case DWC3_GCTL_PRTCAP_HOST:
- default:
- /* do nothing */
- break;
- }
+ list_add_tail(&dwc->list, &dwc3_list);
return 0;
-}
-int dwc3_runtime_suspend(struct dwc3 *dwc)
-{
- int ret;
+err2:
+ dwc3_event_buffers_cleanup(dwc);
- if (dwc3_runtime_checks(dwc))
- return -EBUSY;
+err1:
+ dwc3_core_exit(dwc);
- ret = dwc3_suspend_common(dwc, PMSG_AUTO_SUSPEND);
- if (ret)
- return ret;
+err0:
+ dwc3_free_event_buffers(dwc);
- return 0;
+ return ret;
}
-EXPORT_SYMBOL_GPL(dwc3_runtime_suspend);
-int dwc3_runtime_resume(struct dwc3 *dwc)
+/**
+ * dwc3_uboot_exit - dwc3 core uboot cleanup code
+ * @index: index of this controller
+ *
+ * Performs cleanup of memory allocated in dwc3_uboot_init and other misc
+ * cleanups (equivalent to dwc3_remove in linux). index of _this_ controller
+ * should be passed and should match with the index passed in
+ * dwc3_device during init.
+ *
+ * Generally called from board file.
+ */
+void dwc3_uboot_exit(int index)
{
- struct device *dev = dwc->dev;
- int ret;
-
- ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
- if (ret)
- return ret;
-
- switch (dwc->current_dr_role) {
- case DWC3_GCTL_PRTCAP_DEVICE:
- if (dwc->pending_events) {
- pm_runtime_put(dev);
- dwc->pending_events = false;
- enable_irq(dwc->irq_gadget);
- }
- break;
- case DWC3_GCTL_PRTCAP_HOST:
- default:
- /* do nothing */
- break;
- }
-
- pm_runtime_mark_last_busy(dev);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dwc3_runtime_resume);
+ struct dwc3 *dwc;
-int dwc3_runtime_idle(struct dwc3 *dwc)
-{
- struct device *dev = dwc->dev;
+ list_for_each_entry(dwc, &dwc3_list, list) {
+ if (dwc->index != index)
+ continue;
- switch (dwc->current_dr_role) {
- case DWC3_GCTL_PRTCAP_DEVICE:
- if (dwc3_runtime_checks(dwc))
- return -EBUSY;
- break;
- case DWC3_GCTL_PRTCAP_HOST:
- default:
- /* do nothing */
+ dwc3_core_exit_mode(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_free_event_buffers(dwc);
+ dwc3_core_exit(dwc);
+ list_del(&dwc->list);
+ kfree(dwc->mem);
break;
}
-
- pm_runtime_mark_last_busy(dev);
- pm_runtime_autosuspend(dev);
-
- return 0;
}
-EXPORT_SYMBOL_GPL(dwc3_runtime_idle);
-static int dwc3_plat_runtime_suspend(struct device *dev)
-{
- return dwc3_runtime_suspend(dev_get_drvdata(dev));
-}
-static int dwc3_plat_runtime_resume(struct device *dev)
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+__weak int dwc3_uboot_interrupt_status(struct udevice *dev)
{
- return dwc3_runtime_resume(dev_get_drvdata(dev));
+ return 1;
}
-static int dwc3_plat_runtime_idle(struct device *dev)
-{
- return dwc3_runtime_idle(dev_get_drvdata(dev));
-}
-#endif /* CONFIG_PM */
-
-#ifdef CONFIG_PM_SLEEP
-int dwc3_pm_suspend(struct dwc3 *dwc)
-{
- struct device *dev = dwc->dev;
- int ret;
-
- ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
- if (ret)
- return ret;
-
- pinctrl_pm_select_sleep_state(dev);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dwc3_pm_suspend);
-
-int dwc3_pm_resume(struct dwc3 *dwc)
+/**
+ * dm_usb_gadget_handle_interrupts - handle dwc3 core interrupt
+ * @dev: device of this controller
+ *
+ * Invokes dwc3 gadget interrupts.
+ *
+ * Generally called from board file.
+ */
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
{
- struct device *dev = dwc->dev;
- int ret = 0;
-
- pinctrl_pm_select_default_state(dev);
-
- pm_runtime_disable(dev);
- ret = pm_runtime_set_active(dev);
- if (ret)
- goto out;
-
- ret = dwc3_resume_common(dwc, PMSG_RESUME);
- if (ret)
- pm_runtime_set_suspended(dev);
+ struct dwc3 *dwc = NULL;
-out:
- pm_runtime_enable(dev);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(dwc3_pm_resume);
+ if (!dwc3_uboot_interrupt_status(dev))
+ return 0;
-void dwc3_pm_complete(struct dwc3 *dwc)
-{
- u32 reg;
+ list_for_each_entry(dwc, &dwc3_list, list) {
+ if (dwc->dev != dev)
+ continue;
- if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
- dwc->dis_split_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
- reg |= DWC3_GUCTL3_SPLITDISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_gadget_uboot_handle_interrupt(dwc);
+ break;
}
-}
-EXPORT_SYMBOL_GPL(dwc3_pm_complete);
-
-int dwc3_pm_prepare(struct dwc3 *dwc)
-{
- struct device *dev = dwc->dev;
-
- /*
- * Indicate to the PM core that it may safely leave the device in
- * runtime suspend if runtime-suspended already in device mode.
- */
- if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE &&
- pm_runtime_suspended(dev) &&
- !dev_pinctrl(dev))
- return 1;
return 0;
}
-EXPORT_SYMBOL_GPL(dwc3_pm_prepare);
-
-static int dwc3_plat_suspend(struct device *dev)
-{
- return dwc3_pm_suspend(dev_get_drvdata(dev));
-}
-
-static int dwc3_plat_resume(struct device *dev)
-{
- return dwc3_pm_resume(dev_get_drvdata(dev));
-}
-
-static void dwc3_plat_complete(struct device *dev)
-{
- dwc3_pm_complete(dev_get_drvdata(dev));
-}
-
-static int dwc3_plat_prepare(struct device *dev)
-{
- return dwc3_pm_prepare(dev_get_drvdata(dev));
-}
-#else
-#define dwc3_plat_complete NULL
-#define dwc3_plat_prepare NULL
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct dev_pm_ops dwc3_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_plat_suspend, dwc3_plat_resume)
- .complete = dwc3_plat_complete,
- .prepare = dwc3_plat_prepare,
- /*
- * Runtime suspend halts the controller on disconnection. It relies on
- * platforms with custom connection notification to start the controller
- * again.
- */
- SET_RUNTIME_PM_OPS(dwc3_plat_runtime_suspend, dwc3_plat_runtime_resume,
- dwc3_plat_runtime_idle)
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id of_dwc3_match[] = {
- {
- .compatible = "snps,dwc3"
- },
- {
- .compatible = "synopsys,dwc3"
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, of_dwc3_match);
-#endif
-
-#ifdef CONFIG_ACPI
-
-#define ACPI_ID_INTEL_BSW "808622B7"
-
-static const struct acpi_device_id dwc3_acpi_match[] = {
- { ACPI_ID_INTEL_BSW, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
#endif
-static struct platform_driver dwc3_driver = {
- .probe = dwc3_probe,
- .remove = dwc3_remove,
- .driver = {
- .name = "dwc3",
- .of_match_table = of_match_ptr(of_dwc3_match),
- .acpi_match_table = ACPI_PTR(dwc3_acpi_match),
- .pm = &dwc3_dev_pm_ops,
- },
-};
-
-module_platform_driver(dwc3_driver);
-
-MODULE_ALIAS("platform:dwc3");
-MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index d5b985fa12f4..1b9fca02b4cf 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -11,27 +11,13 @@
#ifndef __DRIVERS_USB_DWC3_CORE_H
#define __DRIVERS_USB_DWC3_CORE_H
-#include <linux/device.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
#include <linux/ioport.h>
-#include <linux/list.h>
#include <linux/bitops.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/debugfs.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
-#include <linux/usb/role.h>
-#include <linux/ulpi/interface.h>
-
-#include <linux/phy/phy.h>
-
-#include <linux/power_supply.h>
+#include <linux/usb/phy.h>
/*
* DWC3 Multiport controllers support up to 15 High-Speed PHYs
@@ -418,6 +404,7 @@
/* Global User Control Register*/
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
#define DWC3_GUCTL_REFCLKPER_SEL 22
+#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
@@ -744,7 +731,6 @@ struct dwc3_event_buffer {
*/
struct dwc3_ep {
struct usb_ep endpoint;
- struct delayed_work nostream_work;
struct list_head cancelled_list;
struct list_head pending_list;
struct list_head started_list;
@@ -1177,7 +1163,6 @@ struct dwc3 {
dma_addr_t ep0_trb_addr;
dma_addr_t bounce_addr;
struct dwc3_request ep0_usb_req;
- struct completion ep0_in_setup;
/* device lock */
spinlock_t lock;
@@ -1185,8 +1170,11 @@ struct dwc3 {
/* mode switching lock */
struct mutex mutex;
+#if defined(__UBOOT__) && CONFIG_IS_ENABLED(DM_USB)
+ struct udevice *dev;
+#else
struct device *dev;
- struct device *sysdev;
+#endif
struct platform_device *xhci;
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
@@ -1194,7 +1182,7 @@ struct dwc3 {
struct dwc3_event_buffer *ev_buf;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
- struct usb_gadget *gadget;
+ struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
struct clk *bus_clk;
@@ -1231,9 +1219,9 @@ struct dwc3 {
struct usb_role_switch *role_sw;
enum usb_dr_mode role_switch_default_mode;
- struct power_supply *usb_psy;
-
u32 fladj;
+ u8 incrx_mode;
+ u32 incrx_size;
u32 ref_clk_per;
u32 irq_gadget;
u32 otg_irq;
@@ -1315,6 +1303,8 @@ struct dwc3 {
u8 num_eps;
+ void *mem;
+
struct dwc3_hwparams hwparams;
struct debugfs_regset32 *regset;
@@ -1400,6 +1390,9 @@ struct dwc3 {
struct dentry *debug_root;
u32 gsbuscfg0_reqinfo;
u32 wakeup_pending_funcs;
+
+ int index;
+ struct list_head list;
};
#define INCRX_BURST_MODE 0
@@ -1565,8 +1558,10 @@ struct dwc3_gadget_ep_cmd_params {
/* prototypes */
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
-void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
+void dwc3_of_parse(struct dwc3 *dwc);
+int dwc3_init(struct dwc3 *dwc);
+void dwc3_remove(struct dwc3 *dwc);
#define DWC3_IP_IS(_ip) \
(dwc->ip == _ip##_IP)
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 9db8f3ca493d..99519602eb2c 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -1,129 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * dwc3-am62.c - TI specific Glue layer for AM62 DWC3 USB Controller
- *
- * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com
+ * TI AM62 specific glue layer for DWC3
*/
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/syscon.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/pm_runtime.h>
-#include <linux/clk.h>
-#include <linux/regmap.h>
-#include <linux/pinctrl/consumer.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
-#include "core.h"
+#include "dwc3-generic.h"
-/* USB WRAPPER register offsets */
-#define USBSS_PID 0x0
-#define USBSS_OVERCURRENT_CTRL 0x4
-#define USBSS_PHY_CONFIG 0x8
-#define USBSS_PHY_TEST 0xc
-#define USBSS_CORE_STAT 0x14
-#define USBSS_HOST_VBUS_CTRL 0x18
#define USBSS_MODE_CONTROL 0x1c
-#define USBSS_WAKEUP_CONFIG 0x30
-#define USBSS_WAKEUP_STAT 0x34
-#define USBSS_OVERRIDE_CONFIG 0x38
-#define USBSS_IRQ_MISC_STATUS_RAW 0x430
-#define USBSS_IRQ_MISC_STATUS 0x434
-#define USBSS_IRQ_MISC_ENABLE_SET 0x438
-#define USBSS_IRQ_MISC_ENABLE_CLR 0x43c
-#define USBSS_IRQ_MISC_EOI 0x440
-#define USBSS_INTR_TEST 0x490
-#define USBSS_VBUS_FILTER 0x614
-#define USBSS_VBUS_STAT 0x618
-#define USBSS_DEBUG_CFG 0x708
-#define USBSS_DEBUG_DATA 0x70c
-#define USBSS_HOST_HUB_CTRL 0x714
-
-/* PHY CONFIG register bits */
+#define USBSS_PHY_CONFIG 0x8
#define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1)
#define USBSS_PHY_VBUS_SEL_SHIFT 1
-#define USBSS_PHY_LANE_REVERSE BIT(0)
-
-/* CORE STAT register bits */
-#define USBSS_CORE_OPERATIONAL_MODE_MASK GENMASK(13, 12)
-#define USBSS_CORE_OPERATIONAL_MODE_SHIFT 12
-
-/* MODE CONTROL register bits */
#define USBSS_MODE_VALID BIT(0)
-
-/* WAKEUP CONFIG register bits */
-#define USBSS_WAKEUP_CFG_OVERCURRENT_EN BIT(3)
-#define USBSS_WAKEUP_CFG_LINESTATE_EN BIT(2)
-#define USBSS_WAKEUP_CFG_SESSVALID_EN BIT(1)
-#define USBSS_WAKEUP_CFG_VBUSVALID_EN BIT(0)
-
-#define USBSS_WAKEUP_CFG_ALL (USBSS_WAKEUP_CFG_VBUSVALID_EN | \
- USBSS_WAKEUP_CFG_SESSVALID_EN | \
- USBSS_WAKEUP_CFG_LINESTATE_EN | \
- USBSS_WAKEUP_CFG_OVERCURRENT_EN)
-
-#define USBSS_WAKEUP_CFG_NONE 0
-
-/* WAKEUP STAT register bits */
-#define USBSS_WAKEUP_STAT_OVERCURRENT BIT(4)
-#define USBSS_WAKEUP_STAT_LINESTATE BIT(3)
-#define USBSS_WAKEUP_STAT_SESSVALID BIT(2)
-#define USBSS_WAKEUP_STAT_VBUSVALID BIT(1)
-#define USBSS_WAKEUP_STAT_CLR BIT(0)
-
-/* IRQ_MISC_STATUS_RAW register bits */
-#define USBSS_IRQ_MISC_RAW_VBUSVALID BIT(22)
-#define USBSS_IRQ_MISC_RAW_SESSVALID BIT(20)
-
-/* IRQ_MISC_STATUS register bits */
-#define USBSS_IRQ_MISC_VBUSVALID BIT(22)
-#define USBSS_IRQ_MISC_SESSVALID BIT(20)
-
-/* IRQ_MISC_ENABLE_SET register bits */
-#define USBSS_IRQ_MISC_ENABLE_SET_VBUSVALID BIT(22)
-#define USBSS_IRQ_MISC_ENABLE_SET_SESSVALID BIT(20)
-
-/* IRQ_MISC_ENABLE_CLR register bits */
-#define USBSS_IRQ_MISC_ENABLE_CLR_VBUSVALID BIT(22)
-#define USBSS_IRQ_MISC_ENABLE_CLR_SESSVALID BIT(20)
-
-/* IRQ_MISC_EOI register bits */
-#define USBSS_IRQ_MISC_EOI_VECTOR BIT(0)
-
-/* VBUS_STAT register bits */
-#define USBSS_VBUS_STAT_SESSVALID BIT(2)
-#define USBSS_VBUS_STAT_VBUSVALID BIT(0)
-
-/* USB_PHY_CTRL register bits in CTRL_MMR */
-#define PHY_CORE_VOLTAGE_MASK BIT(31)
#define PHY_PLL_REFCLK_MASK GENMASK(3, 0)
-
-/* USB PHY2 register offsets */
-#define USB_PHY_PLL_REG12 0x130
-#define USB_PHY_PLL_LDO_REF_EN BIT(5)
-#define USB_PHY_PLL_LDO_REF_EN_EN BIT(4)
-
-#define DWC3_AM62_AUTOSUSPEND_DELAY 100
-
-#define USBSS_DEBUG_CFG_OFF 0x0
-#define USBSS_DEBUG_CFG_DISABLED 0x7
-
-struct dwc3_am62 {
- struct device *dev;
- void __iomem *usbss;
- struct clk *usb2_refclk;
- int rate_code;
- struct regmap *syscon;
- unsigned int offset;
- unsigned int vbus_divider;
- u32 wakeup_stat;
- void __iomem *phy_regs;
-};
-
-static const int dwc3_ti_rate_table[] = { /* in KHZ */
+static const int dwc3_ti_am62_rate_table[] = { /* in KHZ */
9600,
10000,
12000,
@@ -139,277 +33,93 @@ static const int dwc3_ti_rate_table[] = { /* in KHZ */
52000,
};
-static inline u32 dwc3_ti_readl(struct dwc3_am62 *am62, u32 offset)
-{
- return readl((am62->usbss) + offset);
-}
-
-static inline void dwc3_ti_writel(struct dwc3_am62 *am62, u32 offset, u32 value)
+static void dwc3_ti_am62_glue_configure(struct udevice *dev, int index,
+ enum usb_dr_mode mode)
{
- writel(value, (am62->usbss) + offset);
-}
-
-static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
-{
- struct device *dev = am62->dev;
- struct device_node *node = dev->of_node;
- struct regmap *syscon;
- int ret;
-
- syscon = syscon_regmap_lookup_by_phandle_args(node, "ti,syscon-phy-pll-refclk",
- 1, &am62->offset);
- if (IS_ERR(syscon)) {
- dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
- return PTR_ERR(syscon);
- }
-
- am62->syscon = syscon;
-
- /* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
- ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0);
- if (ret) {
- dev_err(dev, "failed to set phy core voltage\n");
- return ret;
- }
-
- ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code);
- if (ret) {
- dev_err(dev, "failed to set phy pll reference clock rate\n");
- return ret;
- }
-
- return 0;
-}
-
-static int dwc3_ti_init(struct dwc3_am62 *am62)
-{
- int ret;
- u32 reg;
-
- /* Read the syscon property and set the rate code */
- ret = phy_syscon_pll_refclk(am62);
- if (ret)
- return ret;
-
- /* Workaround Errata i2409 */
- if (am62->phy_regs) {
- reg = readl(am62->phy_regs + USB_PHY_PLL_REG12);
- reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
- writel(reg, am62->phy_regs + USB_PHY_PLL_REG12);
- }
-
- /* VBUS divider select */
- reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
- if (am62->vbus_divider)
- reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
-
- dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
-
- clk_prepare_enable(am62->usb2_refclk);
-
- /* Set mode valid bit to indicate role is valid */
- reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
- reg |= USBSS_MODE_VALID;
- dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
-
- return 0;
-}
-
-static int dwc3_ti_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = pdev->dev.of_node;
- struct dwc3_am62 *am62;
+ struct clk usb2_refclk;
+ int rate_code, i, ret;
unsigned long rate;
- int i, ret;
-
- am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
- if (!am62)
- return -ENOMEM;
-
- am62->dev = dev;
- platform_set_drvdata(pdev, am62);
+ u32 reg;
+ void *usbss;
+ bool vbus_divider;
+ struct regmap *syscon;
+ struct ofnode_phandle_args args;
- am62->usbss = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(am62->usbss)) {
+ usbss = dev_remap_addr_index(dev, 0);
+ if (IS_ERR(usbss)) {
dev_err(dev, "can't map IOMEM resource\n");
- return PTR_ERR(am62->usbss);
+ return;
}
- am62->usb2_refclk = devm_clk_get(dev, "ref");
- if (IS_ERR(am62->usb2_refclk)) {
+ ret = clk_get_by_name(dev, "ref", &usb2_refclk);
+ if (ret) {
dev_err(dev, "can't get usb2_refclk\n");
- return PTR_ERR(am62->usb2_refclk);
+ return;
}
/* Calculate the rate code */
- rate = clk_get_rate(am62->usb2_refclk);
- rate /= 1000; // To KHz
- for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
- if (dwc3_ti_rate_table[i] == rate)
+ rate = clk_get_rate(&usb2_refclk);
+ rate /= 1000; /* To KHz */
+ for (i = 0; i < ARRAY_SIZE(dwc3_ti_am62_rate_table); i++) {
+ if (dwc3_ti_am62_rate_table[i] == rate)
break;
}
- if (i == ARRAY_SIZE(dwc3_ti_rate_table)) {
+ if (i == ARRAY_SIZE(dwc3_ti_am62_rate_table)) {
dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
- return -EINVAL;
+ return;
}
- am62->rate_code = i;
+ rate_code = i;
- am62->phy_regs = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(am62->phy_regs)) {
- dev_err(dev, "can't map PHY IOMEM resource. Won't apply i2409 fix.\n");
- am62->phy_regs = NULL;
+ /* Read the syscon property */
+ syscon = syscon_regmap_lookup_by_phandle(dev, "ti,syscon-phy-pll-refclk");
+ if (IS_ERR(syscon)) {
+ dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
+ return;
}
- am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
-
- ret = dwc3_ti_init(am62);
+ ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "ti,syscon-phy-pll-refclk", NULL, 1,
+ 0, &args);
if (ret)
- return ret;
+ return;
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- /*
- * Don't ignore its dependencies with its children
- */
- pm_suspend_ignore_children(dev, false);
- pm_runtime_get_noresume(dev);
-
- ret = of_platform_populate(node, NULL, NULL, dev);
+ /* Program PHY PLL refclk by reading syscon property */
+ ret = regmap_update_bits(syscon, args.args[0], PHY_PLL_REFCLK_MASK, rate_code);
if (ret) {
- dev_err(dev, "failed to create dwc3 core: %d\n", ret);
- goto err_pm_disable;
- }
-
- /* Device has capability to wakeup system from sleep */
- device_set_wakeup_capable(dev, true);
- ret = device_wakeup_enable(dev);
- if (ret)
- dev_err(dev, "couldn't enable device as a wakeup source: %d\n", ret);
-
- /* Setting up autosuspend */
- pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
- return 0;
-
-err_pm_disable:
- clk_disable_unprepare(am62->usb2_refclk);
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- return ret;
-}
-
-static void dwc3_ti_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct dwc3_am62 *am62 = platform_get_drvdata(pdev);
- u32 reg;
-
- pm_runtime_get_sync(dev);
- device_init_wakeup(dev, false);
- of_platform_depopulate(dev);
-
- /* Clear mode valid bit */
- reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
- reg &= ~USBSS_MODE_VALID;
- dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
-
- pm_runtime_put_sync(dev);
- pm_runtime_disable(dev);
- pm_runtime_dont_use_autosuspend(dev);
- pm_runtime_set_suspended(dev);
-}
-
-#ifdef CONFIG_PM
-static int dwc3_ti_suspend_common(struct device *dev)
-{
- struct dwc3_am62 *am62 = dev_get_drvdata(dev);
- u32 reg, current_prtcap_dir;
-
- if (device_may_wakeup(dev)) {
- reg = dwc3_ti_readl(am62, USBSS_CORE_STAT);
- current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK)
- >> USBSS_CORE_OPERATIONAL_MODE_SHIFT;
- /* Set wakeup config enable bits */
- reg = dwc3_ti_readl(am62, USBSS_WAKEUP_CONFIG);
- if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) {
- reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN;
- } else {
- reg = USBSS_WAKEUP_CFG_VBUSVALID_EN | USBSS_WAKEUP_CFG_SESSVALID_EN;
- /*
- * Enable LINESTATE wake up only if connected to bus
- * and in U2/L3 state else it causes spurious wake-up.
- */
- }
- dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, reg);
- /* clear wakeup status so we know what caused the wake up */
- dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
- }
-
- /* just to track if module resets on suspend */
- dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_DISABLED);
-
- clk_disable_unprepare(am62->usb2_refclk);
-
- return 0;
-}
-
-static int dwc3_ti_resume_common(struct device *dev)
-{
- struct dwc3_am62 *am62 = dev_get_drvdata(dev);
- u32 reg;
-
- reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG);
- if (reg != USBSS_DEBUG_CFG_DISABLED) {
- /* lost power/context */
- dwc3_ti_init(am62);
- } else {
- dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF);
- clk_prepare_enable(am62->usb2_refclk);
+ dev_err(dev, "failed to set phy pll reference clock rate\n");
+ return;
}
- if (device_may_wakeup(dev)) {
- /* Clear wakeup config enable bits */
- dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
- }
+ /* VBUS divider select */
+ reg = readl(usbss + USBSS_PHY_CONFIG);
+ vbus_divider = dev_read_bool(dev, "ti,vbus-divider");
+ if (vbus_divider)
+ reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
- reg = dwc3_ti_readl(am62, USBSS_WAKEUP_STAT);
- am62->wakeup_stat = reg;
+ writel(reg, usbss + USBSS_PHY_CONFIG);
- return 0;
+ /* Set mode valid */
+ reg = readl(usbss + USBSS_MODE_CONTROL);
+ reg |= USBSS_MODE_VALID;
+ writel(reg, usbss + USBSS_MODE_CONTROL);
}
-static UNIVERSAL_DEV_PM_OPS(dwc3_ti_pm_ops, dwc3_ti_suspend_common,
- dwc3_ti_resume_common, NULL);
-
-#define DEV_PM_OPS (&dwc3_ti_pm_ops)
-#else
-#define DEV_PM_OPS NULL
-#endif /* CONFIG_PM */
-
-static const struct of_device_id dwc3_ti_of_match[] = {
- { .compatible = "ti,am62-usb"},
- {},
+struct dwc3_glue_ops ti_am62_ops = {
+ .glue_configure = dwc3_ti_am62_glue_configure,
};
-MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
-static struct platform_driver dwc3_ti_driver = {
- .probe = dwc3_ti_probe,
- .remove = dwc3_ti_remove,
- .driver = {
- .name = "dwc3-am62",
- .pm = DEV_PM_OPS,
- .of_match_table = dwc3_ti_of_match,
- },
+static const struct udevice_id dwc3_am62_match[] = {
+ { .compatible = "ti,am62-usb", .data = (ulong)&ti_am62_ops },
+ { /* sentinel */ }
};
-module_platform_driver(dwc3_ti_driver);
-
-MODULE_ALIAS("platform:dwc3-am62");
-MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("DesignWare USB3 TI Glue Layer");
+U_BOOT_DRIVER(dwc3_am62_wrapper) = {
+ .name = "dwc3-am62",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = dwc3_am62_match,
+ .bind = dwc3_glue_bind,
+ .probe = dwc3_glue_probe,
+ .remove = dwc3_glue_remove,
+ .plat_auto = sizeof(struct dwc3_glue_data),
+};
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 4b219c35eb35..9de8ecff3adc 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -25,8 +25,6 @@
#include <linux/usb/otg.h>
#include <linux/compat.h>
-#include "linux-compat.h"
-
/*
* All these registers belong to OMAP's Wrapper around the
* DesignWare USB3 Core.
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 666ac432f52d..0a4f69cbc28a 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1,4 +1,3 @@
-// SPDX-License-Identifier: GPL-2.0
/*
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
@@ -8,22 +7,19 @@
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
+#include <cpu_func.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bug.h>
+#include <linux/dma-mapping.h>
#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
#include <linux/list.h>
-#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include "core.h"
-#include "debug.h"
#include "gadget.h"
#include "io.h"
@@ -58,8 +54,6 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
else
trb->ctrl |= (DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_LST);
-
- trace_dwc3_prepare_trb(dep, trb);
}
static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
@@ -112,7 +106,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
direction = !!(dep->flags & DWC3_EP0_DIR_IN);
if (dwc->ep0state != EP0_DATA_PHASE) {
- dev_WARN(dwc->dev, "Unexpected pending request\n");
+ dev_warn(dwc->dev, "Unexpected pending request\n");
return 0;
}
@@ -133,7 +127,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
- usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED);
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
@@ -264,16 +258,8 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep0_set_halt(ep, value);
- spin_unlock_irqrestore(&dwc->lock, flags);
- return ret;
+ return __dwc3_gadget_ep0_set_halt(ep, value);
}
void dwc3_ep0_out_start(struct dwc3 *dwc)
@@ -282,8 +268,6 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
int ret;
int i;
- complete(&dwc->ep0_in_setup);
-
dep = dwc->eps[0];
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
@@ -354,7 +338,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
/*
* LTM will be set once we know how to set this in HW.
*/
- usb_status |= dwc->gadget->is_selfpowered;
+ usb_status |= dwc->gadget.is_selfpowered;
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
@@ -364,7 +348,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
if (reg & DWC3_DCTL_INITU2ENA)
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
} else {
- usb_status |= dwc->gadget->wakeup_armed <<
+ usb_status |= dwc->gadget.wakeup_armed <<
USB_DEVICE_REMOTE_WAKEUP;
}
@@ -482,12 +466,12 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
- state = dwc->gadget->state;
+ state = dwc->gadget.state;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
if (dwc->wakeup_configured)
- dwc->gadget->wakeup_armed = set;
+ dwc->gadget.wakeup_armed = set;
else
ret = -EINVAL;
break;
@@ -594,7 +578,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget->state;
+ enum usb_device_state state = dwc->gadget.state;
u32 addr;
u32 reg;
@@ -615,9 +599,9 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
- usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
else
- usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
return 0;
}
@@ -628,7 +612,7 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
if (dwc->async_callbacks) {
spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
+ ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
spin_lock(&dwc->lock);
}
return ret;
@@ -636,7 +620,7 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget->state;
+ enum usb_device_state state = dwc->gadget.state;
u32 cfg;
int ret;
u32 reg;
@@ -662,7 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* to change the state on the next usb_ep_queue()
*/
if (ret == 0)
- usb_gadget_set_state(dwc->gadget,
+ usb_gadget_set_state(&dwc->gadget,
USB_STATE_CONFIGURED);
/*
@@ -681,7 +665,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret)
- usb_gadget_set_state(dwc->gadget,
+ usb_gadget_set_state(&dwc->gadget,
USB_STATE_ADDRESS);
break;
default:
@@ -737,7 +721,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
struct dwc3_ep *dep;
- enum usb_device_state state = dwc->gadget->state;
+ enum usb_device_state state = dwc->gadget.state;
u16 wLength;
if (state == USB_STATE_DEFAULT)
@@ -781,7 +765,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct
if (wIndex || wLength)
return -EINVAL;
- dwc->gadget->isoch_delay = wValue;
+ dwc->gadget.isoch_delay = wValue;
return 0;
}
@@ -830,8 +814,6 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out;
- trace_dwc3_ctrl_req(ctrl);
-
len = le16_to_cpu(ctrl->wLength);
if (!len) {
dwc->three_stage_setup = false;
@@ -873,7 +855,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
trb = dwc->ep0_trb;
- trace_dwc3_complete_trb(ep0, trb);
r = next_request(&ep0->pending_list);
if (!r)
@@ -898,7 +879,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ur->length && ur->zero) || dwc->ep0_bounced) {
trb++;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- trace_dwc3_complete_trb(ep0, trb);
if (r->direction)
dwc->eps[1]->trb_enqueue = 0;
@@ -925,8 +905,6 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
dep = dwc->eps[0];
trb = dwc->ep0_trb;
- trace_dwc3_complete_trb(dep, trb);
-
if (!list_empty(&dep->pending_list)) {
r = next_request(&dep->pending_list);
@@ -998,21 +976,21 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
&& (dep->number == 0)) {
u32 maxpacket;
u32 rem;
+ struct usb_request *ureq = &req->request;
+ enum dma_data_direction dir = req->direction ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE;
- ret = usb_gadget_map_request_by_dev(dwc->sysdev,
- &req->request, dep->number);
- if (ret)
+ ureq->dma = dma_map_single(ureq->buf, ureq->length, dir);
+ if (!ureq->dma)
return;
maxpacket = dep->endpoint.maxpacket;
- rem = req->request.length % maxpacket;
+ rem = ureq->length % maxpacket;
dwc->ep0_bounced = true;
/* prepare normal TRB */
- dwc3_ep0_prepare_one_trb(dep, req->request.dma,
- req->request.length,
- DWC3_TRBCTL_CONTROL_DATA,
- true);
+ dwc3_ep0_prepare_one_trb(dep, ureq->dma, ureq->length,
+ DWC3_TRBCTL_CONTROL_DATA, true);
req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
@@ -1024,17 +1002,17 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dep);
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
+ struct usb_request *ureq = &req->request;
+ enum dma_data_direction dir = req->direction ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE;
- ret = usb_gadget_map_request_by_dev(dwc->sysdev,
- &req->request, dep->number);
- if (ret)
+ ureq->dma = dma_map_single(ureq->buf, ureq->length, dir);
+ if (!ureq->dma)
return;
/* prepare normal TRB */
- dwc3_ep0_prepare_one_trb(dep, req->request.dma,
- req->request.length,
- DWC3_TRBCTL_CONTROL_DATA,
- true);
+ dwc3_ep0_prepare_one_trb(dep, ureq->dma, ureq->length,
+ DWC3_TRBCTL_CONTROL_DATA, true);
req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
@@ -1047,14 +1025,16 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
false);
ret = dwc3_ep0_start_trans(dep);
} else {
- ret = usb_gadget_map_request_by_dev(dwc->sysdev,
- &req->request, dep->number);
- if (ret)
+ struct usb_request *ureq = &req->request;
+ enum dma_data_direction dir = req->direction ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE;
+
+ ureq->dma = dma_map_single(ureq->buf, ureq->length, dir);
+ if (!ureq->dma)
return;
- dwc3_ep0_prepare_one_trb(dep, req->request.dma,
- req->request.length, DWC3_TRBCTL_CONTROL_DATA,
- false);
+ dwc3_ep0_prepare_one_trb(dep, ureq->dma, ureq->length,
+ DWC3_TRBCTL_CONTROL_DATA, false);
req->trb = &dwc->ep0_trb[dep->trb_enqueue];
@@ -1174,7 +1154,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
*/
if (!list_empty(&dep->pending_list)) {
dwc->delayed_status = false;
- usb_gadget_set_state(dwc->gadget,
+ usb_gadget_set_state(&dwc->gadget,
USB_STATE_CONFIGURED);
dwc3_ep0_do_control_status(dwc, event);
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 74968f93d4a3..7f76c2d1aed5 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -8,14 +8,14 @@
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
-#include <linux/kernel.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
@@ -27,6 +27,8 @@
#include "gadget.h"
#include "io.h"
+#define usleep_range(a, b) udelay((b))
+
#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
& ~((d)->interval - 1))
@@ -193,24 +195,21 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
struct dwc3_request *req, int status)
{
- struct dwc3 *dwc = dep->dwc;
+ struct usb_request *ureq = &req->request;
+ enum dma_data_direction dir = req->direction ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE;
list_del(&req->list);
req->remaining = 0;
req->num_trbs = 0;
- if (req->request.status == -EINPROGRESS)
- req->request.status = status;
+ if (ureq->status == -EINPROGRESS)
+ ureq->status = status;
if (req->trb)
- usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
+ ureq->dma = dma_map_single(ureq->buf, ureq->length, dir);
req->trb = NULL;
- trace_dwc3_gadget_giveback(req);
-
- if (dep->number > 1)
- pm_runtime_put(dwc->dev);
}
/**
@@ -226,8 +225,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
- struct dwc3 *dwc = dep->dwc;
-
dwc3_gadget_del_and_unmap_request(dep, req, status);
req->status = DWC3_REQUEST_STATUS_COMPLETED;
@@ -271,8 +268,6 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
status = -ETIMEDOUT;
}
- trace_dwc3_gadget_generic_cmd(cmd, param, status);
-
return ret;
}
@@ -324,7 +319,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
*
* DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
- if (dwc->gadget->speed <= USB_SPEED_HIGH ||
+ if (dwc->gadget.speed <= USB_SPEED_HIGH ||
DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
@@ -393,7 +388,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
ret = 0;
break;
case DEPEVT_TRANSFER_NO_RESOURCE:
- dev_WARN(dwc->dev, "No resource for %s\n",
+ dev_warn(dwc->dev, "No resource for %s\n",
dep->name);
ret = -EINVAL;
break;
@@ -412,7 +407,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
ret = -EAGAIN;
break;
default:
- dev_WARN(dwc->dev, "UNKNOWN cmd status\n");
+ dev_warn(dwc->dev, "UNKNOWN cmd status\n");
}
break;
@@ -425,8 +420,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
skip_status:
- trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
-
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
if (ret == 0)
dep->flags |= DWC3_EP_TRANSFER_STARTED;
@@ -464,7 +457,7 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
*/
if (dep->direction &&
!DWC3_VER_IS_PRIOR(DWC3, 260A) &&
- (dwc->gadget->speed >= USB_SPEED_SUPER))
+ (dwc->gadget.speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(¶ms, 0, sizeof(params));
@@ -482,14 +475,12 @@ static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
{
- struct dwc3 *dwc = dep->dwc;
-
if (dep->trb_pool)
return 0;
- dep->trb_pool = dma_alloc_coherent(dwc->sysdev,
- sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
- &dep->trb_pool_dma, GFP_KERNEL);
+ dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) *
+ DWC3_TRB_NUM,
+ (unsigned long *)&dep->trb_pool_dma);
if (!dep->trb_pool) {
dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
dep->name);
@@ -501,10 +492,7 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
- struct dwc3 *dwc = dep->dwc;
-
- dma_free_coherent(dwc->sysdev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
- dep->trb_pool, dep->trb_pool_dma);
+ dma_free_coherent(dep->trb_pool);
dep->trb_pool = NULL;
dep->trb_pool_dma = 0;
@@ -589,7 +577,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget->speed >= USB_SPEED_SUPER) {
+ if (dwc->gadget.speed >= USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst;
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
@@ -644,7 +632,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
bInterval_m1 = min_t(u8, desc->bInterval - 1, 13);
if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
- dwc->gadget->speed == USB_SPEED_FULL)
+ dwc->gadget.speed == USB_SPEED_FULL)
dep->interval = desc->bInterval;
else
dep->interval = 1 << (desc->bInterval - 1);
@@ -818,7 +806,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
ram_depth = dwc3_gadget_calc_ram_depth(dwc);
- switch (dwc->gadget->speed) {
+ switch (dwc->gadget.speed) {
case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
@@ -1017,8 +1005,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
}
out:
- trace_dwc3_gadget_ep_enable(dep);
-
return 0;
}
@@ -1068,8 +1054,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
u32 reg;
u32 mask;
- trace_dwc3_gadget_ep_disable(dep);
-
/* make sure HW endpoint isn't stalled */
if (dep->flags & DWC3_EP_STALL)
__dwc3_gadget_ep_set_halt(dep, 0, false);
@@ -1137,9 +1121,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
- "%s is already enabled\n",
- dep->name))
+ if (dep->flags & DWC3_EP_ENABLED)
return 0;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1164,9 +1146,7 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
- "%s is already disabled\n",
- dep->name))
+ if (!(dep->flags & DWC3_EP_ENABLED))
return 0;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1191,8 +1171,6 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
req->dep = dep;
req->status = DWC3_REQUEST_STATUS_UNKNOWN;
- trace_dwc3_alloc_request(req);
-
return &req->request;
}
@@ -1201,7 +1179,6 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
{
struct dwc3_request *req = to_dwc3_request(request);
- trace_dwc3_free_request(req);
kfree(req);
}
@@ -1278,13 +1255,11 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
unsigned int no_interrupt = req->request.no_interrupt;
unsigned int is_last = req->request.is_last;
struct dwc3 *dwc = dep->dwc;
- struct usb_gadget *gadget = dwc->gadget;
+ struct usb_gadget *gadget = &dwc->gadget;
enum usb_device_speed speed = gadget->speed;
if (use_bounce_buffer)
dma = dep->dwc->bounce_addr;
- else if (req->request.num_sgs > 0)
- dma = sg_dma_address(req->start_sg);
else
dma = req->request.dma;
@@ -1363,7 +1338,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
* This is only possible with faulty memory because we
* checked it already :)
*/
- dev_WARN(dwc->dev, "Unknown endpoint type %d\n",
+ dev_warn(dwc->dev, "Unknown endpoint type %d\n",
usb_endpoint_type(dep->endpoint.desc));
}
@@ -1407,12 +1382,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
* controller to observe the HWO bit set prematurely.
* Add a write memory barrier to prevent CPU re-ordering.
*/
- wmb();
+ // FIXME wmb();
trb->ctrl |= DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_enq(dep);
-
- trace_dwc3_prepare_trb(dep, trb);
}
static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
@@ -1468,106 +1441,6 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
return num_trbs;
}
-static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
- struct dwc3_request *req)
-{
- struct scatterlist *sg = req->start_sg;
- struct scatterlist *s;
- int i;
- unsigned int length = req->request.length;
- unsigned int remaining = req->num_pending_sgs;
- unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining;
- unsigned int num_trbs = req->num_trbs;
- bool needs_extra_trb = dwc3_needs_extra_trb(dep, req);
-
- /*
- * If we resume preparing the request, then get the remaining length of
- * the request and resume where we left off.
- */
- for_each_sg(req->request.sg, s, num_queued_sgs, i)
- length -= sg_dma_len(s);
-
- for_each_sg(sg, s, remaining, i) {
- unsigned int num_trbs_left = dwc3_calc_trbs_left(dep);
- unsigned int trb_length;
- bool must_interrupt = false;
- bool last_sg = false;
-
- trb_length = min_t(unsigned int, length, sg_dma_len(s));
-
- length -= trb_length;
-
- /*
- * IOMMU driver is coalescing the list of sgs which shares a
- * page boundary into one and giving it to USB driver. With
- * this the number of sgs mapped is not equal to the number of
- * sgs passed. So mark the chain bit to false if it isthe last
- * mapped sg.
- */
- if ((i == remaining - 1) || !length)
- last_sg = true;
-
- if (!num_trbs_left)
- break;
-
- if (last_sg) {
- if (!dwc3_prepare_last_sg(dep, req, trb_length, i))
- break;
- } else {
- /*
- * Look ahead to check if we have enough TRBs for the
- * next SG entry. If not, set interrupt on this TRB to
- * resume preparing the next SG entry when more TRBs are
- * free.
- */
- if (num_trbs_left == 1 || (needs_extra_trb &&
- num_trbs_left <= 2 &&
- sg_dma_len(sg_next(s)) >= length)) {
- struct dwc3_request *r;
-
- /* Check if previous requests already set IOC */
- list_for_each_entry(r, &dep->started_list, list) {
- if (r != req && !r->request.no_interrupt)
- break;
-
- if (r == req)
- must_interrupt = true;
- }
- }
-
- dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false,
- must_interrupt);
- }
-
- /*
- * There can be a situation where all sgs in sglist are not
- * queued because of insufficient trb number. To handle this
- * case, update start_sg to next sg to be queued, so that
- * we have free trbs we can continue queuing from where we
- * previously stopped
- */
- if (!last_sg)
- req->start_sg = sg_next(s);
-
- req->num_pending_sgs--;
-
- /*
- * The number of pending SG entries may not correspond to the
- * number of mapped SG entries. If all the data are queued, then
- * don't include unused SG entries.
- */
- if (length == 0) {
- req->num_pending_sgs = 0;
- break;
- }
-
- if (must_interrupt)
- break;
- }
-
- return req->num_trbs - num_trbs;
-}
-
static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
@@ -1602,12 +1475,6 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep)
* break things.
*/
list_for_each_entry(req, &dep->started_list, list) {
- if (req->num_pending_sgs > 0) {
- ret = dwc3_prepare_trbs_sg(dep, req);
- if (!ret || req->num_pending_sgs)
- return ret;
- }
-
if (!dwc3_calc_trbs_left(dep))
return ret;
@@ -1623,22 +1490,15 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep)
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
struct dwc3 *dwc = dep->dwc;
+ struct usb_request *ureq = &req->request;
+ enum dma_data_direction dir = dep->direction ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE;
- ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
- dep->direction);
- if (ret)
- return ret;
+ ureq->dma = dma_map_single(ureq->buf, ureq->length, dir);
+ if (!ureq->dma)
+ return -ENOMEM;
- req->start_sg = req->request.sg;
- req->num_pending_sgs = req->request.num_mapped_sgs;
-
- if (req->num_pending_sgs > 0) {
- ret = dwc3_prepare_trbs_sg(dep, req);
- if (req->num_pending_sgs)
- return ret;
- } else {
- ret = dwc3_prepare_trbs_linear(dep, req);
- }
+ ret = dwc3_prepare_trbs_linear(dep, req);
if (!ret || !dwc3_calc_trbs_left(dep))
return ret;
@@ -1917,12 +1777,12 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
if (!dwc->dis_start_transfer_quirk &&
(DWC3_VER_IS_PRIOR(DWC31, 170A) ||
DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
- if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction)
+ if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
return dwc3_gadget_start_isoc_quirk(dep);
}
if (desc->bInterval <= 14 &&
- dwc->gadget->speed >= USB_SPEED_HIGH) {
+ dwc->gadget.speed >= USB_SPEED_HIGH) {
u32 frame = __dwc3_gadget_get_frame(dwc);
bool rollover = frame <
(dep->frame_number & DWC3_FRNUMBER_MASK);
@@ -1986,13 +1846,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
dep->name, &req->request))
return -EINVAL;
- pm_runtime_get(dwc->dev);
-
req->request.actual = 0;
req->request.status = -EINPROGRESS;
- trace_dwc3_ep_queue(req);
-
list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
@@ -2038,17 +1894,8 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
{
struct dwc3_request *req = to_dwc3_request(request);
struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
-
- unsigned long flags;
-
- int ret;
- spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_queue(dep, req);
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return ret;
+ return __dwc3_gadget_ep_queue(dep, req);
}
static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
@@ -2124,8 +1971,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
unsigned long flags;
int ret = 0;
- trace_dwc3_ep_dequeue(req);
-
spin_lock_irqsave(&dwc->lock, flags);
list_for_each_entry(r, &dep->cancelled_list, list) {
@@ -2270,34 +2115,21 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
- unsigned long flags;
-
- int ret;
-
- spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_set_halt(dep, value, false);
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return ret;
+ return __dwc3_gadget_ep_set_halt(dep, value, false);
}
static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
- struct dwc3 *dwc = dep->dwc;
- unsigned long flags;
int ret;
- spin_lock_irqsave(&dwc->lock, flags);
dep->flags |= DWC3_EP_WEDGE;
if (dep->number == 0 || dep->number == 1)
ret = __dwc3_gadget_ep0_set_halt(ep, 1);
else
ret = __dwc3_gadget_ep_set_halt(dep, 1, false);
- spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
}
@@ -2422,7 +2254,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
}
spin_lock_irqsave(&dwc->lock, flags);
- if (!dwc->gadget->wakeup_armed) {
+ if (!dwc->gadget.wakeup_armed) {
dev_err(dwc->dev, "not armed for remote wakeup\n");
spin_unlock_irqrestore(&dwc->lock, flags);
return -EINVAL;
@@ -2436,63 +2268,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
static void dwc3_resume_gadget(struct dwc3 *dwc);
-static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
-{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
- int ret;
- int link_state;
-
- if (!dwc->wakeup_configured) {
- dev_err(dwc->dev, "remote wakeup not configured\n");
- return -EINVAL;
- }
-
- spin_lock_irqsave(&dwc->lock, flags);
- /*
- * If the link is in U3, signal for remote wakeup and wait for the
- * link to transition to U0 before sending device notification.
- */
- link_state = dwc3_gadget_get_link_state(dwc);
- if (link_state == DWC3_LINK_STATE_U3) {
- dwc->wakeup_pending_funcs |= BIT(intf_id);
- ret = __dwc3_gadget_wakeup(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
- return ret;
- }
-
- ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
- DWC3_DGCMDPAR_DN_FUNC_WAKE |
- DWC3_DGCMDPAR_INTF_SEL(intf_id));
- if (ret)
- dev_err(dwc->dev, "function remote wakeup failed, ret:%d\n", ret);
-
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return ret;
-}
-
-static int dwc3_gadget_set_remote_wakeup(struct usb_gadget *g, int set)
-{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
-
- spin_lock_irqsave(&dwc->lock, flags);
- dwc->wakeup_configured = !!set;
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return 0;
-}
-
static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
int is_selfpowered)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
-
- spin_lock_irqsave(&dwc->lock, flags);
g->is_selfpowered = !!is_selfpowered;
- spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
@@ -2611,9 +2390,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
u32 timeout = 2000;
u32 saved_config = 0;
- if (pm_runtime_suspended(dwc->dev))
- return 0;
-
/*
* When operating in USB 2.0 speeds (HS/FS), ensure that
* GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY are cleared before starting
@@ -2721,18 +2497,8 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
* stall the transfer, and move back to the SETUP phase, so that any
* pending endxfers can be executed.
*/
- if (dwc->ep0state != EP0_SETUP_PHASE) {
- reinit_completion(&dwc->ep0_in_setup);
-
- ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
- msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
- if (ret == 0) {
- dev_warn(dwc->dev, "wait for SETUP phase timed out\n");
- spin_lock_irqsave(&dwc->lock, flags);
- dwc3_ep0_reset_state(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
- }
- }
+ if (dwc->ep0state != EP0_SETUP_PHASE)
+ dwc3_ep0_reset_state(dwc);
/*
* Note: if the GEVNTCOUNT indicates events in the event buffer, the
@@ -2753,7 +2519,7 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
__dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
- usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
return ret;
}
@@ -2786,44 +2552,11 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
dwc->softconnect = is_on;
- /*
- * Avoid issuing a runtime resume if the device is already in the
- * suspended state during gadget disconnect. DWC3 gadget was already
- * halted/stopped during runtime suspend.
- */
- if (!is_on) {
- pm_runtime_barrier(dwc->dev);
- if (pm_runtime_suspended(dwc->dev))
- return 0;
- }
-
- /*
- * Check the return value for successful resume, or error. For a
- * successful resume, the DWC3 runtime PM resume routine will handle
- * the run stop sequence, so avoid duplicate operations here.
- */
- ret = pm_runtime_get_sync(dwc->dev);
- if (!ret || ret < 0) {
- pm_runtime_put(dwc->dev);
- if (ret < 0)
- pm_runtime_set_suspended(dwc->dev);
- return ret;
- }
-
- if (dwc->pullups_connected == is_on) {
- pm_runtime_put(dwc->dev);
- return 0;
- }
-
- synchronize_irq(dwc->irq_gadget);
-
if (!is_on)
ret = dwc3_gadget_soft_disconnect(dwc);
else
ret = dwc3_gadget_soft_connect(dwc);
- pm_runtime_put(dwc->dev);
-
return ret;
}
@@ -2856,7 +2589,6 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
}
-static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
/**
@@ -3000,26 +2732,8 @@ static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
- int ret;
- int irq;
- irq = dwc->irq_gadget;
- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
- IRQF_SHARED, "dwc3", dwc->ev_buf);
- if (ret) {
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
- irq, ret);
- return ret;
- }
-
- spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = driver;
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- if (dwc->sys_wakeup)
- device_wakeup_enable(dwc->sysdev);
-
return 0;
}
@@ -3033,17 +2747,9 @@ static void __dwc3_gadget_stop(struct dwc3 *dwc)
static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- unsigned long flags;
-
- if (dwc->sys_wakeup)
- device_wakeup_disable(dwc->sysdev);
- spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = NULL;
dwc->max_cfg_eps = 0;
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- free_irq(dwc->irq_gadget, dwc->ev_buf);
return 0;
}
@@ -3113,19 +2819,11 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- union power_supply_propval val = {0};
- int ret;
if (dwc->usb2_phy)
return usb_phy_set_power(dwc->usb2_phy, mA);
- if (!dwc->usb_psy)
- return -EOPNOTSUPP;
-
- val.intval = 1000 * mA;
- ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
-
- return ret;
+ return 0;
}
/**
@@ -3186,8 +2884,6 @@ static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
- .func_wakeup = dwc3_gadget_func_wakeup,
- .set_remote_wakeup = dwc3_gadget_set_remote_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
@@ -3202,6 +2898,47 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
/* -------------------------------------------------------------------------- */
+static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+ u32 count;
+ u32 reg;
+
+ evt = dwc->ev_buf;
+
+ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ count &= DWC3_GEVNTCOUNT_MASK;
+ if (!count)
+ return IRQ_NONE;
+
+ evt->count = count;
+ evt->flags |= DWC3_EVENT_PENDING;
+
+ /* Mask interrupt */
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
+ reg |= DWC3_GEVNTSIZ_INTMASK;
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ irqreturn_t ret = IRQ_NONE;
+ irqreturn_t status;
+
+ spin_lock(&dwc->lock);
+
+ status = dwc3_check_event_buf(dwc);
+ if (status == IRQ_WAKE_THREAD)
+ ret = status;
+
+ spin_unlock(&dwc->lock);
+
+ return ret;
+}
+
static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
@@ -3210,7 +2947,7 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!dep->direction)
- dwc->gadget->ep0 = &dep->endpoint;
+ dwc->gadget.ep0 = &dep->endpoint;
dep->endpoint.caps.type_control = true;
@@ -3258,7 +2995,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget->ep_list);
+ &dwc->gadget.ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
@@ -3305,7 +3042,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget->ep_list);
+ &dwc->gadget.ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
@@ -3313,50 +3050,6 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
return dwc3_alloc_trb_pool(dep);
}
-#define nostream_work_to_dep(w) (container_of(to_delayed_work(w), struct dwc3_ep, nostream_work))
-static void dwc3_nostream_work(struct work_struct *work)
-{
- struct dwc3_ep *dep = nostream_work_to_dep(work);
- struct dwc3 *dwc = dep->dwc;
- unsigned long flags;
-
- spin_lock_irqsave(&dwc->lock, flags);
- if (dep->flags & DWC3_EP_STREAM_PRIMED)
- goto out;
-
- if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
- (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
- !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
- goto out;
- /*
- * If the host rejects a stream due to no active stream, by the
- * USB and xHCI spec, the endpoint will be put back to idle
- * state. When the host is ready (buffer added/updated), it will
- * prime the endpoint to inform the usb device controller. This
- * triggers the device controller to issue ERDY to restart the
- * stream. However, some hosts don't follow this and keep the
- * endpoint in the idle state. No prime will come despite host
- * streams are updated, and the device controller will not be
- * triggered to generate ERDY to move the next stream data. To
- * workaround this and maintain compatibility with various
- * hosts, force to reinitiate the stream until the host is ready
- * instead of waiting for the host to prime the endpoint.
- */
- if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
- unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
-
- dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
- } else {
- dep->flags |= DWC3_EP_DELAY_START;
- dwc3_stop_active_transfer(dep, true, true);
- spin_unlock_irqrestore(&dwc->lock, flags);
- return;
- }
-out:
- dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
- spin_unlock_irqrestore(&dwc->lock, flags);
-}
-
static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
{
struct dwc3_ep *dep;
@@ -3402,7 +3095,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
INIT_LIST_HEAD(&dep->cancelled_list);
- INIT_DELAYED_WORK(&dep->nostream_work, dwc3_nostream_work);
dwc3_debugfs_create_endpoint_dir(dep);
@@ -3439,7 +3131,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
u8 num;
int ret;
- INIT_LIST_HEAD(&dwc->gadget->ep_list);
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
ret = dwc3_gadget_get_reserved_endpoints(dwc, propname,
reserved_eps, ARRAY_SIZE(reserved_eps));
@@ -3503,7 +3195,6 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
dwc3_ep_inc_deq(dep);
- trace_dwc3_complete_trb(dep, trb);
req->num_trbs--;
/*
@@ -3844,7 +3535,6 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
if (event->status == DEPEVT_STREAMEVT_FOUND) {
- cancel_delayed_work(&dep->nostream_work);
dep->flags |= DWC3_EP_STREAM_PRIMED;
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
return;
@@ -3853,15 +3543,11 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
switch (event->parameters) {
case DEPEVT_STREAM_PRIME:
- cancel_delayed_work(&dep->nostream_work);
dep->flags |= DWC3_EP_STREAM_PRIMED;
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
break;
case DEPEVT_STREAM_NOSTREAM:
dep->flags &= ~DWC3_EP_STREAM_PRIMED;
- if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
- queue_delayed_work(system_wq, &dep->nostream_work,
- msecs_to_jiffies(100));
break;
}
}
@@ -3921,7 +3607,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(dwc->gadget);
+ dwc->gadget_driver->disconnect(&dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3930,7 +3616,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->suspend(dwc->gadget);
+ dwc->gadget_driver->suspend(&dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3939,7 +3625,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
{
if (dwc->async_callbacks && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(dwc->gadget);
+ dwc->gadget_driver->resume(&dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3949,9 +3635,9 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
if (!dwc->gadget_driver)
return;
- if (dwc->async_callbacks && dwc->gadget->speed != USB_SPEED_UNKNOWN) {
+ if (dwc->async_callbacks && dwc->gadget.speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
- usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver);
+ usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
@@ -4062,20 +3748,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_disconnect_gadget(dwc);
- dwc->gadget->speed = USB_SPEED_UNKNOWN;
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
- dwc->gadget->wakeup_armed = false;
+ dwc->gadget.wakeup_armed = false;
dwc3_gadget_enable_linksts_evts(dwc, false);
- usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
dwc3_ep0_reset_state(dwc);
-
- /*
- * Request PM idle to address condition where usage count is
- * already decremented to zero, but waiting for the disconnect
- * interrupt to set dwc->connected to FALSE.
- */
- pm_request_idle(dwc->dev);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -4146,7 +3825,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
- dwc->gadget->wakeup_armed = false;
+ dwc->gadget.wakeup_armed = false;
dwc3_gadget_enable_linksts_evts(dwc, false);
dwc3_clear_stall_all_ep(dwc);
@@ -4174,7 +3853,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
if (DWC3_IP_IS(DWC32))
lanes = DWC3_DSTS_CONNLANES(reg) + 1;
- dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
+ dwc->gadget.ssp_rate = USB_SSP_GEN_UNKNOWN;
/*
* RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
@@ -4188,13 +3867,13 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
switch (speed) {
case DWC3_DSTS_SUPERSPEED_PLUS:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget->ep0->maxpacket = 512;
- dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget.ep0->maxpacket = 512;
+ dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
if (lanes > 1)
- dwc->gadget->ssp_rate = USB_SSP_GEN_2x2;
+ dwc->gadget.ssp_rate = USB_SSP_GEN_2x2;
else
- dwc->gadget->ssp_rate = USB_SSP_GEN_2x1;
+ dwc->gadget.ssp_rate = USB_SSP_GEN_2x1;
break;
case DWC3_DSTS_SUPERSPEED:
/*
@@ -4214,27 +3893,27 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget->ep0->maxpacket = 512;
- dwc->gadget->speed = USB_SPEED_SUPER;
+ dwc->gadget.ep0->maxpacket = 512;
+ dwc->gadget.speed = USB_SPEED_SUPER;
if (lanes > 1) {
- dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
- dwc->gadget->ssp_rate = USB_SSP_GEN_1x2;
+ dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget.ssp_rate = USB_SSP_GEN_1x2;
}
break;
case DWC3_DSTS_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget->ep0->maxpacket = 64;
- dwc->gadget->speed = USB_SPEED_HIGH;
+ dwc->gadget.ep0->maxpacket = 64;
+ dwc->gadget.speed = USB_SPEED_HIGH;
break;
case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget->ep0->maxpacket = 64;
- dwc->gadget->speed = USB_SPEED_FULL;
+ dwc->gadget.ep0->maxpacket = 64;
+ dwc->gadget.speed = USB_SPEED_FULL;
break;
}
- dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket;
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
/* Enable USB2 LPM Capability */
@@ -4313,7 +3992,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, unsigned int evtinfo)
if (dwc->async_callbacks && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(dwc->gadget);
+ dwc->gadget_driver->resume(&dwc->gadget);
spin_lock(&dwc->lock);
}
@@ -4402,7 +4081,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
switch (next) {
case DWC3_LINK_STATE_U0:
- if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
+ if (dwc->gadget.wakeup_armed || dwc->wakeup_pending_funcs) {
dwc3_gadget_enable_linksts_evts(dwc, false);
dwc3_resume_gadget(dwc);
dwc->suspended = false;
@@ -4469,7 +4148,6 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_wakeup_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
- dev_WARN_ONCE(dwc->dev, true, "unexpected hibernation event\n");
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
@@ -4485,15 +4163,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
case DWC3_DEVICE_EVENT_OVERFLOW:
break;
default:
- dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ dev_warn(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
}
}
static void dwc3_process_event_entry(struct dwc3 *dwc,
const union dwc3_event *event)
{
- trace_dwc3_event(event->raw, dwc);
-
if (!event->type.is_devspec)
dwc3_endpoint_interrupt(dwc, &event->depevt);
else if (event->type.type == DWC3_EVENT_TYPE_DEV)
@@ -4545,7 +4221,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
* Add an explicit write memory barrier to make sure that the update of
* clearing DWC3_EVENT_PENDING is observed in dwc3_check_event_buf()
*/
- wmb();
+ // FIXME wmb();
if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
@@ -4558,112 +4234,12 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
{
struct dwc3_event_buffer *evt = _evt;
- struct dwc3 *dwc = evt->dwc;
- unsigned long flags;
- irqreturn_t ret = IRQ_NONE;
-
- local_bh_disable();
- spin_lock_irqsave(&dwc->lock, flags);
- ret = dwc3_process_event_buf(evt);
- spin_unlock_irqrestore(&dwc->lock, flags);
- local_bh_enable();
-
- return ret;
-}
-
-static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
-{
- struct dwc3 *dwc = evt->dwc;
- u32 amount;
- u32 count;
-
- if (pm_runtime_suspended(dwc->dev)) {
- dwc->pending_events = true;
- /*
- * Trigger runtime resume. The get() function will be balanced
- * after processing the pending events in dwc3_process_pending
- * events().
- */
- pm_runtime_get(dwc->dev);
- disable_irq_nosync(dwc->irq_gadget);
- return IRQ_HANDLED;
- }
-
- /*
- * With PCIe legacy interrupt, test shows that top-half irq handler can
- * be called again after HW interrupt deassertion. Check if bottom-half
- * irq event handler completes before caching new event to prevent
- * losing events.
- */
- if (evt->flags & DWC3_EVENT_PENDING)
- return IRQ_HANDLED;
-
- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
- count &= DWC3_GEVNTCOUNT_MASK;
- if (!count)
- return IRQ_NONE;
-
- if (count > evt->length) {
- dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
- count, evt->length);
- return IRQ_NONE;
- }
-
- evt->count = count;
- evt->flags |= DWC3_EVENT_PENDING;
- /* Mask interrupt */
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
- DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
-
- amount = min(count, evt->length - evt->lpos);
- memcpy(evt->cache + evt->lpos, evt->buf + evt->lpos, amount);
-
- if (amount < count)
- memcpy(evt->cache, evt->buf, count - amount);
-
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
-
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t dwc3_interrupt(int irq, void *_evt)
-{
- struct dwc3_event_buffer *evt = _evt;
-
- return dwc3_check_event_buf(evt);
-}
-
-static int dwc3_gadget_get_irq(struct dwc3 *dwc)
-{
- struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
- int irq;
-
- irq = platform_get_irq_byname_optional(dwc3_pdev, "peripheral");
- if (irq > 0)
- goto out;
-
- if (irq == -EPROBE_DEFER)
- goto out;
-
- irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
- if (irq > 0)
- goto out;
-
- if (irq == -EPROBE_DEFER)
- goto out;
-
- irq = platform_get_irq(dwc3_pdev, 0);
-
-out:
- return irq;
+ return dwc3_process_event_buf(evt);
}
static void dwc_gadget_release(struct device *dev)
{
- struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev);
-
- kfree(gadget);
}
/**
@@ -4675,20 +4251,10 @@ static void dwc_gadget_release(struct device *dev)
int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
- int irq;
struct device *dev;
- irq = dwc3_gadget_get_irq(dwc);
- if (irq < 0) {
- ret = irq;
- goto err0;
- }
-
- dwc->irq_gadget = irq;
-
- dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
- sizeof(*dwc->ep0_trb) * 2,
- &dwc->ep0_trb_addr, GFP_KERNEL);
+ dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2,
+ (long *)&dwc->ep0_trb_addr);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
@@ -4701,31 +4267,20 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err1;
}
- dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
- &dwc->bounce_addr, GFP_KERNEL);
+ dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE,
+ (unsigned long *)&dwc->bounce_addr);
if (!dwc->bounce) {
ret = -ENOMEM;
goto err2;
}
- init_completion(&dwc->ep0_in_setup);
- dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);
- if (!dwc->gadget) {
- ret = -ENOMEM;
- goto err3;
- }
-
-
- usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
- dev = &dwc->gadget->dev;
- dev->platform_data = dwc;
- dwc->gadget->ops = &dwc3_gadget_ops;
- dwc->gadget->speed = USB_SPEED_UNKNOWN;
- dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
- dwc->gadget->sg_supported = true;
- dwc->gadget->name = "dwc3-gadget";
- dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable;
- dwc->gadget->wakeup_capable = true;
+ usb_initialize_gadget((struct device *)dwc->dev, &dwc->gadget,
+ dwc_gadget_release);
+ dev = &dwc->gadget.dev;
+ dwc->gadget.ops = &dwc3_gadget_ops;
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget.ssp_rate = USB_SSP_GEN_UNKNOWN;
+ dwc->gadget.name = "dwc3-gadget";
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -4748,8 +4303,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
- dwc->gadget->max_speed = dwc->maximum_speed;
- dwc->gadget->max_ssp_rate = dwc->max_ssp_rate;
+ dwc->gadget.max_speed = dwc->maximum_speed;
+ dwc->gadget.max_ssp_rate = dwc->max_ssp_rate;
/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -4760,38 +4315,30 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;
- ret = usb_add_gadget(dwc->gadget);
+ ret = usb_add_gadget(&dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to add gadget\n");
goto err5;
}
if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
- dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate);
+ dwc3_gadget_set_ssp_rate(&dwc->gadget, dwc->max_ssp_rate);
else
- dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
-
- /* No system wakeup if no gadget driver bound */
- if (dwc->sys_wakeup)
- device_wakeup_disable(dwc->sysdev);
+ dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
return 0;
err5:
dwc3_gadget_free_endpoints(dwc);
err4:
- usb_put_gadget(dwc->gadget);
- dwc->gadget = NULL;
-err3:
- dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
- dwc->bounce_addr);
+ usb_put_gadget(&dwc->gadget);
+ dma_free_coherent(dwc->bounce);
err2:
kfree(dwc->setup_buf);
err1:
- dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
- dwc->ep0_trb, dwc->ep0_trb_addr);
+ dma_free_coherent(dwc->ep0_trb);
err0:
return ret;
@@ -4801,48 +4348,33 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
- if (!dwc->gadget)
- return;
-
dwc3_enable_susphy(dwc, false);
- usb_del_gadget(dwc->gadget);
+ usb_del_gadget_udc(&dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
- usb_put_gadget(dwc->gadget);
- dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
- dwc->bounce_addr);
+ usb_put_gadget(&dwc->gadget);
+ dma_free_coherent(dwc->bounce);
kfree(dwc->setup_buf);
- dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
- dwc->ep0_trb, dwc->ep0_trb_addr);
+ dma_free_coherent(dwc->ep0_trb);
}
-int dwc3_gadget_suspend(struct dwc3 *dwc)
+/**
+ * dwc3_gadget_uboot_handle_interrupt - handle dwc3 gadget interrupt
+ * @dwc: struct dwce *
+ *
+ * Handles ep0 and gadget interrupt
+ *
+ * Should be called from dwc3 core.
+ */
+void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc)
{
- unsigned long flags;
- int ret;
-
- ret = dwc3_gadget_soft_disconnect(dwc);
- /*
- * Attempt to reset the controller's state. Likely no
- * communication can be established until the host
- * performs a port reset.
- */
- if (ret && dwc->softconnect) {
- dwc3_gadget_soft_connect(dwc);
- return -EAGAIN;
- }
+ int ret = dwc3_interrupt(0, dwc);
- spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver)
- dwc3_disconnect_gadget(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ if (ret == IRQ_WAKE_THREAD) {
+ struct dwc3_event_buffer *evt;
- return 0;
-}
+ dwc3_thread_interrupt(0, dwc);
-int dwc3_gadget_resume(struct dwc3 *dwc)
-{
- if (!dwc->gadget_driver || !dwc->softconnect)
- return 0;
-
- return dwc3_gadget_soft_connect(dwc);
+ /* Clean + Invalidate the buffer after touching it */
+ dwc3_flush_cache((uintptr_t)evt->buf, evt->length);
+ }
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index d73e735e4081..095c27ffe9ce 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -17,7 +17,8 @@
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
-#define gadget_to_dwc(g) (dev_get_platdata(&g->dev))
+#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+
/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
@@ -120,6 +121,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index);
+void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 1e96ea339d48..8bb0d891226c 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -11,10 +11,10 @@
#ifndef __DRIVERS_USB_DWC3_IO_H
#define __DRIVERS_USB_DWC3_IO_H
-#include <linux/io.h>
-#include "trace.h"
-#include "debug.h"
-#include "core.h"
+#include <asm/io.h>
+#include <cpu_func.h>
+
+#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
{
@@ -27,13 +27,6 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
*/
value = readl(base + offset - DWC3_GLOBALS_REGS_START);
- /*
- * When tracing we want to make it easy to find the correct address on
- * documentation, so we revert it back to the proper addresses, the
- * same way they are described on SNPS documentation
- */
- trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value);
-
return value;
}
@@ -45,13 +38,14 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
* However, the offsets are given starting from xHCI address space.
*/
writel(value, base + offset - DWC3_GLOBALS_REGS_START);
+}
- /*
- * When tracing we want to make it easy to find the correct address on
- * documentation, so we revert it back to the proper addresses, the
- * same way they are described on SNPS documentation
- */
- trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value);
+static inline void dwc3_flush_cache(uintptr_t addr, int length)
+{
+ uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1);
+ uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE);
+
+ flush_dcache_range((unsigned long)start_addr, (unsigned long)end_addr);
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/ti_usb_phy.c b/drivers/usb/dwc3/ti_usb_phy.c
index f0ecdea958ae..f1ddc0086945 100644
--- a/drivers/usb/dwc3/ti_usb_phy.c
+++ b/drivers/usb/dwc3/ti_usb_phy.c
@@ -27,8 +27,6 @@
#include <asm/arch/sys_proto.h>
#include <dm.h>
-#include "linux-compat.h"
-
#define PLL_STATUS 0x00000004
#define PLL_GO 0x00000008
#define PLL_CONFIGURATION1 0x0000000C
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index b3c780a4e35c..5b0c55d59c8d 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -1435,52 +1435,6 @@ int dm_usb_gadget_handle_interrupts(struct udevice *dev)
return at91_udc_irq(udc);
}
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
-{
- struct at91_udc *udc = controller;
- int ret;
-
- if (!driver || !driver->bind || !driver->setup) {
- printf("bad paramter\n");
- return -EINVAL;
- }
-
- if (udc->driver) {
- printf("UDC already has a gadget driver\n");
- return -EBUSY;
- }
-
- at91_start(&udc->gadget, driver);
-
- udc->driver = driver;
-
- ret = driver->bind(&udc->gadget);
- if (ret) {
- pr_err("driver->bind() returned %d\n", ret);
- udc->driver = NULL;
- }
-
- return ret;
-}
-
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
-{
- struct at91_udc *udc = controller;
-
- if (!driver || !driver->unbind || !driver->disconnect) {
- pr_err("bad paramter\n");
- return -EINVAL;
- }
-
- driver->disconnect(&udc->gadget);
- driver->unbind(&udc->gadget);
- udc->driver = NULL;
-
- at91_stop(&udc->gadget);
-
- return 0;
-}
-
int at91_udc_probe(struct at91_udc_data *pdata)
{
struct at91_udc *udc;
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index f7a92ded6dab..1f0015ed36a0 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -32,12 +32,6 @@ static int usba_udc_stop(struct usb_gadget *gadget);
#include "atmel_usba_udc.h"
-static int vbus_is_present(struct usba_udc *udc)
-{
- /* No Vbus detection: Assume always present */
- return 1;
-}
-
static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
{
unsigned int transaction_len;
@@ -1187,32 +1181,6 @@ static int usba_udc_irq(struct usba_udc *udc)
return 0;
}
-static int usba_udc_enable(struct usba_udc *udc)
-{
- udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
-
- udc->vbus_prev = 0;
-
- /* If Vbus is present, enable the controller and wait for reset */
- if (vbus_is_present(udc) && udc->vbus_prev == 0) {
- usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
- }
-
- return 0;
-}
-
-static int usba_udc_disable(struct usba_udc *udc)
-{
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- reset_all_endpoints(udc);
-
- /* This will also disable the DP pullup */
- usba_writel(udc, CTRL, USBA_DISABLE_MASK);
-
- return 0;
-}
-
static struct usba_ep *usba_udc_pdata(struct usba_platform_data *pdata,
struct usba_udc *udc)
{
@@ -1273,69 +1241,55 @@ int dm_usb_gadget_handle_interrupts(struct udevice *dev)
return usba_udc_irq(udc);
}
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+int usba_udc_probe(struct usba_platform_data *pdata)
{
- struct usba_udc *udc = &controller;
- int ret;
-
- if (!driver || !driver->bind || !driver->setup) {
- log_err("bad parameter\n");
- return -EINVAL;
- }
+ struct usba_udc *udc;
- if (udc->driver) {
- log_err("UDC already has a gadget driver\n");
- return -EBUSY;
- }
+ udc = &controller;
- usba_udc_enable(udc);
+ udc->usba_ep = usba_udc_pdata(pdata, udc);
- udc->driver = driver;
+ return 0;
+}
- ret = driver->bind(&udc->gadget);
- if (ret) {
- log_err("driver->bind() returned %d\n", ret);
- udc->driver = NULL;
- }
+#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
+struct usba_priv_data {
+ struct clk_bulk clks;
+ struct usba_udc udc;
+};
- return ret;
+static int vbus_is_present(struct usba_udc *udc)
+{
+ /* No Vbus detection: Assume always present */
+ return 1;
}
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int usba_udc_enable(struct usba_udc *udc)
{
- struct usba_udc *udc = &controller;
-
- if (!driver || !driver->unbind || !driver->disconnect) {
- log_err("bad parameter\n");
- return -EINVAL;
- }
+ udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
- driver->disconnect(&udc->gadget);
- driver->unbind(&udc->gadget);
- udc->driver = NULL;
+ udc->vbus_prev = 0;
- usba_udc_disable(udc);
+ /* If Vbus is present, enable the controller and wait for reset */
+ if (vbus_is_present(udc) && udc->vbus_prev == 0) {
+ usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+ usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+ }
return 0;
}
-int usba_udc_probe(struct usba_platform_data *pdata)
+static int usba_udc_disable(struct usba_udc *udc)
{
- struct usba_udc *udc;
-
- udc = &controller;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ reset_all_endpoints(udc);
- udc->usba_ep = usba_udc_pdata(pdata, udc);
+ /* This will also disable the DP pullup */
+ usba_writel(udc, CTRL, USBA_DISABLE_MASK);
return 0;
}
-#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
-struct usba_priv_data {
- struct clk_bulk clks;
- struct usba_udc udc;
-};
-
static int usba_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 4bff75da759d..ae208ea0627e 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -84,62 +84,15 @@ static struct usb_endpoint_descriptor ep0_desc = {
};
static int ci_pullup(struct usb_gadget *gadget, int is_on);
-static int ci_ep_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc);
-static int ci_ep_disable(struct usb_ep *ep);
-static int ci_ep_queue(struct usb_ep *ep,
- struct usb_request *req, gfp_t gfp_flags);
-static int ci_ep_dequeue(struct usb_ep *ep, struct usb_request *req);
-static struct usb_request *
-ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags);
-static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req);
static const struct usb_gadget_ops ci_udc_ops = {
.pullup = ci_pullup,
};
-static const struct usb_ep_ops ci_ep_ops = {
- .enable = ci_ep_enable,
- .disable = ci_ep_disable,
- .queue = ci_ep_queue,
- .dequeue = ci_ep_dequeue,
- .alloc_request = ci_ep_alloc_request,
- .free_request = ci_ep_free_request,
-};
-
__weak void ci_init_after_reset(struct ehci_ctrl *ctrl)
{
}
-/* Init values for USB endpoints. */
-static const struct usb_ep ci_ep_init[5] = {
- [0] = { /* EP 0 */
- .maxpacket = 64,
- .name = "ep0",
- .ops = &ci_ep_ops,
- },
- [1] = {
- .maxpacket = 512,
- .name = "ep1in-bulk",
- .ops = &ci_ep_ops,
- },
- [2] = {
- .maxpacket = 512,
- .name = "ep2out-bulk",
- .ops = &ci_ep_ops,
- },
- [3] = {
- .maxpacket = 512,
- .name = "ep3in-int",
- .ops = &ci_ep_ops,
- },
- [4] = {
- .maxpacket = 512,
- .name = "ep-",
- .ops = &ci_ep_ops,
- },
-};
-
static struct ci_drv controller = {
.gadget = {
.name = "ci_udc",
@@ -263,51 +216,6 @@ static void ci_invalidate_td(struct ept_queue_item *td)
invalidate_dcache_range(start, end);
}
-static struct usb_request *
-ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
-{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
- int num = -1;
- struct ci_req *ci_req;
-
- if (ci_ep->desc)
- num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-
- if (num == 0 && controller.ep0_req)
- return &controller.ep0_req->req;
-
- ci_req = calloc(1, sizeof(*ci_req));
- if (!ci_req)
- return NULL;
-
- INIT_LIST_HEAD(&ci_req->queue);
-
- if (num == 0)
- controller.ep0_req = ci_req;
-
- return &ci_req->req;
-}
-
-static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
-{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
- struct ci_req *ci_req = container_of(req, struct ci_req, req);
- int num = -1;
-
- if (ci_ep->desc)
- num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-
- if (num == 0) {
- if (!controller.ep0_req)
- return;
- controller.ep0_req = 0;
- }
-
- if (ci_req->b_buf)
- free(ci_req->b_buf);
- free(ci_req);
-}
-
static void ep_enable(int num, int in, int maxpacket)
{
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
@@ -328,126 +236,6 @@ static void ep_enable(int num, int in, int maxpacket)
writel(n, &udc->epctrl[num]);
}
-static int ci_ep_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
- int num, in;
- num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
- ci_ep->desc = desc;
- ep->desc = desc;
-
- if (num) {
- int max = get_unaligned_le16(&desc->wMaxPacketSize);
-
- if ((max > 64) && (controller.gadget.speed == USB_SPEED_FULL))
- max = 64;
- if (ep->maxpacket != max) {
- DBG("%s: from %d to %d\n", __func__,
- ep->maxpacket, max);
- ep->maxpacket = max;
- }
- }
- ep_enable(num, in, ep->maxpacket);
- DBG("%s: num=%d maxpacket=%d\n", __func__, num, ep->maxpacket);
- return 0;
-}
-
-static int ep_disable(int num, int in)
-{
- struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
- unsigned int ep_bit, enable_bit;
- int err;
-
- if (in) {
- ep_bit = EPT_TX(num);
- enable_bit = CTRL_TXE;
- } else {
- ep_bit = EPT_RX(num);
- enable_bit = CTRL_RXE;
- }
-
- /* clear primed buffers */
- do {
- writel(ep_bit, &udc->epflush);
- err = wait_for_bit_le32(&udc->epflush, ep_bit, false, 1000, false);
- if (err)
- return err;
- } while (readl(&udc->epstat) & ep_bit);
-
- /* clear enable bit */
- clrbits_le32(&udc->epctrl[num], enable_bit);
-
- return 0;
-}
-
-static int ci_ep_disable(struct usb_ep *ep)
-{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
- int num, in, err;
-
- num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
-
- err = ep_disable(num, in);
- if (err)
- return err;
-
- ci_ep->desc = NULL;
- ep->desc = NULL;
- ci_ep->req_primed = false;
- return 0;
-}
-
-static int ci_bounce(struct ci_req *ci_req, int in)
-{
- struct usb_request *req = &ci_req->req;
- unsigned long addr = (unsigned long)req->buf;
- unsigned long hwaddr;
- uint32_t aligned_used_len;
-
- /* Input buffer address is not aligned. */
- if (addr & (ARCH_DMA_MINALIGN - 1))
- goto align;
-
- /* Input buffer length is not aligned. */
- if (req->length & (ARCH_DMA_MINALIGN - 1))
- goto align;
-
- /* The buffer is well aligned, only flush cache. */
- ci_req->hw_len = req->length;
- ci_req->hw_buf = req->buf;
- goto flush;
-
-align:
- if (ci_req->b_buf && req->length > ci_req->b_len) {
- free(ci_req->b_buf);
- ci_req->b_buf = 0;
- }
- if (!ci_req->b_buf) {
- ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN);
- ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len);
- if (!ci_req->b_buf)
- return -ENOMEM;
- }
- ci_req->hw_len = ci_req->b_len;
- ci_req->hw_buf = ci_req->b_buf;
-
- if (in)
- memcpy(ci_req->hw_buf, req->buf, req->length);
-
-flush:
- hwaddr = (unsigned long)ci_req->hw_buf;
- if (!hwaddr)
- return 0;
-
- aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN);
- flush_dcache_range(hwaddr, hwaddr + aligned_used_len);
-
- return 0;
-}
-
static void ci_debounce(struct ci_req *ci_req, int in)
{
struct usb_request *req = &ci_req->req;
@@ -574,70 +362,6 @@ static void ci_ep_submit_next_request(struct ci_ep *ci_ep)
writel(bit, &udc->epprime);
}
-static int ci_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct ci_ep *ci_ep = container_of(_ep, struct ci_ep, ep);
- struct ci_req *ci_req;
-
- list_for_each_entry(ci_req, &ci_ep->queue, queue) {
- if (&ci_req->req == _req)
- break;
- }
-
- if (&ci_req->req != _req)
- return -EINVAL;
-
- list_del_init(&ci_req->queue);
-
- if (ci_req->req.status == -EINPROGRESS) {
- ci_req->req.status = -ECONNRESET;
- if (ci_req->req.complete)
- ci_req->req.complete(_ep, _req);
- }
-
- return 0;
-}
-
-static int ci_ep_queue(struct usb_ep *ep,
- struct usb_request *req, gfp_t gfp_flags)
-{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
- struct ci_req *ci_req = container_of(req, struct ci_req, req);
- int in, ret;
- int __maybe_unused num;
-
- num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
-
- if (!num && ci_ep->req_primed) {
- /*
- * The flipping of ep0 between IN and OUT relies on
- * ci_ep_queue consuming the current IN/OUT setting
- * immediately. If this is deferred to a later point when the
- * req is pulled out of ci_req->queue, then the IN/OUT setting
- * may have been changed since the req was queued, and state
- * will get out of sync. This condition doesn't occur today,
- * but could if bugs were introduced later, and this error
- * check will save a lot of debugging time.
- */
- printf("%s: ep0 transaction already in progress\n", __func__);
- return -EPROTO;
- }
-
- ret = ci_bounce(ci_req, in);
- if (ret)
- return ret;
-
- DBG("ept%d %s pre-queue req %p, buffer %p\n",
- num, in ? "in" : "out", ci_req, ci_req->hw_buf);
- list_add_tail(&ci_req->queue, &ci_ep->queue);
-
- if (!ci_ep->req_primed)
- ci_ep_submit_next_request(ci_ep);
-
- return 0;
-}
-
static void flip_ep0_direction(void)
{
if (ep0_desc.bEndpointAddress == USB_DIR_IN) {
@@ -983,149 +707,6 @@ static int ci_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-static int ci_udc_probe(void)
-{
- struct ept_queue_head *head;
- int i;
-
- const int num = 2 * NUM_ENDPOINTS;
-
- const int eplist_min_align = 4096;
- const int eplist_align = roundup(eplist_min_align, ARCH_DMA_MINALIGN);
- const int eplist_raw_sz = num * sizeof(struct ept_queue_head);
- const int eplist_sz = roundup(eplist_raw_sz, ARCH_DMA_MINALIGN);
-
- /* The QH list must be aligned to 4096 bytes. */
- controller.epts = memalign(eplist_align, eplist_sz);
- if (!controller.epts)
- return -ENOMEM;
- memset(controller.epts, 0, eplist_sz);
-
- controller.items_mem = memalign(ILIST_ALIGN, ILIST_SZ);
- if (!controller.items_mem) {
- free(controller.epts);
- return -ENOMEM;
- }
- memset(controller.items_mem, 0, ILIST_SZ);
-
- for (i = 0; i < 2 * NUM_ENDPOINTS; i++) {
- /*
- * Configure QH for each endpoint. The structure of the QH list
- * is such that each two subsequent fields, N and N+1 where N is
- * even, in the QH list represent QH for one endpoint. The Nth
- * entry represents OUT configuration and the N+1th entry does
- * represent IN configuration of the endpoint.
- */
- head = controller.epts + i;
- if (i < 2)
- head->config = CFG_MAX_PKT(EP0_MAX_PACKET_SIZE)
- | CFG_ZLT | CFG_IOS;
- else
- head->config = CFG_MAX_PKT(EP_MAX_PACKET_SIZE)
- | CFG_ZLT;
- head->next = TERMINATE;
- head->info = 0;
-
- if (i & 1) {
- ci_flush_qh(i / 2);
- ci_flush_qtd(i / 2);
- }
- }
-
- INIT_LIST_HEAD(&controller.gadget.ep_list);
-
- /* Init EP 0 */
- memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init));
- controller.ep[0].desc = &ep0_desc;
- INIT_LIST_HEAD(&controller.ep[0].queue);
- controller.ep[0].req_primed = false;
- controller.gadget.ep0 = &controller.ep[0].ep;
- INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
-
- /* Init EP 1..3 */
- for (i = 1; i < 4; i++) {
- memcpy(&controller.ep[i].ep, &ci_ep_init[i],
- sizeof(*ci_ep_init));
- INIT_LIST_HEAD(&controller.ep[i].queue);
- controller.ep[i].req_primed = false;
- list_add_tail(&controller.ep[i].ep.ep_list,
- &controller.gadget.ep_list);
- }
-
- /* Init EP 4..n */
- for (i = 4; i < NUM_ENDPOINTS; i++) {
- memcpy(&controller.ep[i].ep, &ci_ep_init[4],
- sizeof(*ci_ep_init));
- INIT_LIST_HEAD(&controller.ep[i].queue);
- controller.ep[i].req_primed = false;
- list_add_tail(&controller.ep[i].ep.ep_list,
- &controller.gadget.ep_list);
- }
-
- ci_ep_alloc_request(&controller.ep[0].ep, 0);
- if (!controller.ep0_req) {
- free(controller.items_mem);
- free(controller.epts);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
-{
- int ret;
-
- if (!driver)
- return -EINVAL;
- if (!driver->bind || !driver->setup || !driver->disconnect)
- return -EINVAL;
-
-#if CONFIG_IS_ENABLED(DM_USB)
- ret = usb_setup_ehci_gadget(&controller.ctrl);
-#else
- ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
-#endif
- if (ret)
- return ret;
-
- ret = ci_udc_probe();
- if (ret) {
- DBG("udc probe failed, returned %d\n", ret);
- return ret;
- }
-
- ret = driver->bind(&controller.gadget);
- if (ret) {
- DBG("driver->bind() returned %d\n", ret);
- return ret;
- }
- controller.driver = driver;
-
- return 0;
-}
-
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
-{
- udc_disconnect();
-
- driver->unbind(&controller.gadget);
- controller.driver = NULL;
-
- ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
- free(controller.items_mem);
- free(controller.epts);
-
-#if CONFIG_IS_ENABLED(DM_USB)
- usb_remove_ehci_gadget(&controller.ctrl);
-#else
- usb_lowlevel_stop(0);
- controller.ctrl = NULL;
-#endif
-
- return 0;
-}
-
bool dfu_usb_get_reset(void)
{
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 04b85419931e..060788419553 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -833,7 +833,7 @@ static int bos_desc(struct usb_composite_dev *cdev)
/* Get Controller configuration */
if (cdev->gadget->ops->get_config_params) {
- cdev->gadget->ops->get_config_params(
+ cdev->gadget->ops->get_config_params(cdev->gadget,
&dcd_config_params);
} else {
dcd_config_params.bU1devExitLat =
diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c
index 40393141ca95..98a3b59cfa91 100644
--- a/drivers/usb/gadget/dwc2_udc_otg.c
+++ b/drivers/usb/gadget/dwc2_udc_otg.c
@@ -113,9 +113,6 @@ static void dwc2_handle_ep0(struct dwc2_udc *dev);
static int dwc2_ep0_write(struct dwc2_udc *dev);
static int write_fifo_ep0(struct dwc2_ep *ep, struct dwc2_request *req);
static void done(struct dwc2_ep *ep, struct dwc2_request *req, int status);
-static void stop_activity(struct dwc2_udc *dev,
- struct usb_gadget_driver *driver);
-static int udc_enable(struct dwc2_udc *dev);
static void udc_set_address(struct dwc2_udc *dev, unsigned char address);
static void reconfig_usbd(struct dwc2_udc *dev);
static void set_max_pktsize(struct dwc2_udc *dev, enum usb_device_speed speed);
@@ -170,22 +167,6 @@ __weak void otg_phy_off(struct dwc2_udc *dev) {}
#include "dwc2_udc_otg_xfer_dma.c"
-/*
- * udc_disable - disable USB device controller
- */
-static void udc_disable(struct dwc2_udc *dev)
-{
- debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
-
- udc_set_address(dev, 0);
-
- dev->ep0state = WAIT_FOR_SETUP;
- dev->gadget.speed = USB_SPEED_UNKNOWN;
- dev->usb_address = 0;
-
- otg_phy_off(dev);
-}
-
/*
* udc_reinit - initialize software state
*/
@@ -219,6 +200,18 @@ static void udc_reinit(struct dwc2_udc *dev)
#define BYTES2MAXP(x) (x / 8)
#define MAXP2BYTES(x) (x * 8)
+static int dwc2_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+ clrsetbits_le32(®->device_regs.dctl, DCTL_SFTDISCON,
+ is_on ? 0 : DCTL_SFTDISCON);
+
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+
+#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
+
/* until it's enabled, this UDC should be completely invisible
* to any USB host.
*/
@@ -238,112 +231,73 @@ static int udc_enable(struct dwc2_udc *dev)
return 0;
}
-static int dwc2_gadget_pullup(struct usb_gadget *g, int is_on)
-{
- clrsetbits_le32(®->device_regs.dctl, DCTL_SFTDISCON,
- is_on ? 0 : DCTL_SFTDISCON);
-
- return 0;
-}
-
-#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
-/*
- Register entry point for the peripheral controller driver.
-*/
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+static int dwc2_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
struct dwc2_udc *dev = the_controller;
- int retval = 0;
- unsigned long flags = 0;
debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
- if (!driver || driver->speed < USB_SPEED_FULL
- || !driver->bind || !driver->disconnect || !driver->setup)
+ if (!driver || driver->speed < USB_SPEED_FULL ||
+ !driver->bind || !driver->disconnect || !driver->setup)
return -EINVAL;
+
if (!dev)
return -ENODEV;
+
if (dev->driver)
return -EBUSY;
- spin_lock_irqsave(&dev->lock, flags);
/* first hook up the driver ... */
dev->driver = driver;
- spin_unlock_irqrestore(&dev->lock, flags);
-
- if (retval) { /* TODO */
- printf("target device_add failed, error %d\n", retval);
- return retval;
- }
-
- retval = driver->bind(&dev->gadget);
- if (retval) {
- debug_cond(DEBUG_SETUP != 0,
- "%s: bind to driver --> error %d\n",
- dev->gadget.name, retval);
- dev->driver = 0;
- return retval;
- }
-
- enable_irq(IRQ_OTG);
debug_cond(DEBUG_SETUP != 0,
"Registered gadget driver %s\n", dev->gadget.name);
- udc_enable(dev);
-
- return 0;
+ return udc_enable(dev);
}
-/*
- * Unregister entry point for the peripheral controller driver.
- */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static void stop_activity(struct dwc2_udc *dev,
+ struct usb_gadget_driver *driver)
{
- struct dwc2_udc *dev = the_controller;
- unsigned long flags = 0;
-
- if (!dev)
- return -ENODEV;
- if (!driver || driver != dev->driver)
- return -EINVAL;
+ int i;
- spin_lock_irqsave(&dev->lock, flags);
- dev->driver = 0;
- stop_activity(dev, driver);
- spin_unlock_irqrestore(&dev->lock, flags);
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = 0;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
- driver->unbind(&dev->gadget);
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < DWC2_MAX_ENDPOINTS; i++) {
+ struct dwc2_ep *ep = &dev->ep[i];
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
- disable_irq(IRQ_OTG);
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
- udc_disable(dev);
- return 0;
+ /* re-init driver-visible data structures */
+ udc_reinit(dev);
}
-#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
-static int dwc2_gadget_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct dwc2_udc *dev)
{
- struct dwc2_udc *dev = the_controller;
-
- debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
-
- if (!driver || driver->speed < USB_SPEED_FULL ||
- !driver->bind || !driver->disconnect || !driver->setup)
- return -EINVAL;
-
- if (!dev)
- return -ENODEV;
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
- if (dev->driver)
- return -EBUSY;
+ udc_set_address(dev, 0);
- /* first hook up the driver ... */
- dev->driver = driver;
+ dev->ep0state = WAIT_FOR_SETUP;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ dev->usb_address = 0;
- debug_cond(DEBUG_SETUP != 0,
- "Registered gadget driver %s\n", dev->gadget.name);
- return udc_enable(dev);
+ otg_phy_off(dev);
}
static int dwc2_gadget_stop(struct usb_gadget *g)
@@ -433,34 +387,6 @@ static void nuke(struct dwc2_ep *ep, int status)
}
}
-static void stop_activity(struct dwc2_udc *dev,
- struct usb_gadget_driver *driver)
-{
- int i;
-
- /* don't disconnect drivers more than once */
- if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- driver = 0;
- dev->gadget.speed = USB_SPEED_UNKNOWN;
-
- /* prevent new request submissions, kill any outstanding requests */
- for (i = 0; i < DWC2_MAX_ENDPOINTS; i++) {
- struct dwc2_ep *ep = &dev->ep[i];
- ep->stopped = 1;
- nuke(ep, -ESHUTDOWN);
- }
-
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
-
- /* re-init driver-visible data structures */
- udc_reinit(dev);
-}
-
static void reconfig_usbd(struct dwc2_udc *dev)
{
/* 2. Soft-reset OTG Core and then unreset again. */
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 30016b805bfd..93ea70a5d79b 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -6,9 +6,7 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/types.h>
-#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/string.h>
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 7973927e8a7f..13a20441419d 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -925,8 +925,9 @@ set_ether_config(struct eth_dev *dev, gfp_t gfp_flags)
dev->status = ep_desc(gadget, &hs_status_desc,
&fs_status_desc);
dev->status_ep->driver_data = dev;
+ dev->status_ep->desc = dev->status;
- result = usb_ep_enable(dev->status_ep, dev->status);
+ result = usb_ep_enable(dev->status_ep);
if (result != 0) {
debug("enable %s --> %d\n",
dev->status_ep->name, result);
@@ -951,14 +952,16 @@ set_ether_config(struct eth_dev *dev, gfp_t gfp_flags)
* from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG.
*/
if (!cdc_active(dev)) {
- result = usb_ep_enable(dev->in_ep, dev->in);
+ dev->in_ep->desc = dev->in;
+ result = usb_ep_enable(dev->in_ep);
if (result != 0) {
debug("enable %s --> %d\n",
dev->in_ep->name, result);
goto done;
}
- result = usb_ep_enable(dev->out_ep, dev->out);
+ dev->out_ep->desc = dev->out;
+ result = usb_ep_enable(dev->out_ep);
if (result != 0) {
debug("enable %s --> %d\n",
dev->out_ep->name, result);
@@ -1156,7 +1159,8 @@ static void issue_start_status(struct eth_dev *dev)
* FIXME iff req->context != null just dequeue it
*/
usb_ep_disable(dev->status_ep);
- usb_ep_enable(dev->status_ep, dev->status);
+ dev->status_ep->desc = dev->status;
+ usb_ep_enable(dev->status_ep);
/*
* 3.8.1 says to issue first NETWORK_CONNECTION, then
@@ -1314,7 +1318,8 @@ eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
break;
if (dev->status) {
usb_ep_disable(dev->status_ep);
- usb_ep_enable(dev->status_ep, dev->status);
+ dev->status_ep->desc = dev->status;
+ usb_ep_enable(dev->status_ep);
}
value = 0;
@@ -1333,8 +1338,10 @@ eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (wValue == 1) {
if (!cdc_active(dev))
break;
- usb_ep_enable(dev->in_ep, dev->in);
- usb_ep_enable(dev->out_ep, dev->out);
+ dev->in_ep->desc = dev->in;
+ usb_ep_enable(dev->in_ep);
+ dev->out_ep->desc = dev->out;
+ usb_ep_enable(dev->out_ep);
dev->cdc_filter = DEFAULT_FILTER;
if (dev->status)
issue_start_status(dev);
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 8f7256069f58..183e585da580 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -325,20 +325,20 @@ static struct usb_request *acm_start_ep(struct usb_ep *ep, void *complete_cb,
static int acm_start_data(struct f_acm *f_acm, struct usb_gadget *gadget)
{
- const struct usb_endpoint_descriptor *d;
int ret;
/* EP IN */
- d = ep_desc(gadget, &acm_hs_in_desc, &acm_fs_in_desc);
- ret = usb_ep_enable(f_acm->ep_in, d);
+ f_acm->ep_in->desc = ep_desc(gadget, &acm_hs_in_desc, &acm_fs_in_desc);
+ ret = usb_ep_enable(f_acm->ep_in);
if (ret)
return ret;
f_acm->req_in = acm_start_ep(f_acm->ep_in, acm_tx_complete, f_acm);
/* EP OUT */
- d = ep_desc(gadget, &acm_hs_out_desc, &acm_fs_out_desc);
- ret = usb_ep_enable(f_acm->ep_out, d);
+ f_acm->ep_out->desc = ep_desc(gadget, &acm_hs_out_desc,
+ &acm_fs_out_desc);
+ ret = usb_ep_enable(f_acm->ep_out);
if (ret)
return ret;
@@ -354,12 +354,11 @@ static int acm_start_data(struct f_acm *f_acm, struct usb_gadget *gadget)
static int acm_start_ctrl(struct f_acm *f_acm, struct usb_gadget *gadget)
{
- const struct usb_endpoint_descriptor *d;
-
usb_ep_disable(f_acm->ep_notify);
- d = ep_desc(gadget, &acm_hs_notify_desc, &acm_fs_notify_desc);
- usb_ep_enable(f_acm->ep_notify, d);
+ f_acm->ep_notify->desc = ep_desc(gadget, &acm_hs_notify_desc,
+ &acm_fs_notify_desc);
+ usb_ep_enable(f_acm->ep_notify);
acm_start_ep(f_acm->ep_notify, acm_notify_complete, f_acm);
@@ -454,6 +453,9 @@ static void acm_disable(struct usb_function *f)
usb_ep_disable(f_acm->ep_out);
usb_ep_disable(f_acm->ep_in);
usb_ep_disable(f_acm->ep_notify);
+ f_acm->ep_out->desc = NULL;
+ f_acm->ep_in->desc = NULL;
+ f_acm->ep_notify->desc = NULL;
if (f_acm->req_out) {
free(f_acm->req_out->buf);
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 8df0e3f331d1..e1eed5fa4cb4 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -320,13 +320,13 @@ static int fastboot_set_alt(struct usb_function *f,
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct f_fastboot *f_fb = func_to_fastboot(f);
- const struct usb_endpoint_descriptor *d;
debug("%s: func: %s intf: %d alt: %d\n",
__func__, f->name, interface, alt);
- d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out);
- ret = usb_ep_enable(f_fb->out_ep, d);
+ f_fb->out_ep->desc = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out,
+ &ss_ep_out);
+ ret = usb_ep_enable(f_fb->out_ep);
if (ret) {
puts("failed to enable out ep\n");
return ret;
@@ -340,8 +340,8 @@ static int fastboot_set_alt(struct usb_function *f,
}
f_fb->out_req->complete = rx_handler_command;
- d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
- ret = usb_ep_enable(f_fb->in_ep, d);
+ f_fb->in_ep->desc = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
+ ret = usb_ep_enable(f_fb->in_ep);
if (ret) {
puts("failed to enable in ep\n");
goto err;
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 71dc58da3f03..350331cfb01c 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2158,7 +2158,8 @@ static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep,
int rc;
ep->driver_data = common;
- rc = usb_ep_enable(ep, d);
+ ep->desc = d;
+ rc = usb_ep_enable(ep);
if (rc)
ERROR(common, "can't enable %s, result %d\n", ep->name, rc);
return rc;
@@ -2205,10 +2206,12 @@ reset:
/* Disable the endpoints */
if (fsg->bulk_in_enabled) {
usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in->desc = NULL;
fsg->bulk_in_enabled = 0;
}
if (fsg->bulk_out_enabled) {
usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out->desc = NULL;
fsg->bulk_out_enabled = 0;
}
diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c
index d679cdae97c8..37957b202f8c 100644
--- a/drivers/usb/gadget/f_rockusb.c
+++ b/drivers/usb/gadget/f_rockusb.c
@@ -202,6 +202,8 @@ static void rockusb_disable(struct usb_function *f)
usb_ep_disable(f_rkusb->out_ep);
usb_ep_disable(f_rkusb->in_ep);
+ f_rkusb->out_ep->desc = NULL;
+ f_rkusb->in_ep->desc = NULL;
if (f_rkusb->out_req) {
free(f_rkusb->out_req->buf);
@@ -246,13 +248,12 @@ static int rockusb_set_alt(struct usb_function *f, unsigned int interface,
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct f_rockusb *f_rkusb = func_to_rockusb(f);
- const struct usb_endpoint_descriptor *d;
debug("%s: func: %s intf: %d alt: %d\n",
__func__, f->name, interface, alt);
- d = rkusb_ep_desc(gadget, &fs_ep_out, &hs_ep_out);
- ret = usb_ep_enable(f_rkusb->out_ep, d);
+ f_rkusb->out_ep->desc = rkusb_ep_desc(gadget, &fs_ep_out, &hs_ep_out);
+ ret = usb_ep_enable(f_rkusb->out_ep);
if (ret) {
printf("failed to enable out ep\n");
return ret;
@@ -266,8 +267,8 @@ static int rockusb_set_alt(struct usb_function *f, unsigned int interface,
}
f_rkusb->out_req->complete = rx_handler_command;
- d = rkusb_ep_desc(gadget, &fs_ep_in, &hs_ep_in);
- ret = usb_ep_enable(f_rkusb->in_ep, d);
+ f_rkusb->in_ep->desc = rkusb_ep_desc(gadget, &fs_ep_in, &hs_ep_in);
+ ret = usb_ep_enable(f_rkusb->in_ep);
if (ret) {
printf("failed to enable in ep\n");
goto err;
diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c
index 647001d8dd0c..4624b88632fb 100644
--- a/drivers/usb/gadget/f_sdp.c
+++ b/drivers/usb/gadget/f_sdp.c
@@ -624,12 +624,14 @@ static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) {
- result = usb_ep_enable(sdp->in_ep, &in_hs_desc);
- result |= usb_ep_enable(sdp->out_ep, &out_hs_desc);
+ sdp->in_ep->desc = &in_hs_desc;
+ sdp->out_ep->desc = &out_hs_desc;
} else {
- result = usb_ep_enable(sdp->in_ep, &in_desc);
- result |= usb_ep_enable(sdp->out_ep, &out_desc);
+ sdp->in_ep->desc = &in_hs_desc;
+ sdp->out_ep->desc = &out_hs_desc;
}
+ result = usb_ep_enable(sdp->in_ep);
+ result |= usb_ep_enable(sdp->out_ep);
if (result)
return result;
@@ -661,6 +663,8 @@ static void sdp_disable(struct usb_function *f)
usb_ep_disable(sdp->in_ep);
usb_ep_disable(sdp->out_ep);
+ sdp->in_ep->desc = NULL;
+ sdp->out_ep->desc = NULL;
if (sdp->in_req) {
free(sdp->in_req->buf);
diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c
index 540b9f882378..210537d51e92 100644
--- a/drivers/usb/gadget/f_thor.c
+++ b/drivers/usb/gadget/f_thor.c
@@ -909,16 +909,15 @@ static int thor_eps_setup(struct usb_function *f)
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct thor_dev *dev = thor_func->dev;
- struct usb_endpoint_descriptor *d;
struct usb_request *req;
struct usb_ep *ep;
int result;
ep = dev->in_ep;
- d = ep_desc(gadget, &hs_in_desc, &fs_in_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
+ ep->desc = ep_desc(gadget, &hs_in_desc, &fs_in_desc);
+ debug("(d)bEndpointAddress: 0x%x\n", ep->desc->bEndpointAddress);
- result = usb_ep_enable(ep, d);
+ result = usb_ep_enable(ep);
if (result)
goto err;
@@ -933,10 +932,10 @@ static int thor_eps_setup(struct usb_function *f)
req->complete = thor_rx_tx_complete;
dev->in_req = req;
ep = dev->out_ep;
- d = ep_desc(gadget, &hs_out_desc, &fs_out_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
+ ep->desc = ep_desc(gadget, &hs_out_desc, &fs_out_desc);
+ debug("(d)bEndpointAddress: 0x%x\n", ep->desc->bEndpointAddress);
- result = usb_ep_enable(ep, d);
+ result = usb_ep_enable(ep);
if (result)
goto err_free_in_req;
@@ -951,10 +950,10 @@ static int thor_eps_setup(struct usb_function *f)
dev->out_req = req;
/* ACM control EP */
ep = dev->int_ep;
- d = ep_desc(gadget, &hs_int_desc, &fs_int_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
+ ep->desc = ep_desc(gadget, &hs_int_desc, &fs_int_desc);
+ debug("(d)bEndpointAddress: 0x%x\n", ep->desc->bEndpointAddress);
- result = usb_ep_enable(ep, d);
+ result = usb_ep_enable(ep);
if (result)
goto err;
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 56b9b123fa16..f80ae6e5add5 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -7,4 +7,5 @@ obj-$(CONFIG_USB_DWC3_GADGET) += udc-core.o
endif
obj-$(CONFIG_$(PHASE_)DM_USB_GADGET) += udc-core.o
+obj-$(CONFIG_$(PHASE_)USB_GADGET) += udc-core.o
obj-y += udc-uclass.o
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index d709e24c1fd4..b5ef7f1131cc 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -6,27 +6,17 @@
* Author: Felipe Balbi <balbi@ti.com>
*/
-#define pr_fmt(fmt) "UDC core: " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/idr.h>
-#include <linux/err.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/compat.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <linux/bug.h>
#include <linux/dma-mapping.h>
-#include <linux/sched/task_stack.h>
-#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/usb.h>
-
-#include "trace.h"
-
-static DEFINE_IDA(gadget_id_numbers);
-
-static const struct bus_type gadget_bus_type;
/**
* struct usb_udc - describes one usb device controller
@@ -40,7 +30,6 @@ static const struct bus_type gadget_bus_type;
* @allow_connect: Indicates whether UDC is allowed to be pulled up.
* Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
* unbound.
- * @vbus_work: work routine to handle VBUS status change notifications.
* @connect_lock: protects udc->started, gadget->connect,
* gadget->allow_connect and gadget->deactivate. The routines
* usb_gadget_connect_locked(), usb_gadget_disconnect_locked(),
@@ -58,15 +47,13 @@ struct usb_udc {
bool vbus;
bool started;
bool allow_connect;
- struct work_struct vbus_work;
struct mutex connect_lock;
};
-static const struct class udc_class;
static LIST_HEAD(udc_list);
/* Protects udc_list, udc->driver, driver->is_bound, and related calls */
-static DEFINE_MUTEX(udc_lock);
+DEFINE_MUTEX(udc_lock);
/* ------------------------------------------------------------------------- */
@@ -83,8 +70,6 @@ void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
{
ep->maxpacket_limit = maxpacket_limit;
ep->maxpacket = maxpacket_limit;
-
- trace_usb_ep_set_maxpacket_limit(ep, 0);
}
EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
@@ -133,8 +118,6 @@ int usb_ep_enable(struct usb_ep *ep)
ep->enabled = true;
out:
- trace_usb_ep_enable(ep, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_ep_enable);
@@ -167,8 +150,6 @@ int usb_ep_disable(struct usb_ep *ep)
ep->enabled = false;
out:
- trace_usb_ep_disable(ep, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_ep_disable);
@@ -190,13 +171,7 @@ EXPORT_SYMBOL_GPL(usb_ep_disable);
struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
- struct usb_request *req = NULL;
-
- req = ep->ops->alloc_request(ep, gfp_flags);
-
- trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM);
-
- return req;
+ return ep->ops->alloc_request(ep, gfp_flags);
}
EXPORT_SYMBOL_GPL(usb_ep_alloc_request);
@@ -212,7 +187,6 @@ EXPORT_SYMBOL_GPL(usb_ep_alloc_request);
void usb_ep_free_request(struct usb_ep *ep,
struct usb_request *req)
{
- trace_usb_ep_free_request(ep, req, 0);
ep->ops->free_request(ep, req);
}
EXPORT_SYMBOL_GPL(usb_ep_free_request);
@@ -300,8 +274,6 @@ int usb_ep_queue(struct usb_ep *ep,
ret = ep->ops->queue(ep, req, gfp_flags);
out:
- trace_usb_ep_queue(ep, req, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_ep_queue);
@@ -325,12 +297,7 @@ EXPORT_SYMBOL_GPL(usb_ep_queue);
*/
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
- int ret;
-
- ret = ep->ops->dequeue(ep, req);
- trace_usb_ep_dequeue(ep, req, ret);
-
- return ret;
+ return ep->ops->dequeue(ep, req);
}
EXPORT_SYMBOL_GPL(usb_ep_dequeue);
@@ -359,12 +326,7 @@ EXPORT_SYMBOL_GPL(usb_ep_dequeue);
*/
int usb_ep_set_halt(struct usb_ep *ep)
{
- int ret;
-
- ret = ep->ops->set_halt(ep, 1);
- trace_usb_ep_set_halt(ep, ret);
-
- return ret;
+ return ep->ops->set_halt(ep, 1);
}
EXPORT_SYMBOL_GPL(usb_ep_set_halt);
@@ -385,12 +347,7 @@ EXPORT_SYMBOL_GPL(usb_ep_set_halt);
*/
int usb_ep_clear_halt(struct usb_ep *ep)
{
- int ret;
-
- ret = ep->ops->set_halt(ep, 0);
- trace_usb_ep_clear_halt(ep, ret);
-
- return ret;
+ return ep->ops->set_halt(ep, 0);
}
EXPORT_SYMBOL_GPL(usb_ep_clear_halt);
@@ -415,8 +372,6 @@ int usb_ep_set_wedge(struct usb_ep *ep)
else
ret = ep->ops->set_halt(ep, 1);
- trace_usb_ep_set_wedge(ep, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_ep_set_wedge);
@@ -447,8 +402,6 @@ int usb_ep_fifo_status(struct usb_ep *ep)
else
ret = -EOPNOTSUPP;
- trace_usb_ep_fifo_status(ep, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_ep_fifo_status);
@@ -468,8 +421,6 @@ void usb_ep_fifo_flush(struct usb_ep *ep)
{
if (ep->ops->fifo_flush)
ep->ops->fifo_flush(ep);
-
- trace_usb_ep_fifo_flush(ep, 0);
}
EXPORT_SYMBOL_GPL(usb_ep_fifo_flush);
@@ -484,13 +435,8 @@ EXPORT_SYMBOL_GPL(usb_ep_fifo_flush);
*/
int usb_gadget_frame_number(struct usb_gadget *gadget)
{
- int ret;
- ret = gadget->ops->get_frame(gadget);
-
- trace_usb_gadget_frame_number(gadget, ret);
-
- return ret;
+ return gadget->ops->get_frame(gadget);
}
EXPORT_SYMBOL_GPL(usb_gadget_frame_number);
@@ -519,39 +465,10 @@ int usb_gadget_wakeup(struct usb_gadget *gadget)
ret = gadget->ops->wakeup(gadget);
out:
- trace_usb_gadget_wakeup(gadget, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_wakeup);
-/**
- * usb_gadget_set_remote_wakeup - configures the device remote wakeup feature.
- * @gadget:the device being configured for remote wakeup
- * @set:value to be configured.
- *
- * set to one to enable remote wakeup feature and zero to disable it.
- *
- * returns zero on success, else negative errno.
- */
-int usb_gadget_set_remote_wakeup(struct usb_gadget *gadget, int set)
-{
- int ret = 0;
-
- if (!gadget->ops->set_remote_wakeup) {
- ret = -EOPNOTSUPP;
- goto out;
- }
-
- ret = gadget->ops->set_remote_wakeup(gadget, set);
-
-out:
- trace_usb_gadget_set_remote_wakeup(gadget, ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_set_remote_wakeup);
-
/**
* usb_gadget_set_selfpowered - sets the device selfpowered feature.
* @gadget:the device being declared as self-powered
@@ -573,8 +490,6 @@ int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
ret = gadget->ops->set_selfpowered(gadget, 1);
out:
- trace_usb_gadget_set_selfpowered(gadget, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered);
@@ -601,43 +516,10 @@ int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
ret = gadget->ops->set_selfpowered(gadget, 0);
out:
- trace_usb_gadget_clear_selfpowered(gadget, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered);
-/**
- * usb_gadget_vbus_connect - Notify controller that VBUS is powered
- * @gadget:The device which now has VBUS power.
- * Context: can sleep
- *
- * This call is used by a driver for an external transceiver (or GPIO)
- * that detects a VBUS power session starting. Common responses include
- * resuming the controller, activating the D+ (or D-) pullup to let the
- * host detect that a USB device is attached, and starting to draw power
- * (8mA or possibly more, especially after SET_CONFIGURATION).
- *
- * Returns zero on success, else negative errno.
- */
-int usb_gadget_vbus_connect(struct usb_gadget *gadget)
-{
- int ret = 0;
-
- if (!gadget->ops->vbus_session) {
- ret = -EOPNOTSUPP;
- goto out;
- }
-
- ret = gadget->ops->vbus_session(gadget, 1);
-
-out:
- trace_usb_gadget_vbus_connect(gadget, ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect);
-
/**
* usb_gadget_vbus_draw - constrain controller's VBUS power usage
* @gadget:The device whose VBUS usage is being described
@@ -652,21 +534,10 @@ EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect);
*/
int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
{
- int ret = 0;
-
- if (!gadget->ops->vbus_draw) {
- ret = -EOPNOTSUPP;
- goto out;
- }
-
- ret = gadget->ops->vbus_draw(gadget, mA);
- if (!ret)
- gadget->mA = mA;
+ if (!gadget->ops->vbus_draw)
+ return -EOPNOTSUPP;
-out:
- trace_usb_gadget_vbus_draw(gadget, ret);
-
- return ret;
+ return gadget->ops->vbus_draw(gadget, mA);
}
EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw);
@@ -693,41 +564,37 @@ int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
ret = gadget->ops->vbus_session(gadget, 0);
out:
- trace_usb_gadget_vbus_disconnect(gadget, ret);
-
return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
static int usb_gadget_connect_locked(struct usb_gadget *gadget)
- __must_hold(&gadget->udc->connect_lock)
-{
- int ret = 0;
-
- if (!gadget->ops->pullup) {
- ret = -EOPNOTSUPP;
- goto out;
- }
-
- if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) {
- /*
- * If the gadget isn't usable (because it is deactivated,
- * unbound, or not yet started), we only save the new state.
- * The gadget will be connected automatically when it is
- * activated/bound/started.
- */
- gadget->connected = true;
- goto out;
- }
-
- ret = gadget->ops->pullup(gadget, 1);
- if (!ret)
- gadget->connected = 1;
+ __must_hold(&gadget->udc->connect_lock)
+{
+ int ret = 0;
+
+ if (!gadget->ops->pullup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) {
+ /*
+ * If the gadget isn't usable (because it is deactivated,
+ * unbound, or not yet started), we only save the new state.
+ * The gadget will be connected automatically when it is
+ * activated/bound/started.
+ */
+ gadget->connected = true;
+ goto out;
+ }
+
+ ret = gadget->ops->pullup(gadget, 1);
+ if (!ret)
+ gadget->connected = 1;
out:
- trace_usb_gadget_connect(gadget, ret);
-
- return ret;
+ return ret;
}
/**
@@ -742,51 +609,49 @@ out:
*/
int usb_gadget_connect(struct usb_gadget *gadget)
{
- int ret;
+ int ret;
- mutex_lock(&gadget->udc->connect_lock);
- ret = usb_gadget_connect_locked(gadget);
- mutex_unlock(&gadget->udc->connect_lock);
+ mutex_lock(&gadget->udc->connect_lock);
+ ret = usb_gadget_connect_locked(gadget);
+ mutex_unlock(&gadget->udc->connect_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_connect);
static int usb_gadget_disconnect_locked(struct usb_gadget *gadget)
- __must_hold(&gadget->udc->connect_lock)
+ __must_hold(&gadget->udc->connect_lock)
{
- int ret = 0;
+ int ret = 0;
- if (!gadget->ops->pullup) {
- ret = -EOPNOTSUPP;
- goto out;
- }
+ if (!gadget->ops->pullup) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
- if (!gadget->connected)
- goto out;
+ if (!gadget->connected)
+ goto out;
- if (gadget->deactivated || !gadget->udc->started) {
- /*
- * If gadget is deactivated we only save new state.
- * Gadget will stay disconnected after activation.
- */
- gadget->connected = false;
- goto out;
- }
+ if (gadget->deactivated || !gadget->udc->started) {
+ /*
+ * If gadget is deactivated we only save new state.
+ * Gadget will stay disconnected after activation.
+ */
+ gadget->connected = false;
+ goto out;
+ }
- ret = gadget->ops->pullup(gadget, 0);
- if (!ret)
- gadget->connected = 0;
+ ret = gadget->ops->pullup(gadget, 0);
+ if (!ret)
+ gadget->connected = 0;
- mutex_lock(&udc_lock);
- if (gadget->udc->driver)
- gadget->udc->driver->disconnect(gadget);
- mutex_unlock(&udc_lock);
+ mutex_lock(&udc_lock);
+ if (gadget->udc->driver)
+ gadget->udc->driver->disconnect(gadget);
+ mutex_unlock(&udc_lock);
out:
- trace_usb_gadget_disconnect(gadget, ret);
-
- return ret;
+ return ret;
}
/**
@@ -804,180 +669,41 @@ out:
*/
int usb_gadget_disconnect(struct usb_gadget *gadget)
{
- int ret;
+ int ret;
- mutex_lock(&gadget->udc->connect_lock);
- ret = usb_gadget_disconnect_locked(gadget);
- mutex_unlock(&gadget->udc->connect_lock);
+ mutex_lock(&gadget->udc->connect_lock);
+ ret = usb_gadget_disconnect_locked(gadget);
+ mutex_unlock(&gadget->udc->connect_lock);
- return ret;
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_disconnect);
-/**
- * usb_gadget_deactivate - deactivate function which is not ready to work
- * @gadget: the peripheral being deactivated
- *
- * This routine may be used during the gadget driver bind() call to prevent
- * the peripheral from ever being visible to the USB host, unless later
- * usb_gadget_activate() is called. For example, user mode components may
- * need to be activated before the system can talk to hosts.
- *
- * This routine may sleep; it must not be called in interrupt context
- * (such as from within a gadget driver's disconnect() callback).
- *
- * Returns zero on success, else negative errno.
- */
-int usb_gadget_deactivate(struct usb_gadget *gadget)
-{
- int ret = 0;
-
- mutex_lock(&gadget->udc->connect_lock);
- if (gadget->deactivated)
- goto unlock;
-
- if (gadget->connected) {
- ret = usb_gadget_disconnect_locked(gadget);
- if (ret)
- goto unlock;
-
- /*
- * If gadget was being connected before deactivation, we want
- * to reconnect it in usb_gadget_activate().
- */
- gadget->connected = true;
- }
- gadget->deactivated = true;
-
-unlock:
- mutex_unlock(&gadget->udc->connect_lock);
- trace_usb_gadget_deactivate(gadget, ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_deactivate);
-
-/**
- * usb_gadget_activate - activate function which is not ready to work
- * @gadget: the peripheral being activated
- *
- * This routine activates gadget which was previously deactivated with
- * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed.
- *
- * This routine may sleep; it must not be called in interrupt context.
- *
- * Returns zero on success, else negative errno.
- */
-int usb_gadget_activate(struct usb_gadget *gadget)
-{
- int ret = 0;
-
- mutex_lock(&gadget->udc->connect_lock);
- if (!gadget->deactivated)
- goto unlock;
-
- gadget->deactivated = false;
-
- /*
- * If gadget has been connected before deactivation, or became connected
- * while it was being deactivated, we call usb_gadget_connect().
- */
- if (gadget->connected)
- ret = usb_gadget_connect_locked(gadget);
-
-unlock:
- mutex_unlock(&gadget->udc->connect_lock);
- trace_usb_gadget_activate(gadget, ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_activate);
-
/* ------------------------------------------------------------------------- */
-#ifdef CONFIG_HAS_DMA
-
-int usb_gadget_map_request_by_dev(struct device *dev,
+int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return 0;
- if (req->sg_was_mapped) {
- req->num_mapped_sgs = req->num_sgs;
- return 0;
- }
-
- if (req->num_sgs) {
- int mapped;
-
- mapped = dma_map_sg(dev, req->sg, req->num_sgs,
- is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (mapped == 0) {
- dev_err(dev, "failed to map SGs\n");
- return -EFAULT;
- }
-
- req->num_mapped_sgs = mapped;
- } else {
- if (is_vmalloc_addr(req->buf)) {
- dev_err(dev, "buffer is not dma capable\n");
- return -EFAULT;
- } else if (object_is_on_stack(req->buf)) {
- dev_err(dev, "buffer is on stack\n");
- return -EFAULT;
- }
-
- req->dma = dma_map_single(dev, req->buf, req->length,
- is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
-
- if (dma_mapping_error(dev, req->dma)) {
- dev_err(dev, "failed to map buffer\n");
- return -EFAULT;
- }
-
- req->dma_mapped = 1;
- }
-
+ req->dma = dma_map_single(req->buf, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
return 0;
}
-EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev);
-
-int usb_gadget_map_request(struct usb_gadget *gadget,
- struct usb_request *req, int is_in)
-{
- return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in);
-}
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
-void usb_gadget_unmap_request_by_dev(struct device *dev,
+void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
- if (req->length == 0 || req->sg_was_mapped)
+ if (req->length == 0)
return;
- if (req->num_mapped_sgs) {
- dma_unmap_sg(dev, req->sg, req->num_sgs,
- is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
-
- req->num_mapped_sgs = 0;
- } else if (req->dma_mapped) {
- dma_unmap_single(dev, req->dma, req->length,
- is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->dma_mapped = 0;
- }
-}
-EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
-
-void usb_gadget_unmap_request(struct usb_gadget *gadget,
- struct usb_request *req, int is_in)
-{
- usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in);
+ dma_unmap_single(req->dma, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
-#endif /* CONFIG_HAS_DMA */
-
/* ------------------------------------------------------------------------- */
/**
@@ -991,11 +717,6 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req)
{
- if (likely(req->status == 0))
- usb_led_activity(USB_LED_EVENT_GADGET);
-
- trace_usb_gadget_giveback_request(ep, req, 0);
-
req->complete(ep, req);
}
EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
@@ -1111,71 +832,13 @@ EXPORT_SYMBOL_GPL(usb_gadget_check_config);
/* ------------------------------------------------------------------------- */
-static void usb_gadget_state_work(struct work_struct *work)
-{
- struct usb_gadget *gadget = work_to_gadget(work);
- struct usb_udc *udc = gadget->udc;
-
- if (udc)
- sysfs_notify(&udc->dev.kobj, NULL, "state");
-}
-
void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state)
{
gadget->state = state;
- schedule_work(&gadget->work);
}
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
-/* ------------------------------------------------------------------------- */
-
-/* Acquire connect_lock before calling this function. */
-static int usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock)
-{
- if (udc->vbus)
- return usb_gadget_connect_locked(udc->gadget);
- else
- return usb_gadget_disconnect_locked(udc->gadget);
-}
-
-static void vbus_event_work(struct work_struct *work)
-{
- struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work);
-
- mutex_lock(&udc->connect_lock);
- usb_udc_connect_control_locked(udc);
- mutex_unlock(&udc->connect_lock);
-}
-
-/**
- * usb_udc_vbus_handler - updates the udc core vbus status, and try to
- * connect or disconnect gadget
- * @gadget: The gadget which vbus change occurs
- * @status: The vbus status
- *
- * The udc driver calls it when it wants to connect or disconnect gadget
- * according to vbus status.
- *
- * This function can be invoked from interrupt context by irq handlers of
- * the gadget drivers, however, usb_udc_connect_control() has to run in
- * non-atomic context due to the following:
- * a. Some of the gadget driver implementations expect the ->pullup
- * callback to be invoked in non-atomic context.
- * b. usb_gadget_disconnect() acquires udc_lock which is a mutex.
- * Hence offload invocation of usb_udc_connect_control() to workqueue.
- */
-void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
-{
- struct usb_udc *udc = gadget->udc;
-
- if (udc) {
- udc->vbus = status;
- schedule_work(&udc->vbus_work);
- }
-}
-EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
-
/**
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which bus reset occurs
@@ -1277,6 +940,36 @@ static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
gadget->ops->udc_set_speed(gadget, s);
}
+static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
+{
+ int ret;
+
+ dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
+ driver->function);
+
+ udc->driver = driver;
+
+ usb_gadget_udc_set_speed(udc, driver->speed);
+
+ ret = driver->bind(udc->gadget);
+ if (ret)
+ goto err1;
+ ret = usb_gadget_udc_start_locked(udc);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_gadget_connect(udc->gadget);
+
+ return 0;
+err1:
+ if (ret != -EISNAM)
+ dev_err(&udc->dev, "failed to start %s: %d\n",
+ udc->driver->function, ret);
+ udc->driver = NULL;
+ return ret;
+}
+
/**
* usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks
* @udc: The UDC which should enable async callbacks
@@ -1336,17 +1029,9 @@ static void usb_udc_release(struct device *dev)
struct usb_udc *udc;
udc = container_of(dev, struct usb_udc, dev);
- dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
kfree(udc);
}
-static const struct attribute_group *usb_udc_attr_groups[];
-
-static void usb_udc_nop_release(struct device *dev)
-{
- dev_vdbg(dev, "%s\n", __func__);
-}
-
/**
* usb_initialize_gadget - initialize a gadget and its embedded struct device
* @parent: the parent device to this udc. Usually the controller driver's
@@ -1357,16 +1042,8 @@ static void usb_udc_nop_release(struct device *dev)
void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
- INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent;
-
- if (release)
- gadget->dev.release = release;
- else
- gadget->dev.release = usb_udc_nop_release;
-
- device_initialize(&gadget->dev);
- gadget->dev.bus = &gadget_bus_type;
+ gadget->dev.release = release;
}
EXPORT_SYMBOL_GPL(usb_initialize_gadget);
@@ -1386,15 +1063,9 @@ int usb_add_gadget(struct usb_gadget *gadget)
if (!udc)
goto error;
- device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
- udc->dev.class = &udc_class;
- udc->dev.groups = usb_udc_attr_groups;
+ udc->dev.class = NULL;
udc->dev.parent = gadget->dev.parent;
- ret = dev_set_name(&udc->dev, "%s",
- kobject_name(&gadget->dev.parent->kobj));
- if (ret)
- goto err_put_udc;
udc->gadget = gadget;
gadget->udc = udc;
@@ -1405,50 +1076,11 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
mutex_unlock(&udc_lock);
- INIT_WORK(&udc->vbus_work, vbus_event_work);
-
- ret = device_add(&udc->dev);
- if (ret)
- goto err_unlist_udc;
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
- udc->vbus = true;
-
- ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL);
- if (ret < 0)
- goto err_del_udc;
- gadget->id_number = ret;
- dev_set_name(&gadget->dev, "gadget.%d", ret);
-
- ret = device_add(&gadget->dev);
- if (ret)
- goto err_free_id;
-
- ret = sysfs_create_link(&udc->dev.kobj,
- &gadget->dev.kobj, "gadget");
- if (ret)
- goto err_del_gadget;
return 0;
- err_del_gadget:
- device_del(&gadget->dev);
-
- err_free_id:
- ida_free(&gadget_id_numbers, gadget->id_number);
-
- err_del_udc:
- flush_work(&gadget->work);
- device_del(&udc->dev);
-
- err_unlist_udc:
- mutex_lock(&udc_lock);
- list_del(&udc->list);
- mutex_unlock(&udc_lock);
-
- err_put_udc:
- put_device(&udc->dev);
-
error:
return ret;
}
@@ -1467,13 +1099,8 @@ EXPORT_SYMBOL_GPL(usb_add_gadget);
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
- int ret;
-
usb_initialize_gadget(parent, gadget, release);
- ret = usb_add_gadget(gadget);
- if (ret)
- usb_put_gadget(gadget);
- return ret;
+ return usb_add_gadget(gadget);
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
@@ -1498,7 +1125,7 @@ char *usb_get_gadget_udc_name(void)
mutex_lock(&udc_lock);
list_for_each_entry(udc, &udc_list, list) {
if (!udc->driver) {
- name = kstrdup(udc->gadget->name, GFP_KERNEL);
+ name = strdup(udc->gadget->name);
break;
}
}
@@ -1521,409 +1148,119 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
-/**
- * usb_del_gadget - deletes a gadget and unregisters its udc
- * @gadget: the gadget to be deleted.
- *
- * This will unbind @gadget, if it is bound.
- * It will not do a final usb_put_gadget().
- */
-void usb_del_gadget(struct usb_gadget *gadget)
+static void usb_gadget_remove_driver(struct usb_udc *udc)
{
- struct usb_udc *udc = gadget->udc;
-
- if (!udc)
- return;
-
- dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+ dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
+ udc->driver->function);
- mutex_lock(&udc_lock);
- list_del(&udc->list);
- mutex_unlock(&udc_lock);
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
+ udc->driver->unbind(udc->gadget);
+ usb_gadget_udc_stop_locked(udc);
- kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
- sysfs_remove_link(&udc->dev.kobj, "gadget");
- device_del(&gadget->dev);
- flush_work(&gadget->work);
- ida_free(&gadget_id_numbers, gadget->id_number);
- cancel_work_sync(&udc->vbus_work);
- device_unregister(&udc->dev);
+ udc->driver = NULL;
}
-EXPORT_SYMBOL_GPL(usb_del_gadget);
/**
- * usb_del_gadget_udc - unregisters a gadget
- * @gadget: the gadget to be unregistered.
+ * usb_del_gadget_udc - deletes @udc from udc_list
+ * @gadget: the gadget to be removed.
*
- * Calls usb_del_gadget() and does a final usb_put_gadget().
+ * This, will call usb_gadget_unregister_driver() if
+ * the @udc is still busy.
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
{
- usb_del_gadget(gadget);
- usb_put_gadget(gadget);
-}
-EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
-
-/* ------------------------------------------------------------------------- */
-
-static int gadget_match_driver(struct device *dev, const struct device_driver *drv)
-{
- struct usb_gadget *gadget = dev_to_usb_gadget(dev);
- struct usb_udc *udc = gadget->udc;
- const struct usb_gadget_driver *driver = container_of(drv,
- struct usb_gadget_driver, driver);
-
- /* If the driver specifies a udc_name, it must match the UDC's name */
- if (driver->udc_name &&
- strcmp(driver->udc_name, dev_name(&udc->dev)) != 0)
- return 0;
-
- /* If the driver is already bound to a gadget, it doesn't match */
- if (driver->is_bound)
- return 0;
-
- /* Otherwise any gadget driver matches any UDC */
- return 1;
-}
-
-static int gadget_bind_driver(struct device *dev)
-{
- struct usb_gadget *gadget = dev_to_usb_gadget(dev);
- struct usb_udc *udc = gadget->udc;
- struct usb_gadget_driver *driver = container_of(dev->driver,
- struct usb_gadget_driver, driver);
- int ret = 0;
+ struct usb_udc *udc = NULL;
mutex_lock(&udc_lock);
- if (driver->is_bound) {
- mutex_unlock(&udc_lock);
- return -ENXIO; /* Driver binds to only one gadget */
- }
- driver->is_bound = true;
- udc->driver = driver;
- mutex_unlock(&udc_lock);
-
- dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function);
-
- usb_gadget_udc_set_speed(udc, driver->max_speed);
-
- ret = driver->bind(udc->gadget, driver);
- if (ret)
- goto err_bind;
-
- mutex_lock(&udc->connect_lock);
- ret = usb_gadget_udc_start_locked(udc);
- if (ret) {
- mutex_unlock(&udc->connect_lock);
- goto err_start;
- }
- usb_gadget_enable_async_callbacks(udc);
- udc->allow_connect = true;
- ret = usb_udc_connect_control_locked(udc);
- if (ret)
- goto err_connect_control;
-
- mutex_unlock(&udc->connect_lock);
-
- kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
- return 0;
-
- err_connect_control:
- udc->allow_connect = false;
- usb_gadget_disable_async_callbacks(udc);
- if (gadget->irq)
- synchronize_irq(gadget->irq);
- usb_gadget_udc_stop_locked(udc);
- mutex_unlock(&udc->connect_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
- err_start:
- driver->unbind(udc->gadget);
-
- err_bind:
- if (ret != -EISNAM)
- dev_err(&udc->dev, "failed to start %s: %d\n",
- driver->function, ret);
-
- mutex_lock(&udc_lock);
- udc->driver = NULL;
- driver->is_bound = false;
+ dev_err(gadget->dev.parent, "gadget not registered.\n");
mutex_unlock(&udc_lock);
- return ret;
-}
-
-static void gadget_unbind_driver(struct device *dev)
-{
- struct usb_gadget *gadget = dev_to_usb_gadget(dev);
- struct usb_udc *udc = gadget->udc;
- struct usb_gadget_driver *driver = udc->driver;
-
- dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
+ return;
- udc->allow_connect = false;
- cancel_work_sync(&udc->vbus_work);
- mutex_lock(&udc->connect_lock);
- usb_gadget_disconnect_locked(gadget);
- usb_gadget_disable_async_callbacks(udc);
- if (gadget->irq)
- synchronize_irq(gadget->irq);
- mutex_unlock(&udc->connect_lock);
-
- udc->driver->unbind(gadget);
-
- mutex_lock(&udc->connect_lock);
- usb_gadget_udc_stop_locked(udc);
- mutex_unlock(&udc->connect_lock);
+found:
+ dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
- mutex_lock(&udc_lock);
- driver->is_bound = false;
- udc->driver = NULL;
+ list_del(&udc->list);
mutex_unlock(&udc_lock);
- kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+ if (udc->driver)
+ usb_gadget_remove_driver(udc);
}
+EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
/* ------------------------------------------------------------------------- */
-int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
- struct module *owner, const char *mod_name)
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
- int ret;
+ struct usb_udc *udc = NULL;
+ int ret = -ENODEV;
- if (!driver || !driver->bind || !driver->setup)
+ if (!driver || !driver->unbind)
return -EINVAL;
- driver->driver.bus = &gadget_bus_type;
- driver->driver.owner = owner;
- driver->driver.mod_name = mod_name;
- driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
- ret = driver_register(&driver->driver);
- if (ret) {
- pr_warn("%s: driver registration failed: %d\n",
- driver->function, ret);
- return ret;
- }
-
mutex_lock(&udc_lock);
- if (!driver->is_bound) {
- if (driver->match_existing_only) {
- pr_warn("%s: couldn't find an available UDC or it's busy\n",
- driver->function);
- ret = -EBUSY;
- } else {
- pr_info("%s: couldn't find an available UDC\n",
- driver->function);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->driver == driver) {
+ usb_gadget_remove_driver(udc);
+ usb_gadget_set_state(udc->gadget,
+ USB_STATE_NOTATTACHED);
ret = 0;
+ break;
}
- }
- mutex_unlock(&udc_lock);
- if (ret)
- driver_unregister(&driver->driver);
+ mutex_unlock(&udc_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(usb_gadget_register_driver_owner);
-
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
-{
- if (!driver || !driver->unbind)
- return -EINVAL;
-
- driver_unregister(&driver->driver);
- return 0;
-}
EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
/* ------------------------------------------------------------------------- */
-static ssize_t srp_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t n)
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
- struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
-
- if (sysfs_streq(buf, "1"))
- usb_gadget_wakeup(udc->gadget);
-
- return n;
-}
-static DEVICE_ATTR_WO(srp);
-
-static ssize_t soft_connect_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t n)
-{
- struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
- ssize_t ret;
-
- device_lock(&udc->gadget->dev);
- if (!udc->driver) {
- dev_err(dev, "soft-connect without a gadget driver\n");
- ret = -EOPNOTSUPP;
- goto out;
- }
-
- if (sysfs_streq(buf, "connect")) {
- mutex_lock(&udc->connect_lock);
- usb_gadget_udc_start_locked(udc);
- usb_gadget_connect_locked(udc->gadget);
- mutex_unlock(&udc->connect_lock);
- } else if (sysfs_streq(buf, "disconnect")) {
- mutex_lock(&udc->connect_lock);
- usb_gadget_disconnect_locked(udc->gadget);
- usb_gadget_udc_stop_locked(udc);
- mutex_unlock(&udc->connect_lock);
- } else {
- dev_err(dev, "unsupported command '%s'\n", buf);
- ret = -EINVAL;
- goto out;
- }
-
- ret = n;
-out:
- device_unlock(&udc->gadget->dev);
- return ret;
-}
-static DEVICE_ATTR_WO(soft_connect);
-
-static ssize_t state_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
- struct usb_gadget *gadget = udc->gadget;
-
- return sprintf(buf, "%s\n", usb_state_string(gadget->state));
-}
-static DEVICE_ATTR_RO(state);
+ struct usb_udc *udc = NULL;
+ unsigned int udc_count = 0;
+ int ret;
-static ssize_t function_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
- struct usb_gadget_driver *drv;
- int rc = 0;
+ if (!driver || !driver->bind || !driver->setup)
+ return -EINVAL;
mutex_lock(&udc_lock);
- drv = udc->driver;
- if (drv && drv->function)
- rc = scnprintf(buf, PAGE_SIZE, "%s\n", drv->function);
- mutex_unlock(&udc_lock);
- return rc;
-}
-static DEVICE_ATTR_RO(function);
-
-#define USB_UDC_SPEED_ATTR(name, param) \
-ssize_t name##_show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
- return scnprintf(buf, PAGE_SIZE, "%s\n", \
- usb_speed_string(udc->gadget->param)); \
-} \
-static DEVICE_ATTR_RO(name)
-
-static USB_UDC_SPEED_ATTR(current_speed, speed);
-static USB_UDC_SPEED_ATTR(maximum_speed, max_speed);
-
-#define USB_UDC_ATTR(name) \
-ssize_t name##_show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
- struct usb_gadget *gadget = udc->gadget; \
- \
- return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
-} \
-static DEVICE_ATTR_RO(name)
-
-static USB_UDC_ATTR(is_otg);
-static USB_UDC_ATTR(is_a_peripheral);
-static USB_UDC_ATTR(b_hnp_enable);
-static USB_UDC_ATTR(a_hnp_support);
-static USB_UDC_ATTR(a_alt_hnp_support);
-static USB_UDC_ATTR(is_selfpowered);
-
-static struct attribute *usb_udc_attrs[] = {
- &dev_attr_srp.attr,
- &dev_attr_soft_connect.attr,
- &dev_attr_state.attr,
- &dev_attr_function.attr,
- &dev_attr_current_speed.attr,
- &dev_attr_maximum_speed.attr,
-
- &dev_attr_is_otg.attr,
- &dev_attr_is_a_peripheral.attr,
- &dev_attr_b_hnp_enable.attr,
- &dev_attr_a_hnp_support.attr,
- &dev_attr_a_alt_hnp_support.attr,
- &dev_attr_is_selfpowered.attr,
- NULL,
-};
-
-static const struct attribute_group usb_udc_attr_group = {
- .attrs = usb_udc_attrs,
-};
-
-static const struct attribute_group *usb_udc_attr_groups[] = {
- &usb_udc_attr_group,
- NULL,
-};
-
-static int usb_udc_uevent(const struct device *dev, struct kobj_uevent_env *env)
-{
- const struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
- int ret;
+ list_for_each_entry(udc, &udc_list, list) {
+ udc_count++;
- ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name);
- if (ret) {
- dev_err(dev, "failed to add uevent USB_UDC_NAME\n");
- return ret;
+ /* For now we take the first one */
+ if (!udc->driver)
+ goto found;
}
- mutex_lock(&udc_lock);
- if (udc->driver)
- ret = add_uevent_var(env, "USB_UDC_DRIVER=%s",
- udc->driver->function);
+ if (!udc_count)
+ printf("No UDC available in the system\n");
+ else
+ /* When this happens, users should 'unbind <class> <index>'
+ * using the output of 'dm tree' and looking at the line right
+ * after the USB peripheral/device controller.
+ */
+ printf("All UDCs in use (%d available), use the unbind command\n",
+ udc_count);
mutex_unlock(&udc_lock);
- if (ret) {
- dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
- return ret;
- }
-
- return 0;
-}
-
-static const struct class udc_class = {
- .name = "udc",
- .dev_uevent = usb_udc_uevent,
-};
-
-static const struct bus_type gadget_bus_type = {
- .name = "gadget",
- .probe = gadget_bind_driver,
- .remove = gadget_unbind_driver,
- .match = gadget_match_driver,
-};
-
-static int __init usb_udc_init(void)
-{
- int rc;
-
- rc = class_register(&udc_class);
- if (rc)
- return rc;
-
- rc = bus_register(&gadget_bus_type);
- if (rc)
- class_unregister(&udc_class);
- return rc;
+ return -ENODEV;
+found:
+ ret = udc_bind_to_driver(udc, driver);
+ mutex_unlock(&udc_lock);
+ return ret;
}
-subsys_initcall(usb_udc_init);
+EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
-static void __exit usb_udc_exit(void)
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
- bus_unregister(&gadget_bus_type);
- class_unregister(&udc_class);
+ return usb_gadget_probe_driver(driver);
}
-module_exit(usb_udc_exit);
+EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
MODULE_DESCRIPTION("UDC Framework");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c
index e3e0ceff43e6..bc1876bed1d6 100644
--- a/drivers/usb/host/xhci-dwc3.c
+++ b/drivers/usb/host/xhci-dwc3.c
@@ -51,7 +51,7 @@ static void dwc3_phy_reset(struct dwc3 *dwc3_reg)
clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
}
-void dwc3_core_soft_reset(struct dwc3 *dwc3_reg)
+static void _dwc3_core_soft_reset(struct dwc3 *dwc3_reg)
{
/* Before Resetting PHY, put Core in Reset */
setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
@@ -79,7 +79,7 @@ int dwc3_core_init(struct dwc3 *dwc3_reg)
return -1;
}
- dwc3_core_soft_reset(dwc3_reg);
+ _dwc3_core_soft_reset(dwc3_reg);
dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1);
diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c
index 6a2d422c4b8e..e7573bc649bd 100644
--- a/drivers/usb/host/xhci-exynos5.c
+++ b/drivers/usb/host/xhci-exynos5.c
@@ -92,7 +92,7 @@ static int xhci_usb_of_to_plat(struct udevice *dev)
return 0;
}
-static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
+void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
{
u32 reg;
diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
index 4b0bc5f02d10..07775d23d508 100644
--- a/drivers/usb/mtu3/mtu3_gadget_ep0.c
+++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
@@ -300,20 +300,20 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
u32 value = 0;
switch (le16_to_cpu(setup->wIndex) >> 8) {
- case TEST_J:
- dev_dbg(mtu->dev, "TEST_J\n");
+ case USB_TEST_J:
+ dev_dbg(mtu->dev, "USB_TEST_J\n");
mtu->test_mode_nr = TEST_J_MODE;
break;
- case TEST_K:
- dev_dbg(mtu->dev, "TEST_K\n");
+ case USB_TEST_K:
+ dev_dbg(mtu->dev, "USB_TEST_K\n");
mtu->test_mode_nr = TEST_K_MODE;
break;
- case TEST_SE0_NAK:
- dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
+ case USB_TEST_SE0_NAK:
+ dev_dbg(mtu->dev, "USB_TEST_SE0_NAK\n");
mtu->test_mode_nr = TEST_SE0_NAK_MODE;
break;
- case TEST_PACKET:
- dev_dbg(mtu->dev, "TEST_PACKET\n");
+ case USB_TEST_PACKET:
+ dev_dbg(mtu->dev, "USB_TEST_PACKET\n");
mtu->test_mode_nr = TEST_PACKET_MODE;
break;
default:
diff --git a/drivers/usb/musb-new/musb_gadget_ep0.c b/drivers/usb/musb-new/musb_gadget_ep0.c
index ea65326ab626..faad283da0cc 100644
--- a/drivers/usb/musb-new/musb_gadget_ep0.c
+++ b/drivers/usb/musb-new/musb_gadget_ep0.c
@@ -317,51 +317,43 @@ __acquires(musb->lock)
goto stall;
switch (ctrlrequest->wIndex >> 8) {
- case 1:
- pr_debug("TEST_J\n");
- /* TEST_J */
+ case USB_TEST_J:
+ pr_debug("USB_TEST_J\n");
musb->test_mode_nr =
MUSB_TEST_J;
break;
- case 2:
- /* TEST_K */
- pr_debug("TEST_K\n");
+ case USB_TEST_K:
+ pr_debug("USB_TEST_K\n");
musb->test_mode_nr =
MUSB_TEST_K;
break;
- case 3:
- /* TEST_SE0_NAK */
- pr_debug("TEST_SE0_NAK\n");
+ case USB_TEST_SE0_NAK:
+ pr_debug("USB_TEST_SE0_NAK\n");
musb->test_mode_nr =
MUSB_TEST_SE0_NAK;
break;
- case 4:
- /* TEST_PACKET */
- pr_debug("TEST_PACKET\n");
+ case USB_TEST_PACKET:
+ pr_debug("USB_TEST_PACKET\n");
musb->test_mode_nr =
MUSB_TEST_PACKET;
break;
case 0xc0:
- /* TEST_FORCE_HS */
pr_debug("TEST_FORCE_HS\n");
musb->test_mode_nr =
MUSB_TEST_FORCE_HS;
break;
case 0xc1:
- /* TEST_FORCE_FS */
pr_debug("TEST_FORCE_FS\n");
musb->test_mode_nr =
MUSB_TEST_FORCE_FS;
break;
case 0xc2:
- /* TEST_FIFO_ACCESS */
pr_debug("TEST_FIFO_ACCESS\n");
musb->test_mode_nr =
MUSB_TEST_FIFO_ACCESS;
break;
case 0xc3:
- /* TEST_FORCE_HOST */
pr_debug("TEST_FORCE_HOST\n");
musb->test_mode_nr =
MUSB_TEST_FORCE_HOST;
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c
index 43ab3245e5c3..16907c4681bc 100644
--- a/drivers/usb/musb-new/musb_uboot.c
+++ b/drivers/usb/musb-new/musb_uboot.c
@@ -383,45 +383,6 @@ int dm_usb_gadget_handle_interrupts(struct udevice *dev)
return gadget->isr(0, gadget);
}
-
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
-{
- int ret;
-
- if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind ||
- !driver->setup) {
- printf("bad parameter.\n");
- return -EINVAL;
- }
-
- if (!gadget) {
- printf("Controller uninitialized\n");
- return -ENXIO;
- }
-
- ret = musb_gadget_start(&gadget->g, driver);
- if (ret < 0) {
- printf("gadget_start failed with %d\n", ret);
- return ret;
- }
-
- ret = driver->bind(&gadget->g);
- if (ret < 0) {
- printf("bind failed with %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
-{
- if (driver->disconnect)
- driver->disconnect(&gadget->g);
- if (driver->unbind)
- driver->unbind(&gadget->g);
- return 0;
-}
#endif /* CONFIG_USB_MUSB_GADGET */
struct musb *musb_register(struct musb_hdrc_platform_data *plat, void *bdata,
diff --git a/include/dm/device_compat.h b/include/dm/device_compat.h
index aa9a6fbb5e3f..6b31a72768fd 100644
--- a/include/dm/device_compat.h
+++ b/include/dm/device_compat.h
@@ -119,4 +119,17 @@
#define dev_vdbg(dev, fmt, ...) \
__dev_printk(LOGL_DEBUG_CONTENT, dev, fmt, ##__VA_ARGS__)
+#define dev_err_probe(dev, err, fmt, ...) \
+ ({ \
+ int _err = (err); \
+ if (_err != -EPROBE_DEFER) { \
+ dev_err(dev, fmt, ##__VA_ARGS__); \
+ dev_err(dev, "[err=%d]", _err); \
+ } else { \
+ dev_dbg(dev, fmt, ##__VA_ARGS__); \
+ dev_dbg(dev, "[err=%d]", _err); \
+ } \
+ _err; \
+ })
+
#endif
diff --git a/include/dm/read.h b/include/dm/read.h
index 12dcde6645c7..201a149f310e 100644
--- a/include/dm/read.h
+++ b/include/dm/read.h
@@ -1273,6 +1273,52 @@ static inline phy_interface_t dev_read_phy_mode(const struct udevice *dev)
#endif /* CONFIG_DM_DEV_READ_INLINE */
+static inline int dev_count_u32(const struct udevice *dev,
+ const char *name)
+{
+ return dev_read_u32_array(dev, name, NULL, 0);
+}
+
+/* Linux compatibility */
+
+#define device_property_count_u32 dev_count_u32
+#define device_property_read_bool dev_read_bool
+#define device_property_read_u16 dev_read_u16
+#define device_property_read_u32_array dev_read_u32_array
+#define device_property_read_u32 dev_read_u32
+#define device_property_read_u8 dev_read_u8
+
+static inline int device_property_read_string(const struct udevice *dev,
+ const char *propname,
+ const char **val)
+{
+ *val = dev_read_string(dev, propname);
+ if (!*val)
+ return -ENOENT;
+ return 0;
+}
+
+static inline bool device_property_present(const struct udevice *dev,
+ const char *propname)
+{
+ return (dev_read_size(dev, propname) > 0);
+}
+
+static inline int device_property_count_u8(const struct udevice *dev,
+ const char *propname)
+{
+ return dev_read_size(dev, propname);
+}
+
+static inline int device_property_read_u8_array(const struct udevice *dev,
+ const char *propname,
+ u8 *out, size_t count)
+{
+ memcpy(out, dev_read_u8_array_ptr(dev, propname, count), count);
+
+ return 0;
+}
+
/**
* dev_for_each_subnode() - Helper function to iterate through subnodes
*
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 623814516175..64fd5f60b2fe 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -284,6 +284,7 @@ struct rw_semaphore { int i; };
#define up_write(...) do { } while (0)
#define down_read(...) do { } while (0)
#define up_read(...) do { } while (0)
+struct device_node;
struct device {
struct device *parent;
struct class *class;
@@ -292,6 +293,7 @@ struct device {
/* This is used from drivers/usb/musb-new subsystem only */
void *driver_data; /* data private to the driver */
void *device_data; /* data private to the device */
+ struct device_node *of_node; /* associated device tree node */
};
struct mutex { int i; };
struct kernel_param { int i; };
@@ -389,4 +391,17 @@ typedef unsigned long dmaaddr_t;
#define free_irq(irq, data) do {} while (0)
#define request_irq(nr, f, flags, nm, data) 0
+/* From include/linux/reset.h */
+
+struct reset_control;
+
+static inline int reset_control_assert(struct reset_control *rstc)
+{
+ return 0;
+}
+
+static inline int reset_control_deassert(struct reset_control *rstc)
+{
+ return 0;
+}
#endif
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index c8c553b930ac..6623c6ae95b3 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* This file holds USB constants and structures that are needed for
* USB device APIs. These are used by the USB device model, which is
@@ -5,7 +6,7 @@
* Wireless USB 1.0 (spread around). Linux has several APIs in C that
* need these:
*
- * - the master/host side Linux-USB kernel driver API;
+ * - the host side Linux-USB kernel driver API;
* - the "usbfs" user space API; and
* - the Linux "gadget" slave/device/peripheral side driver API.
*
@@ -131,11 +132,11 @@
* Test Mode Selectors
* See USB 2.0 spec Table 9-7
*/
-#define TEST_J 1
-#define TEST_K 2
-#define TEST_SE0_NAK 3
-#define TEST_PACKET 4
-#define TEST_FORCE_EN 5
+#define USB_TEST_J 1
+#define USB_TEST_K 2
+#define USB_TEST_SE0_NAK 3
+#define USB_TEST_PACKET 4
+#define USB_TEST_FORCE_ENABLE 5
/*
* New Feature Selectors as added by USB 3.0
@@ -873,9 +874,6 @@ struct usb_ss_cap_descriptor { /* Link Power Management */
__le16 bU2DevExitLat;
} __attribute__((packed));
-#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */
-#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x01F4 /* Less then 500 microsec */
-
#define USB_DT_USB_SS_CAP_SIZE 10
/*
@@ -1055,4 +1053,13 @@ struct usb_string {
const char *s;
};
+/* USB 3.2 SuperSpeed Plus phy signaling rate generation and lane count */
+
+enum usb_ssp_rate {
+ USB_SSP_GEN_UNKNOWN = 0,
+ USB_SSP_GEN_2x1,
+ USB_SSP_GEN_1x2,
+ USB_SSP_GEN_2x2,
+};
+
#endif /* __LINUX_USB_CH9_H */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index fe79bf64a0e1..e0f67475b537 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -88,6 +88,7 @@ struct usb_request {
dma_addr_t dma;
unsigned stream_id:16;
+ unsigned is_last:1;
unsigned no_interrupt:1;
unsigned zero:1;
unsigned short_not_ok:1;
@@ -97,6 +98,7 @@ struct usb_request {
void *context;
struct list_head list;
+ unsigned frame_number; /* ISO ONLY */
int status;
unsigned actual;
};
@@ -153,6 +155,8 @@ struct usb_ep_caps {
* @ops: Function pointers used to access hardware-specific operations.
* @ep_list:the gadget's ep_list holds all of its endpoints
* @caps:The structure describing types and directions supported by endoint.
+ * @enabled: The current endpoint enabled/disabled state.
+ * @claimed: True if this endpoint is claimed by a function.
* @maxpacket:The maximum packet size used on this endpoint. The initial
* value can sometimes be reduced (hardware allowing), according to
* the endpoint descriptor used to configure the endpoint.
@@ -162,6 +166,8 @@ struct usb_ep_caps {
* @max_streams: The maximum number of streams supported
* by this EP (0 - 16, actual number is 2^n)
* @maxburst: the maximum number of bursts supported by this EP (for usb3)
+ * @address: used to identify the endpoint when finding descriptor that
+ * matches connection speed
* @driver_data:for use by the gadget driver. all other fields are
* read-only to gadget drivers.
* @desc: endpoint descriptor. This pointer is set before the endpoint is
@@ -179,301 +185,73 @@ struct usb_ep {
const struct usb_ep_ops *ops;
struct list_head ep_list;
struct usb_ep_caps caps;
+ bool claimed;
bool enabled;
unsigned maxpacket:16;
unsigned maxpacket_limit:16;
unsigned max_streams:16;
unsigned maxburst:5;
+ u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
};
/*-------------------------------------------------------------------------*/
-/**
- * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint
- * @ep:the endpoint being configured
- * @maxpacket_limit:value of maximum packet size limit
- *
- * This function shoud be used only in UDC drivers to initialize endpoint
- * (usually in probe function).
- */
+#if IS_ENABLED(CONFIG_USB_GADGET)
+void usb_ep_set_maxpacket_limit(struct usb_ep *ep, unsigned maxpacket_limit);
+int usb_ep_enable(struct usb_ep *ep);
+int usb_ep_disable(struct usb_ep *ep);
+struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
+void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req);
+int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);
+int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req);
+int usb_ep_set_halt(struct usb_ep *ep);
+int usb_ep_clear_halt(struct usb_ep *ep);
+int usb_ep_set_wedge(struct usb_ep *ep);
+int usb_ep_fifo_status(struct usb_ep *ep);
+void usb_ep_fifo_flush(struct usb_ep *ep);
+#else
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
- unsigned maxpacket_limit)
-{
- ep->maxpacket_limit = maxpacket_limit;
- ep->maxpacket = maxpacket_limit;
-}
-
-/**
- * usb_ep_enable - configure endpoint, making it usable
- * @ep:the endpoint being configured. may not be the endpoint named "ep0".
- * drivers discover endpoints through the ep_list of a usb_gadget.
- * @desc:descriptor for desired behavior. caller guarantees this pointer
- * remains valid until the endpoint is disabled; the data byte order
- * is little-endian (usb-standard).
- *
- * when configurations are set, or when interface settings change, the driver
- * will enable or disable the relevant endpoints. while it is enabled, an
- * endpoint may be used for i/o until the driver receives a disconnect() from
- * the host or until the endpoint is disabled.
- *
- * the ep0 implementation (which calls this routine) must ensure that the
- * hardware capabilities of each endpoint match the descriptor provided
- * for it. for example, an endpoint named "ep2in-bulk" would be usable
- * for interrupt transfers as well as bulk, but it likely couldn't be used
- * for iso transfers or for endpoint 14. some endpoints are fully
- * configurable, with more generic names like "ep-a". (remember that for
- * USB, "in" means "towards the USB master".)
- *
- * returns zero, or a negative error code.
- */
-static inline int usb_ep_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc)
-{
- int ret;
-
- if (ep->enabled)
- return 0;
-
- ret = ep->ops->enable(ep, desc);
- if (ret)
- return ret;
-
- ep->enabled = true;
-
- return 0;
-}
-
-/**
- * usb_ep_disable - endpoint is no longer usable
- * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0".
- *
- * no other task may be using this endpoint when this is called.
- * any pending and uncompleted requests will complete with status
- * indicating disconnect (-ESHUTDOWN) before this call returns.
- * gadget drivers must call usb_ep_enable() again before queueing
- * requests to the endpoint.
- *
- * returns zero, or a negative error code.
- */
+ unsigned maxpacket_limit)
+{ }
+static inline int usb_ep_enable(struct usb_ep *ep)
+{ return 0; }
static inline int usb_ep_disable(struct usb_ep *ep)
-{
- int ret;
-
- if (!ep->enabled)
- return 0;
-
- ret = ep->ops->disable(ep);
- if (ret)
- return ret;
-
- ep->enabled = false;
-
- return 0;
-}
-
-/**
- * usb_ep_alloc_request - allocate a request object to use with this endpoint
- * @ep:the endpoint to be used with with the request
- * @gfp_flags:GFP_* flags to use
- *
- * Request objects must be allocated with this call, since they normally
- * need controller-specific setup and may even need endpoint-specific
- * resources such as allocation of DMA descriptors.
- * Requests may be submitted with usb_ep_queue(), and receive a single
- * completion callback. Free requests with usb_ep_free_request(), when
- * they are no longer needed.
- *
- * Returns the request, or null if one could not be allocated.
- */
+{ return 0; }
static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
- gfp_t gfp_flags)
-{
- return ep->ops->alloc_request(ep, gfp_flags);
-}
-
-/**
- * usb_ep_free_request - frees a request object
- * @ep:the endpoint associated with the request
- * @req:the request being freed
- *
- * Reverses the effect of usb_ep_alloc_request().
- * Caller guarantees the request is not queued, and that it will
- * no longer be requeued (or otherwise used).
- */
+ gfp_t gfp_flags)
+{ return NULL; }
static inline void usb_ep_free_request(struct usb_ep *ep,
- struct usb_request *req)
-{
- ep->ops->free_request(ep, req);
-}
-
-/**
- * usb_ep_queue - queues (submits) an I/O request to an endpoint.
- * @ep:the endpoint associated with the request
- * @req:the request being submitted
- * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
- * pre-allocate all necessary memory with the request.
- *
- * This tells the device controller to perform the specified request through
- * that endpoint (reading or writing a buffer). When the request completes,
- * including being canceled by usb_ep_dequeue(), the request's completion
- * routine is called to return the request to the driver. Any endpoint
- * (except control endpoints like ep0) may have more than one transfer
- * request queued; they complete in FIFO order. Once a gadget driver
- * submits a request, that request may not be examined or modified until it
- * is given back to that driver through the completion callback.
- *
- * Each request is turned into one or more packets. The controller driver
- * never merges adjacent requests into the same packet. OUT transfers
- * will sometimes use data that's already buffered in the hardware.
- * Drivers can rely on the fact that the first byte of the request's buffer
- * always corresponds to the first byte of some USB packet, for both
- * IN and OUT transfers.
- *
- * Bulk endpoints can queue any amount of data; the transfer is packetized
- * automatically. The last packet will be short if the request doesn't fill it
- * out completely. Zero length packets (ZLPs) should be avoided in portable
- * protocols since not all usb hardware can successfully handle zero length
- * packets. (ZLPs may be explicitly written, and may be implicitly written if
- * the request 'zero' flag is set.) Bulk endpoints may also be used
- * for interrupt transfers; but the reverse is not true, and some endpoints
- * won't support every interrupt transfer. (Such as 768 byte packets.)
- *
- * Interrupt-only endpoints are less functional than bulk endpoints, for
- * example by not supporting queueing or not handling buffers that are
- * larger than the endpoint's maxpacket size. They may also treat data
- * toggle differently.
- *
- * Control endpoints ... after getting a setup() callback, the driver queues
- * one response (even if it would be zero length). That enables the
- * status ack, after transfering data as specified in the response. Setup
- * functions may return negative error codes to generate protocol stalls.
- * (Note that some USB device controllers disallow protocol stall responses
- * in some cases.) When control responses are deferred (the response is
- * written after the setup callback returns), then usb_ep_set_halt() may be
- * used on ep0 to trigger protocol stalls.
- *
- * For periodic endpoints, like interrupt or isochronous ones, the usb host
- * arranges to poll once per interval, and the gadget driver usually will
- * have queued some data to transfer at that time.
- *
- * Returns zero, or a negative error code. Endpoints that are not enabled
- * report errors; errors will also be
- * reported when the usb peripheral is disconnected.
- */
-static inline int usb_ep_queue(struct usb_ep *ep,
- struct usb_request *req, gfp_t gfp_flags)
-{
- return ep->ops->queue(ep, req, gfp_flags);
-}
-
-/**
- * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
- * @ep:the endpoint associated with the request
- * @req:the request being canceled
- *
- * if the request is still active on the endpoint, it is dequeued and its
- * completion routine is called (with status -ECONNRESET); else a negative
- * error code is returned.
- *
- * note that some hardware can't clear out write fifos (to unlink the request
- * at the head of the queue) except as part of disconnecting from usb. such
- * restrictions prevent drivers from supporting configuration changes,
- * even to configuration zero (a "chapter 9" requirement).
- */
+ struct usb_request *req)
+{ }
+static inline int usb_ep_queue(struct usb_ep *ep, struct usb_request *req,
+ gfp_t gfp_flags)
+{ return 0; }
static inline int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
-{
- return ep->ops->dequeue(ep, req);
-}
-
-/**
- * usb_ep_set_halt - sets the endpoint halt feature.
- * @ep: the non-isochronous endpoint being stalled
- *
- * Use this to stall an endpoint, perhaps as an error report.
- * Except for control endpoints,
- * the endpoint stays halted (will not stream any data) until the host
- * clears this feature; drivers may need to empty the endpoint's request
- * queue first, to make sure no inappropriate transfers happen.
- *
- * Note that while an endpoint CLEAR_FEATURE will be invisible to the
- * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the
- * current altsetting, see usb_ep_clear_halt(). When switching altsettings,
- * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
- *
- * Returns zero, or a negative error code. On success, this call sets
- * underlying hardware state that blocks data transfers.
- * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
- * transfer requests are still queued, or if the controller hardware
- * (usually a FIFO) still holds bytes that the host hasn't collected.
- */
+{ return 0; }
static inline int usb_ep_set_halt(struct usb_ep *ep)
-{
- return ep->ops->set_halt(ep, 1);
-}
-
-/**
- * usb_ep_clear_halt - clears endpoint halt, and resets toggle
- * @ep:the bulk or interrupt endpoint being reset
- *
- * Use this when responding to the standard usb "set interface" request,
- * for endpoints that aren't reconfigured, after clearing any other state
- * in the endpoint's i/o queue.
- *
- * Returns zero, or a negative error code. On success, this call clears
- * the underlying hardware state reflecting endpoint halt and data toggle.
- * Note that some hardware can't support this request (like pxa2xx_udc),
- * and accordingly can't correctly implement interface altsettings.
- */
+{ return 0; }
static inline int usb_ep_clear_halt(struct usb_ep *ep)
-{
- return ep->ops->set_halt(ep, 0);
-}
-
-/**
- * usb_ep_fifo_status - returns number of bytes in fifo, or error
- * @ep: the endpoint whose fifo status is being checked.
- *
- * FIFO endpoints may have "unclaimed data" in them in certain cases,
- * such as after aborted transfers. Hosts may not have collected all
- * the IN data written by the gadget driver (and reported by a request
- * completion). The gadget driver may not have collected all the data
- * written OUT to it by the host. Drivers that need precise handling for
- * fault reporting or recovery may need to use this call.
- *
- * This returns the number of such bytes in the fifo, or a negative
- * errno if the endpoint doesn't use a FIFO or doesn't support such
- * precise handling.
- */
+{ return 0; }
+static inline int usb_ep_set_wedge(struct usb_ep *ep)
+{ return 0; }
static inline int usb_ep_fifo_status(struct usb_ep *ep)
-{
- if (ep->ops->fifo_status)
- return ep->ops->fifo_status(ep);
- else
- return -EOPNOTSUPP;
-}
-
-/**
- * usb_ep_fifo_flush - flushes contents of a fifo
- * @ep: the endpoint whose fifo is being flushed.
- *
- * This call may be used to flush the "unclaimed data" that may exist in
- * an endpoint fifo after abnormal transaction terminations. The call
- * must never be used except when endpoint is not being used for any
- * protocol translation.
- */
+{ return 0; }
static inline void usb_ep_fifo_flush(struct usb_ep *ep)
-{
- if (ep->ops->fifo_flush)
- ep->ops->fifo_flush(ep);
-}
+{ }
+#endif /* USB_GADGET */
/*-------------------------------------------------------------------------*/
struct usb_dcd_config_params {
__u8 bU1devExitLat; /* U1 Device exit Latency */
+#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */
__le16 bU2DevExitLat; /* U2 Device exit Latency */
+#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */
+ __u8 besl_baseline; /* Recommended baseline BESL (0-15) */
+ __u8 besl_deep; /* Recommended deep BESL (0-15) */
+#define USB_DEFAULT_BESL_UNSPECIFIED 0xFF /* No recommended value */
};
struct usb_gadget;
@@ -491,7 +269,8 @@ struct usb_gadget_ops {
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
- void (*get_config_params)(struct usb_dcd_config_params *);
+ void (*get_config_params)(struct usb_gadget *,
+ struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *,
struct usb_gadget_driver *);
int (*udc_stop)(struct usb_gadget *);
@@ -503,6 +282,10 @@ struct usb_gadget_ops {
struct usb_endpoint_descriptor *);
void (*udc_set_speed)(struct usb_gadget *gadget,
enum usb_device_speed);
+ void (*udc_set_ssp_rate)(struct usb_gadget *gadget,
+ enum usb_ssp_rate rate);
+ void (*udc_async_callbacks)(struct usb_gadget *gadget, bool enable);
+ int (*check_config)(struct usb_gadget *gadget);
};
/**
@@ -552,13 +335,23 @@ struct usb_gadget_ops {
* device is acting as a B-Peripheral (so is_a_peripheral is false).
*/
struct usb_gadget {
+ struct usb_udc *udc;
/* readonly to gadget driver */
const struct usb_gadget_ops *ops;
struct usb_ep *ep0;
struct list_head ep_list; /* of usb_ep */
enum usb_device_speed speed;
enum usb_device_speed max_speed;
+
+ /* USB SuperSpeed Plus only */
+ enum usb_ssp_rate ssp_rate;
+ enum usb_ssp_rate max_ssp_rate;
+
enum usb_device_state state;
+ unsigned isoch_delay;
+
+ unsigned out_epnum;
+ unsigned in_epnum;
unsigned is_dualspeed:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
@@ -568,6 +361,10 @@ struct usb_gadget {
const char *name;
struct device dev;
unsigned quirk_ep_out_aligned_size:1;
+ unsigned is_selfpowered:1;
+ unsigned deactivated:1;
+ unsigned wakeup_armed:1;
+ unsigned connected:1;
};
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
@@ -585,6 +382,15 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
return container_of(dev, struct usb_gadget, dev);
}
+static inline void usb_put_gadget(struct usb_gadget *gadget)
+{
+}
+
+void usb_initialize_gadget(struct device *parent,
+ struct usb_gadget *gadget,
+ void (*release)(struct device *dev));
+int usb_add_gadget(struct usb_gadget *gadget);
+
/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
#define gadget_for_each_ep(tmp, gadget) \
list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)
@@ -630,168 +436,18 @@ static inline int gadget_is_superspeed(struct usb_gadget *g)
return g->max_speed >= USB_SPEED_SUPER;
}
-/**
- * usb_gadget_frame_number - returns the current frame number
- * @gadget: controller that reports the frame number
- *
- * Returns the usb frame number, normally eleven bits from a SOF packet,
- * or negative errno if this device doesn't support this capability.
- */
-static inline int usb_gadget_frame_number(struct usb_gadget *gadget)
-{
- return gadget->ops->get_frame(gadget);
-}
-
-/**
- * usb_gadget_wakeup - tries to wake up the host connected to this gadget
- * @gadget: controller used to wake up the host
- *
- * Returns zero on success, else negative error code if the hardware
- * doesn't support such attempts, or its support has not been enabled
- * by the usb host. Drivers must return device descriptors that report
- * their ability to support this, or hosts won't enable it.
- *
- * This may also try to use SRP to wake the host and start enumeration,
- * even if OTG isn't otherwise in use. OTG devices may also start
- * remote wakeup even when hosts don't explicitly enable it.
- */
-static inline int usb_gadget_wakeup(struct usb_gadget *gadget)
-{
- if (!gadget->ops->wakeup)
- return -EOPNOTSUPP;
- return gadget->ops->wakeup(gadget);
-}
-
-/**
- * usb_gadget_set_selfpowered - sets the device selfpowered feature.
- * @gadget:the device being declared as self-powered
- *
- * this affects the device status reported by the hardware driver
- * to reflect that it now has a local power supply.
- *
- * returns zero on success, else negative errno.
- */
-static inline int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
-{
- if (!gadget->ops->set_selfpowered)
- return -EOPNOTSUPP;
- return gadget->ops->set_selfpowered(gadget, 1);
-}
-
-/**
- * usb_gadget_clear_selfpowered - clear the device selfpowered feature.
- * @gadget:the device being declared as bus-powered
- *
- * this affects the device status reported by the hardware driver.
- * some hardware may not support bus-powered operation, in which
- * case this feature's value can never change.
- *
- * returns zero on success, else negative errno.
- */
-static inline int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
-{
- if (!gadget->ops->set_selfpowered)
- return -EOPNOTSUPP;
- return gadget->ops->set_selfpowered(gadget, 0);
-}
-
-/**
- * usb_gadget_vbus_connect - Notify controller that VBUS is powered
- * @gadget:The device which now has VBUS power.
- *
- * This call is used by a driver for an external transceiver (or GPIO)
- * that detects a VBUS power session starting. Common responses include
- * resuming the controller, activating the D+ (or D-) pullup to let the
- * host detect that a USB device is attached, and starting to draw power
- * (8mA or possibly more, especially after SET_CONFIGURATION).
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_vbus_connect(struct usb_gadget *gadget)
-{
- if (!gadget->ops->vbus_session)
- return -EOPNOTSUPP;
- return gadget->ops->vbus_session(gadget, 1);
-}
-
-/**
- * usb_gadget_vbus_draw - constrain controller's VBUS power usage
- * @gadget:The device whose VBUS usage is being described
- * @mA:How much current to draw, in milliAmperes. This should be twice
- * the value listed in the configuration descriptor bMaxPower field.
- *
- * This call is used by gadget drivers during SET_CONFIGURATION calls,
- * reporting how much power the device may consume. For example, this
- * could affect how quickly batteries are recharged.
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
-{
- if (!gadget->ops->vbus_draw)
- return -EOPNOTSUPP;
- return gadget->ops->vbus_draw(gadget, mA);
-}
-
-/**
- * usb_gadget_vbus_disconnect - notify controller about VBUS session end
- * @gadget:the device whose VBUS supply is being described
- *
- * This call is used by a driver for an external transceiver (or GPIO)
- * that detects a VBUS power session ending. Common responses include
- * reversing everything done in usb_gadget_vbus_connect().
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
-{
- if (!gadget->ops->vbus_session)
- return -EOPNOTSUPP;
- return gadget->ops->vbus_session(gadget, 0);
-}
-
-/**
- * usb_gadget_connect - software-controlled connect to USB host
- * @gadget:the peripheral being connected
- *
- * Enables the D+ (or potentially D-) pullup. The host will start
- * enumerating this gadget when the pullup is active and a VBUS session
- * is active (the link is powered). This pullup is always enabled unless
- * usb_gadget_disconnect() has been used to disable it.
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_connect(struct usb_gadget *gadget)
-{
- if (!gadget->ops->pullup)
- return -EOPNOTSUPP;
- return gadget->ops->pullup(gadget, 1);
-}
-
-/**
- * usb_gadget_disconnect - software-controlled disconnect from USB host
- * @gadget:the peripheral being disconnected
- *
- * Disables the D+ (or potentially D-) pullup, which the host may see
- * as a disconnect (when a VBUS session is active). Not all systems
- * support software pullup controls.
- *
- * This routine may be used during the gadget driver bind() call to prevent
- * the peripheral from ever being visible to the USB host, unless later
- * usb_gadget_connect() is called. For example, user mode components may
- * need to be activated before the system can talk to hosts.
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
-{
- if (!gadget->ops->pullup)
- return -EOPNOTSUPP;
- return gadget->ops->pullup(gadget, 0);
-}
+int usb_gadget_frame_number(struct usb_gadget *gadget);
+int usb_gadget_wakeup(struct usb_gadget *gadget);
+int usb_gadget_set_selfpowered(struct usb_gadget *gadget);
+int usb_gadget_clear_selfpowered(struct usb_gadget *gadget);
+int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA);
+int usb_gadget_vbus_disconnect(struct usb_gadget *gadget);
+int usb_gadget_connect(struct usb_gadget *gadget);
+int usb_gadget_disconnect(struct usb_gadget *gadget);
/*-------------------------------------------------------------------------*/
+
/**
* struct usb_gadget_driver - driver for usb 'slave' devices
* @function: String describing the gadget's function
@@ -980,6 +636,12 @@ extern void usb_gadget_giveback_request(struct usb_ep *ep,
/*-------------------------------------------------------------------------*/
+/* utility to check if endpoint caps match descriptor needs */
+
+extern int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
+ struct usb_ep *ep, struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp);
+
/* utility wrapping a simple endpoint selection policy */
extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index e7e3d259cae5..9fa095ff3a72 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -46,4 +46,14 @@ enum usb_dr_mode usb_get_role_switch_default_mode(ofnode node);
*/
enum usb_device_speed usb_get_maximum_speed(ofnode node);
+/**
+ * usb_get_maximum_ssp_rate - Get the signaling rate generation and lane count
+ * of a SuperSpeed Plus capable device.
+ * @node: ofnode of the given device
+ *
+ * If the string from "maximum-speed" property is super-speed-plus-genXxY where
+ * 'X' is the generation number and 'Y' is the number of lanes, then this
+ * function returns the corresponding enum usb_ssp_rate.
+ */
+enum usb_ssp_rate usb_get_maximum_ssp_rate(ofnode node);
#endif /* __LINUX_USB_OTG_H */
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index 14b2c7eb2e63..9448d2562908 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -21,6 +21,13 @@ enum usb_phy_interface {
USBPHY_INTERFACE_MODE_HSIC,
};
+/* associate a type with PHY */
+enum usb_phy_type {
+ USB_PHY_TYPE_UNDEFINED,
+ USB_PHY_TYPE_USB2,
+ USB_PHY_TYPE_USB3,
+};
+
#if CONFIG_IS_ENABLED(DM_USB)
/**
* usb_get_phy_mode - Get phy mode for given device_node
@@ -37,4 +44,53 @@ static inline enum usb_phy_interface usb_get_phy_mode(ofnode node)
}
#endif
+struct usb_phy {
+ struct device *dev;
+
+ /* initialize/shutdown the phy */
+ int (*init)(struct usb_phy *x);
+ void (*shutdown)(struct usb_phy *x);
+
+ /* enable/disable VBUS */
+ int (*set_vbus)(struct usb_phy *x, int on);
+
+ /* effective for B devices, ignored for A-peripheral */
+ int (*set_power)(struct usb_phy *x,
+ unsigned mA);
+};
+
+static inline int
+usb_phy_init(struct usb_phy *x)
+{
+ if (x && x->init)
+ return x->init(x);
+
+ return 0;
+}
+
+static inline void
+usb_phy_shutdown(struct usb_phy *x)
+{
+ if (x && x->shutdown)
+ x->shutdown(x);
+}
+
+static inline int
+usb_phy_set_power(struct usb_phy *x, unsigned mA)
+{
+ if (!x)
+ return 0;
+
+ /* TODO usb_phy_set_charger_current(x, mA); */
+
+ if (x->set_power)
+ return x->set_power(x, mA);
+ return 0;
+}
+
+static inline int
+usb_phy_set_suspend(struct usb_phy *x, int suspend)
+{
+ return 0;
+}
#endif /* __LINUX_USB_PHY_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 02/64] usb: dwc3: import from kernel v3.19
2026-05-07 9:27 ` [RFC PATCH v2 02/64] usb: dwc3: import from kernel v3.19 Jens Wiklander
@ 2026-05-08 15:25 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:25 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v3.19
>
> Sync Linux kernel dwc3 changes from v3.19-rc1 to v3.19.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v3.19-rc1..v3.19
> Commits imported:
> 39e60635a015 usb: dwc3: gadget: Stop TRB preparation after limit is reached
> ec512fb8e561 usb: dwc3: gadget: Fix TRB preparation during SG
> 84a2b61b6eb9 usb: dwc3: pci: add support for Intel Sunrise Point PCH
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/gadget.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 04/64] usb: dwc3: import from kernel v4.1
2026-05-07 9:27 ` [RFC PATCH v2 04/64] usb: dwc3: import from kernel v4.1 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.1
>
> Sync Linux kernel dwc3 changes from v4.0 to v4.1.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.0..v4.1
> Commits imported:
> 459e210c4fd0 usb: dwc3: gadget: Fix incorrect DEPCMD and DGCMD status macros
> [...]
>
> drivers/usb/dwc3/core.c | 59 +++++++++++++++++++++++++---------------
> drivers/usb/dwc3/core.h | 6 ++--
> drivers/usb/dwc3/gadget.c | 37 +++++++++++--------------
> drivers/usb/dwc3/platform_data.h | 1 +
> 4 files changed, 58 insertions(+), 45 deletions(-)
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> @@ -811,17 +811,13 @@ static int dwc3_probe(struct platform_device *pdev)
> Commits imported:
> 459e210c4fd0 usb: dwc3: gadget: Fix incorrect DEPCMD and DGCMD status macros
> 228321904089 usb: dwc3: dwc3-omap: correct the register macros
> eac68e8f979b usb: dwc3: make LPM configurable in DT
> adf9c3c85615 usb: move definition of PCI_VENDOR_ID_SYNOPSYS to linux/pci_ids.h
> 8f2c9544aba6 usb: dwc3: gadget: drop unnecessary loop when cleaning up TRBs
> 3d0184d08757 usb: dwc3: omap: call of_platform_depopulate() instead
> 3da1f6ee3563 usb: dwc3: core: only reset res->start in case of error
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 05/64] usb: dwc3: import from kernel v4.2
2026-05-07 9:27 ` [RFC PATCH v2 05/64] usb: dwc3: import from kernel v4.2 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.2
>
> Sync Linux kernel dwc3 changes from v4.1 to v4.2.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.1..v4.2
> Commits imported:
> aebda6187181 usb: dwc3: Reset the transfer resource index on SET_INTERFACE
> 43cacb03aabe usb: dwc3: core: avoid NULL pointer dereference
> 50641056d833 usb: dwc3: Use ASCII space in Kconfig
> 19915e623458 Merge 4.1-rc7 into usb-next
> e152813ff5fe Merge tag 'usb-for-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> e18b7975c885 usb: dwc3: gadget: don't clear EP_BUSY too early
> 76e838c9f776 usb: dwc3: gadget: return error if command sent to DEPCMD register fails
> 891b1dc02295 usb: dwc3: gadget: return error if command sent to DGCMD register fails
> 909eacd748b3 Merge 4.1-rc4 into usb-next
> 88bc9d194ff6 usb: dwc3: add ULPI interface support
> a89d977cc04c usb: dwc3: pci: add quirk for Baytrails
> 3e10a2ce98d1 usb: dwc3: add hsphy_interface property
> 45bb7de213d8 usb: dwc3: setup phys earlier
> c5cc74e8c12b usb: dwc3: soft reset to it's own function
> 2917e7181589 usb: dwc3: cache hwparams earlier
> 6c89cce0476f usb: dwc3: store driver data earlier
> f699b94789a6 usb: dwc3: ULPI or UTMI+ select
> b5699eeee68f usb: dwc3: USB2 PHY register access bits
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 110 ++++++++++++++++++++++++++++++---------
> drivers/usb/dwc3/core.h | 26 +++++++++
> drivers/usb/dwc3/ep0.c | 4 ++
> drivers/usb/dwc3/gadget.c | 10 +++-
> drivers/usb/dwc3/platform_data.h | 2 +
> 5 files changed, 126 insertions(+), 26 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 06/64] usb: dwc3: import from kernel v4.3
2026-05-07 9:27 ` [RFC PATCH v2 06/64] usb: dwc3: import from kernel v4.3 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.3
>
> Sync Linux kernel dwc3 changes from v4.2 to v4.3.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.2..v4.3
> Commits imported:
> a66c275b3d5d usb: dwc3: gadget: Fix BUG in RT config
> e2ae0692bf6f usb: dwc3: omap: enable irqs lately
> a474d3b73ba7 usb: dwc3: gadget: add ep capabilities support
> c0bd5456a470 usb: dwc3: ep0: handle non maxpacket aligned transfers > 512
> 2abd9d5fa60f usb: dwc3: ep0: Add chained TRB support
> 368ca113ca0a usb: dwc3; ep0: Modify _dwc3_ep0_start_trans_ API to take 'chain' parameter
> 8a3442205630 usb: dwc3: ep0: preparation for handling non maxpacket aligned transfers > 512
> 2e5464da4e7d usb: dwc3: ep0: use _roundup_ to calculate the transfer size
> b2fb5b1a0f50 usb: dwc3: ep0: Fix mem corruption on OUT transfers of more than 512 bytes
> aa7399744dd0 usb: dwc3: gadget: defer endpoint name change
> 7eaeac5c0e44 usb: dwc3: gadget: add a trace when disabling EPs
> d9972f470bcb usb: dwc3: core: remove unnecessary dev_warn()
> 2df033ca39b5 usb: dwc3: pci: make better use of gpiod API
> 5960387a2fb8 usb: dwc3: omap: Replace deprecated API of extcon
> 9fcfa463e17a usb: dwc3: drop CONFIG_USB_DWC3_DEBUG
> e746b06cc76a usb: dwc3: st: remove two unnecessary messages
> 2babd0d148a3 usb: dwc3: qcom: switch dev_dbg() to dev_info()
> 42f69a02e7ab usb: dwc3: exynos: switch dev_dbg() to dev_info()
> 9ff0fdca3b62 usb: dwc3: keystone: convert dev_dbg() to dev_err()
> e4f756675467 usb: dwc3: omap: drop dev_dbg() usage
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 2 --
> drivers/usb/dwc3/ep0.c | 92 +++++++++++++++++++++++++++++++++--------------
> drivers/usb/dwc3/gadget.c | 61 +++++++++++++++++--------------
> 3 files changed, 101 insertions(+), 54 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 07/64] usb: dwc3: import from kernel v4.4
2026-05-07 9:27 ` [RFC PATCH v2 07/64] usb: dwc3: import from kernel v4.4 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.4
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 08/64] usb: dwc3: import from kernel v4.5
2026-05-07 9:27 ` [RFC PATCH v2 08/64] usb: dwc3: import from kernel v4.5 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.5
>
> Sync Linux kernel dwc3 changes from v4.4 to v4.5.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.4..v4.5
> Commits imported:
> c450960187f4 usb: dwc3: Fix assignment of EP transfer resources
> 6a4290cc28be usb: dwc3: gadget: set the OTG flag in dwc3 gadget driver.
> 5072cfc40a80 usb: dwc3: of-simple: fix build warning on !PM
> d9261898a4b2 usb: dwc3: gadget: don't send extra ZLP
> 95ca961c758c usb: dwc3: gadget: pass a condition to dev_WARN_ONCE()
> 46a01427e969 usb: dwc3: trace: show request flags
> 04c03d10e507 usb: dwc3: gadget: handle request->zero
> b084662776be usb: dwc3: remove dwc3-qcom in favor of dwc3-of-simple
> 16adc674d0d6 usb: dwc3: add generic OF glue layer
> b5d335e5ea6a usb: dwc3: ep0: fix setup_packet_pending initialization
> ac7bdcc1b3ad usb: dwc3: gadget: simplify next_request() return check
> acc38c4970ca usb: dwc3: ep0: purge dev_dbg() calls
> 1407bf13e3bf usb: dwc3: core: purge dev_dbg() calls
> ec5e795cdefb usb: dwc3: gadget: purge dev_dbg() calls
> bb423984c28e usb: dwc3: gadget: simplify dwc3_gadget_ep_queue()
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 8 +-
> drivers/usb/dwc3/core.h | 4 +-
> drivers/usb/dwc3/ep0.c | 17 ++--
> drivers/usb/dwc3/gadget.c | 204 ++++++++++++++++++++++++++++++++--------------
> 4 files changed, 157 insertions(+), 76 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 09/64] usb: dwc3: import from kernel v4.6
2026-05-07 9:27 ` [RFC PATCH v2 09/64] usb: dwc3: import from kernel v4.6 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.6
>
> Sync Linux kernel dwc3 changes from v4.5 to v4.6.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.5..v4.6
> Commits imported:
> 9772b47a4c29 usb: dwc3: gadget: Fix suspend/resume during device mode
> e6bdf8195b4a usb: dwc3: fix memory leak of dwc->regset
> 5c4ad318de3b usb: dwc3: core: fix PHY handling during suspend
> 45d49cb706e5 usb: dwc3: omap: fix up error path on probe()
> adf9a3ab90eb usb: dwc3: keystone: drop dma_mask configuration
> 1ffb4d5cc78a usb: dwc3: pci: add ID for one more Intel Broxton platform
> e901aa159dac usb: dwc3: gadget: fix endpoint renaming
> ad14d4e0308a usb: dwc3: gadget: release spin lock during gadget resume
> f59dcab17629 usb: dwc3: core: improve reset sequence
> 33c1f638a0fe Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
> 77966eb85e6d usb: dwc3: Validate the maximum_speed parameter
> 7580862b3e80 usb: dwc3: Enable SuperSpeedPlus
> 2c7f1bd9127a usb: dwc3: Update maximum_speed for SuperSpeedPlus
> ee5cd41c9117 usb: dwc3: Update speed checks for SuperSpeedPlus
> 1f38f88a24c8 usb: dwc3: Update register fields for SuperSpeedPlus
> c4137a9c841e usb: dwc3: DWC_usb31 controller check
> 71e41bbb4379 Merge 4.5-rc6 into usb-next
> 3d755dcc20dd usb: dwc3: Remove impossible check for of_clk_get_parent_count() < 0
> 172ad9af55d2 Merge 4.5-rc4 into usb-next
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 54 ++++++++++++++++++++++++++++++++++++++++++-----
> drivers/usb/dwc3/core.h | 11 +++++++++-
> drivers/usb/dwc3/ep0.c | 9 +++++---
> drivers/usb/dwc3/gadget.c | 47 +++++++++++++++++++++++++++++++----------
> 4 files changed, 101 insertions(+), 20 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 10/64] usb: dwc3: import from kernel v4.7
2026-05-07 9:27 ` [RFC PATCH v2 10/64] usb: dwc3: import from kernel v4.7 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.7
>
> Sync Linux kernel dwc3 changes from v4.6 to v4.7.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.6..v4.7
> Commits imported:
> 0d064a7b9c43 Merge tag 'mfd-fixes-4.7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
> 5baaf3b9efe1 usb: dwc3: st: Use explicit reset_control_get_exclusive() API
> 4aa34ce3c1ee Merge tag 'mfd-fixes-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
> 002f17bc54ff usb: dwc3: st: Inform the reset framework that our reset line may be shared
> 50c763f8c1ba usb: dwc3: Set the ClearPendIN bit on Clear Stall EP command
> 27a0faafdca5 usb: dwc3: st: Fix USB_DR_MODE_PERIPHERAL configuration.
> 4879efb34f7d usb: dwc3: exynos: Fix deferred probing storm.
> 973986126a41 usb: dwc3: gadget: fix mask and shift order in DWC3_DCFG_NUMP()
> ce15bda10121 Merge tag 'usb-for-v4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> 2a58f9c12bb3 usb: dwc3: gadget: disable automatic calculation of ACK TP NUMP
> 676e34974481 usb: dwc3: gadget: update DCFG.NumP to max burst size
> 7b9cc7a2b101 usb: dwc3: gadget: give better command return code
> ce046d323d74 usb: dwc3: pci: pass the platform device as a parameter to dwc3_pci_quirks()
> 474799f07376 usb: dwc3: pci: make build-in device properties available
> ae4114137607 usb: dwc3: ep0: sanity check test mode selector
> 69b4269eff5f Merge 4.6-rc5 into usb-next
> 818ec3aba883 usb: dwc3: debugfs: dump out endpoint details
> b058f3e8a399 usb: dwc3: core: add helper to extract trb type
> cf6d867d3b57 usb: dwc3: core: add fifo space helper
> 4e9f311833a9 usb: dwc3: make dwc3_debugfs_init return value be void
> af566a0be6e4 usb: dwc3: omap: get rid of dma_status
> 8e7046b71dae usb: dwc3: gadget: don't interrupt when chained
> 052ba52efa17 usb: dwc3: gadget: remove newline from trace
> 36b68aae8e39 usb: dwc3: gadget: use link TRB for all endpoint types
> 4faf75504a7d usb: dwc3: gadget: move % operation to increment helpers
> ef966b9d3353 usb: dwc3: gadget: add trb enqueue/dequeue helpers
> 70fdb273db37 usb: dwc3: get rid of DWC3_TRB_MASK
> c28f82595dde usb: dwc3: switch trb enqueue/dequeue and first_trb_index to u8
> 5ef68c56e169 usb: dwc3: core: document struct dwc3_request
> 53fd88189e08 usb: dwc3: gadget: rename busy/free_slot to trb_enqueue/dequeue
> 495dd5f78145 usb: dwc3: omap: drop dma_mask configuration
> e58dd357741b usb: dwc3: add disable receiver detection in P3 quirk
> c36d8e947a56 usb: dwc3: gadget: put link to U0 before Start Transfer
> 218ef7b647e3 usb: dwc3: gadget: extract unlocked dwc3_gadget_wakeup()
> 2b0f11df84bb usb: dwc3: gadget: clear SUSPHY bit before ep cmds
> c0ca324d09a0 usb: dwc3: gadget: combine return points into a single one
> dea520a4a283 usb: dwc3: gadget: pass ev_buff as cookie to irq handler
> 696c8b128220 usb: dwc3: drop ev_buffs array
> 660e9bde74d6 usb: dwc3: remove num_event_buffers
> aa3342c8bb61 usb: dwc3: better name for our request management lists
> 8495036e986b usb: dwc3: increase maximum number of TRBs per endpoint
> ca4d44ea2a91 usb: dwc3: gadget: always enable CSP
> bc5081617fae usb: dwc3: drop FIFO resizing logic
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 118 +++++-----
> drivers/usb/dwc3/core.h | 85 ++++++--
> drivers/usb/dwc3/debug.h | 6 +-
> drivers/usb/dwc3/ep0.c | 43 ++--
> drivers/usb/dwc3/gadget.c | 457 +++++++++++++++++++--------------------
> drivers/usb/dwc3/gadget.h | 6 +-
> drivers/usb/dwc3/platform_data.h | 2 +-
> 7 files changed, 374 insertions(+), 343 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 11/64] usb: dwc3: import from kernel v4.8
2026-05-07 9:27 ` [RFC PATCH v2 11/64] usb: dwc3: import from kernel v4.8 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.8
>
> Sync Linux kernel dwc3 changes from v4.7 to v4.8.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.7..v4.8
> Commits imported:
> 696118c016dd usb: dwc3: pci: fix build warning on !PM_SLEEP
> 9d7aba7786b6 Revert "usb: dwc3: gadget: always decrement by 1"
> 6f8245b4e37c usb: dwc3: gadget: always decrement by 1
> 696fe69d7e63 usb: dwc3: debug: fix ep name on trace output
> d6011f6fc21b usb: dwc3: gadget: don't rely on jiffies while holding spinlock
> b74c2d875baa usb: dwc3: core: allow device to runtime_suspend several times
> f6c274e11e3b usb: dwc3: pci: runtime_resume child device
> a0ad85ae866f usb: dwc3: gadget: stop processing on HWO set
> 79d17482a409 usb: dwc3: don't set last bit for ISOC endpoints
> 4c4f106c032f usb: dwc3: fix missing platform_set_drvdata() in dwc3_of_simple_probe()
> 4491ed5042f0 usb: dwc3: pci: add Intel Kabylake PCI ID
> 7c705dfe2ebe usb: dwc3: gadget: always cleanup all TRBs
> e5b36ae2f851 usb: dwc3: gadget: fix for short pkts during chained xfers
> c7de57347183 usb: dwc3: gadget: increment request->actual once
> c318a821b982 Merge 4.7-rc6 into usb-next
> 322832f2f19e usb: dwc3: host: Fix broken XHCI host
> 215db9481814 Merge tag 'usb-for-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> 95b57df45062 usb: dwc3: host: use build-in property instead of platform data
> 72704f876f50 dwc3: gadget: Implement the suspend entry event handler
> 9522def40065 usb: dwc3: core: cleanup IRQ resources
> da1410be21bf usb: dwc3: gadget: Add the suspend state checking when stopping gadget
> 0e146028eebf usb: dwc3: gadget: issue ENDTRANSFER conditional on resource_index
> 328082376aea usb: dwc3: fix runtime PM in error path
> b6d4e16e8313 usb: dwc3: gadget: simplify run_stop() break condition
> f2df679b6c55 usb: dwc3: gadget: avoid while(1) in run_stop()
> d807bdd02845 usb: dwc3: gadget: remove udelay() from run_stop()
> 5f82279a0c76 usb: dwc3: core: fixup dr_mode fallback selection
> e6fe66fe08cd usb: dwc3: pci: add dr-mode for Intel dwc3
> 21e64bf20df5 usb: dwc3: gadget: rename 'ignore' argument to 'modify'
> bc71c2df45e5 Merge 4.7-rc4 into usb-next
> d6dc2e76a860 usb: dwc3: gadget: decrement trbs_left for each sg entry
> ba62c09d5cc2 usb: dwc3: gadget: start Bulk endpoints more frequently
> 13fa2e69b1dd usb: dwc3: gadget: disable XFER_NOT_READY
> 55a0237f8f47 usb: dwc3: gadget: use allocated/queued reqs for LST bit
> 69450c4dc164 usb: dwc3: gadget: halt and stop based HWO bit
> 68d34c8a744d usb: dwc3: gadget: keep track of allocated and queued reqs
> b6b1c6db4c7f usb: dwc3: gadget: update transfer needs transfer resource
> d18e65470a4d usb: dwc3: remove handling of platform data
> cf48305de0c6 usb: dwc3: pci: use build-in properties instead of platform data
> 60cfb37ac9bc usb: dwc3: remove trailing newline from dwc3_trace
> d2728fb3e01f usb: dwc3: omap: Pass VBUS and ID events transparently
> 9ab330bf4dfd usb: dwc3: omap: Don't set POWERPRESENT
> 12da8eae208d usb: dwc3: omap: Mark the interrupt handler as shared
> 3f586c92d877 usb: dwc3: omap: use request_threaded_irq()
> 475c8beb35e1 usb: dwc3: add DWC3_GUCTL1 reg for debug
> e77c561432bc usb: dwc3: Fix DWC3_USB31_REVISION_110A definition
> 2da9ad761e70 usb: dwc3: Use the correct speed macros for DSTS/DCFG
> 3de2685f0c39 usb: dwc3: gadget: Fix truncated cast issue
> 361572b5f7a9 usb: dwc3: gadget: Handle TRB index 0 when full or empty
> 7d0a038b130c usb: dwc3: gadget: Account for link TRB in TRBs left
> 32db3d9437b6 usb: dwc3: gadget: Account for max size in TRB space
> 89bc856e5a74 usb: dwc3: gadget: Don't prepare TRBs if no space
> 0d25744ad107 usb: dwc3: gadget: Initialize the TRB ring
> dca0119c3ab6 usb: dwc3: gadget: Simplify skipping of link TRBs
> 96bedb637a1e usb: dwc3: Endianness issue on dwc3_log_ctrl
> d07fa665c79d usb: dwc3: gadget: Fix usage of bitwise operator
> 501058edebf9 usb: dwc3: ep0: Use the correct type for SET_SEL data
> 958b9fa7f8cf usb: dwc3: ep0: Fix endianness of wIndex passed to dwc3_wIndex_to_dep
> 71f7e7027028 usb: dwc3: gadget: improve gcmd trace
> 88811f7b722c usb: dwc3: gadget: remove udelay() from generic cmd
> 0fe886cdb07a usb: dwc3: gadget: single return point on generic commands
> 0933df159c5c usb: dwc3: trace: print ep cmd status with a single trace
> f6bb225bb3ca usb: dwc3: gadget: loop while (timeout)
> ba1598410eff usb: dwc3: gadget: fix trace output when command fails
> f75cacc468ed usb: dwc3: trace: fully decode IRQ events
> 7ab373aadbd0 usb: dwc3: gadget: no more tracking endpoint type with its name
> 22f2c619a116 usb: dwc3: trace: pretty-print TRB's ctrl field
> 74674cbf858f usb: dwc3: gadget: add a per-endpoint request queue lock
> e9af9229098d usb: dwc3: pci: add Power Management dummy hooks
> fc8bb91bc83e usb: dwc3: implement runtime PM
> 4cb4221764ef usb: dwc3: gadget: fix for possible endpoint disable race
> 9cad39fe4e4a usb: dwc3: fix for the isoc transfer EP_BUSY flag
> ab2a92e7a608 usb: dwc3: gadget: only resume USB2 PHY in <=HIGHSPEED
> 3f308d17d7ab usb: dwc3: gadget: hold gadget IRQ in dwc->irq_gadget
> 51f5d49ad6f0 usb: dwc3: core: simplify suspend/resume operations
> c499ff71ff2a usb: dwc3: core: re-factor init and exit paths
> bcdb3272e889 usb: dwc3: core: move fladj to dwc3 structure
> 2eb8801650b3 usb: dwc3: gadget: add a pointer to endpoint registers
> 2cd4718d0bbe usb: dwc3: gadget: pass dep as argument to endpoint command
> 4e99472bc10b usb: dwc3: gadget: initialize NUMP based on RxFIFO Size
> 5ee85d890f8d usb: dwc3: gadget: split __dwc3_gadget_kick_transfer()
> 6b74289937f6 usb: dwc3: gadget: return 0 if we try to Wakeup in superspeed
> b43bba96b903 usb: dwc3: gadget: remove udelay(1) when sending ep cmds
> 6aff48329595 usb: dwc3: gadget: rely on sg_is_last() and list_is_last()
> 4fae2e3e1515 usb: dwc3: gadget: simplify __dwc3_gadget_kick_transfer()
> c4233573f6ee usb: dwc3: gadget: prepare TRBs on update transfers too
> 7f370ed0cfe9 usb: dwc3: core: get rid of DWC3_PM_OPS macro
> 9f8a67b65a49 usb: dwc3: gadget: fix gadget suspend/resume
> d7be295243bb usb: dwc3: gadget: re-factor ->udc_start and ->udc_stop
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 389 +++++++++--------
> drivers/usb/dwc3/core.h | 59 ++-
> drivers/usb/dwc3/debug.h | 140 +++++-
> drivers/usb/dwc3/ep0.c | 26 +-
> drivers/usb/dwc3/gadget.c | 923 ++++++++++++++++++++++-----------------
> drivers/usb/dwc3/gadget.h | 4 +-
> drivers/usb/dwc3/io.h | 7 +-
> drivers/usb/dwc3/platform_data.h | 53 ---
> 8 files changed, 926 insertions(+), 675 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 12/64] usb: dwc3: import from kernel v4.9
2026-05-07 9:27 ` [RFC PATCH v2 12/64] usb: dwc3: import from kernel v4.9 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.9
>
> Sync Linux kernel dwc3 changes from v4.8 to v4.9.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.8..v4.9
> Commits imported:
> 4accb8a1ee7d usb: dwc3: st: add missing <linux/pinctrl/consumer.h> include
> 9b9d7cdd0a20 usb: dwc3: Fix error handling for core init
> 51fbc7c06c89 usb: dwc3: Fix size used in dma_free_coherent()
> d889c23ce4e3 usb: dwc3: gadget: never pre-start Isochronous endpoints
> a9c3ca5fae6b usb: dwc3: gadget: properly account queued requests
> 81522637485d Merge tag 'usb-for-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> b281dc630b41 usb: dwc3: of-simple: add compatible for Cavium
> 26c9cac402c4 usb: dwc3: of-simple: allow glues without clocks
> fc9104d5d717 Merge 4.8-rc6 into usb-next
> 8d53e6267594 usb: dwc3: of-simple: Fix warning during unbind
> 5e6c88d28ccb usb: dwc3: fix Clear Stall EP command failure
> d6e10bf2ba47 usb: dwc3: avoid -Wmaybe-uninitialized warning
> 9d6173e125d9 usb: dwc3: Fix dr_mode validation
> 073c47aca7b7 usb: dwc3: ulpi: make dwc3_ulpi_ops constant
> e6f74849784c usb: ulpi: rename operations {read|write}_dev to simply {read|write}
> b7cf1dc3414d usb: dwc3: ulpi: use new api
> 9b0dd49e3565 Merge 4.8-rc5 into usb-testing
> 594e121f2568 usb: dwc3: gadget: stop kicking if we run out of space
> 7ae7df4982af usb: dwc3: gadget: abolish trbs_left
> 06281d460fc5 usb: dwc3: Add ENDXFER command polling
> 512e47572f7d usb: dwc3: Add revision numbers for the USB 3.0 IP
> 00af62330c39 usb: dwc3: core: Move the mode setting to the right place
> f652191be3f6 usb: dwc3: of-simple: add compatible for rockchip rk3399
> 00fe081dc3a3 usb: dwc3: add dis_del_phy_power_chg_quirk
> 32f2ed864dd5 usb: dwc3: make usb2 phy utmi interface configurable
> 16199f338939 usb: dwc3: add dis_u2_freeclk_exists_quirk
> dc55c67e9c95 usb: dwc3: gadget: improve increment request->actual
> f99f53f24d87 usb: dwc3: gadget: remove condition that never happens
> 1f512119a08c usb: dwc3: gadget: add remaining sg entries to ring
> 2c78c0295fd8 usb: dwc3: gadget: interrupt on ring full too
> 0b3e4af3c740 usb: dwc3: gadget: add sg and num_pending_sgs to dwc3_request
> 31162af447d7 usb: dwc3: gadget: avoid while (1) loop on completion
> 08a36b543803 usb: dwc3: gadget: simplify __dwc3_gadget_ep_queue()
> 45438a0cd9c2 usb: dwc3: gadget: simplify dwc3_ep_prev_trb()
> 737f1ae2556a usb: dwc3: gadget: increment dequeue pointer on completion
> 4bc48c974708 usb: dwc3: gadget: retire LST bit completely
> 1aaaa9acaea1 Merge 4.8-rc3 into usb-next
> a26a142275c6 usb: remove redundant dependency on USB_SUPPORT
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 127 +++++++++++++++++---
> drivers/usb/dwc3/core.h | 35 +++++-
> drivers/usb/dwc3/gadget.c | 292 ++++++++++++++++++----------------------------
> 3 files changed, 258 insertions(+), 196 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 13/64] usb: dwc3: import from kernel v4.10
2026-05-07 9:27 ` [RFC PATCH v2 13/64] usb: dwc3: import from kernel v4.10 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.10
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 14/64] usb: dwc3: import from kernel v4.11
2026-05-07 9:27 ` [RFC PATCH v2 14/64] usb: dwc3: import from kernel v4.11 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.11
>
> Sync Linux kernel dwc3 changes from v4.10 to v4.11.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.10..v4.11
> Commits imported:
> de288e36fe33 usb: dwc3: gadget: delay unmap of bounced requests
> 35b2719e72d3 usb: dwc3: gadget: make to increment req->remaining in all cases
> 0913750f9fb6 usb: dwc3-omap: Fix missing break in dwc3_omap_set_mailbox()
> 1551e35ea418 usb: dwc3: gadget: Fix system suspend/resume on TI platforms
> 73561128eb72 usb: dwc3: Fix incorrect type for utmi mode
> cf3113d893d4 usb: dwc3: gadget: properly increment dequeue pointer on ep_dequeue
> 7369090a9fb5 usb: dwc3: gadget: make Set Endpoint Configuration macros safe
> bc46e23c3425 Merge tag 'usb-for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> e42a5dbb8a3d usb: dwc3: host: pass quirk-broken-port-ped property for known broken revisions
> 28781789051e usb: dwc3: gadget: read IN ep FIFO size from HW
> 692665c621bb Merge 4.10-rc5 into usb-next
> ffb80fc672c3 usb: dwc3: gadget: skip Set/Clear Halt when invalid
> 538967983b88 usb: dwc3: ep0: Fix the possible missed request for handling delay STATUS phase
> c773bb0b9264 usb: dwc3: omap: Replace the extcon API
> c6267a51639b usb: dwc3: gadget: align transfers to wMaxPacketSize
> 905dc04ea796 usb: dwc3: gadget: allocate bounce buffer for unaligned xfers
> e49d3cf4926a usb: dwc3: gadget: extract __dwc3_prepare_one_trb()
> 843053093f47 usb: dwc3: gadget: simplify dwc3_prepare_one_trb()
> 0e4018ff5d12 usb: dwc3: exynos: Remove MODULE_ALIAS()
> 3e27b3f66a56 usb: dwc3: exynos remove suspend clock unspecified debug message
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.h | 5 +
> drivers/usb/dwc3/ep0.c | 14 +++
> drivers/usb/dwc3/gadget.c | 287 +++++++++++++++++++++++++++++++++++++---------
> drivers/usb/dwc3/gadget.h | 14 +--
> 4 files changed, 260 insertions(+), 60 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 15/64] usb: dwc3: import from kernel v4.12
2026-05-07 9:27 ` [RFC PATCH v2 15/64] usb: dwc3: import from kernel v4.12 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.12
>
> Sync Linux kernel dwc3 changes from v4.11 to v4.12.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.11..v4.12
> Commits imported:
> 018047a1dba7 usb: dwc3: keystone: check return value
> d325a1de49d6 usb: dwc3: gadget: Prevent losing events in event cache
> f1d6826cae30 usb: dwc3: gadget: Fix ISO transfer performance
> 682179592e48 usb: dwc3: pci: add Intel Cannonlake PCI IDs
> 8f28472a739e Merge tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
> 2bd804017435 Merge tag 'gpio-v4.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
> ba7756d08212 Merge tag 'usb-for-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> 9840354ff429 usb: dwc3: Add dual-role support
> 41ce1456e1db usb: dwc3: core: make dwc3_set_mode() work properly
> b202c42cbf81 usb: dwc3: debugfs: remove unnecessary | operator
> 6b3261a27428 usb: dwc3: core: add current_dr_role member
> d6e5a549cc4d usb: dwc3: simplify ZLP handling
> 4199c5f8bccd usb: dwc3: ep0: improve handling of unaligned OUT requests
> d686a5ff6a2d usb: dwc3: ep0: pass dep as argument to internal functions
> 7d5e650a5fdb usb: dwc3: ep0: use immediate SETUP on TRB
> b5c7ed5cca82 usb: dwc3: expose dwc3_trb_type_string()
> 90abb425d949 usb: dwc3: debugfs: return strings that match tracepoints
> 26c9f3e391d1 usb: dwc3: debugfs: make use of dwc3_gadget_link_string()
> 2df301cd3d71 usb: dwc3: debugfs: downcase OTG on 'mode' file
> ff3f0789b3dc usb: dwc3: use BIT() macro where possible
> aad7c2596495 usb: dwc3: trace: change format for string to cmd trace
> af771d731b8e usb: dwc3: gadget: Fix starting microframe for ISOC
> 3a932b0f50f4 usb: dwc3: exynos: change goto labels to meaningful names
> f3bcfc7e68f0 usb: dwc3: remove dwc3_gadget_init_hw_endpoints
> 47d3946ea220 usb: dwc3: refactor gadget endpoint count calculation
> 8261bd4e91e2 usb: dwc3: make macros safe to expression arguments
> 05428ba983e6 usb: dwc3: remove dwc3_log_msg trace class
> 1966b8657d05 usb: dwc3: make sure UX_EXIT_PX is cleared
> e47ff590cc57 Merge 4.11-rc4 into usb-next
> 4a56e413b50c usb: dwc3: pci: Switch to devm_acpi_dev_add_driver_gpios()
> 2c93e790e825 usb: add CONFIG_USB_PCI for system have both PCI HW and non-PCI based USB HW
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 109 +++++++++++++------
> drivers/usb/dwc3/core.h | 261 ++++++++++++++++++++++++----------------------
> drivers/usb/dwc3/debug.h | 28 +++++
> drivers/usb/dwc3/ep0.c | 151 ++++++++++++---------------
> drivers/usb/dwc3/gadget.c | 218 +++++++++++++-------------------------
> drivers/usb/dwc3/gadget.h | 20 ++--
> 6 files changed, 392 insertions(+), 395 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 16/64] usb: dwc3: import from kernel v4.13
2026-05-07 9:27 ` [RFC PATCH v2 16/64] usb: dwc3: import from kernel v4.13 Jens Wiklander
@ 2026-05-08 15:26 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:26 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.13
>
> Sync Linux kernel dwc3 changes from v4.12 to v4.13.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.12..v4.13
> Commits imported:
> 40d829fb2ec6 usb: dwc3: gadget: Correct ISOC DATA PIDs for short packets
> 4a71fcb8ac5f usb: dwc3: gadget: only unmap requests from DMA if mapped
> ee249b455494 usb: dwc3: omap: remove IRQ_NOAUTOEN used with shared irq
> 541768b08a40 usb: dwc3: core: Call dwc3_core_get_phy() before initializing phys
> 408c9861c697 Merge tag 'pm-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
> 362f6729cbb1 Merge tag 'usb-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
> de3ef1eb1cd0 PM / core: Drop run_wake flag from struct dev_pm_info
> 24040a58379e Merge tag 'usb-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
> ece7af5f787e usb: dwc3: exynos: Handle return value of clk_prepare_enable
> 46b780d46bcc usb: dwc3: gadget: increase readability of dwc3_gadget_init_endpoints()
> 958d1a4c40dd usb: dwc3: core: program PHY for proper DRD modes
> f54edb539c11 usb: dwc3: core: initialize ULPI before trying to get the PHY
> e0082698b689 usb: dwc3: ulpi: conditionally resume ULPI PHY
> 9b0a1f95c4b4 usb: dwc3: ep0: make sure wValue is 0 on GetStatus()
> 7d8d0639565f usb: dwc3: gadget: implement ->udc_set_speed()
> 94116f8126de ACPI: Switch to use generic guid_t in acpi_evaluate_dsm()
> af32423a2d86 usb: dwc3: trace: decode ctrl request
> 3587f36a125f usb: dwc3: debug: remove static char buffer from dwc3_decode_event()
> e42f09b85f20 usb: dwc3: trace: rely on __string() and __assign_str()
> dfc5e80578f2 usb: dwc3: gadget: slight cleanup to dwc3_process_event_entry()
> 436841d53dd7 usb: dwc3: debugfs: slightly improve output of trb_ring
> bfad65ee9be2 usb: dwc3: update documentation
> 04fb365c453e usb: dwc3: replace %p with %pK
> 6acf116c9558 Merge 4.12-rc2 into usb-next
> 65db7a0c9816 usb: dwc3: add disable u2mac linestate check quirk
> cdb55b39fab8 usb: dwc3: gadget: lazily map requests for DMA
> 202adafe5a6e usb: dwc3: gadget: don't WARN about lack of TRBs
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 57 ++++++--
> drivers/usb/dwc3/core.h | 48 ++++---
> drivers/usb/dwc3/debug.h | 247 ++++++++++++++++++++++++++++++++-
> drivers/usb/dwc3/ep0.c | 8 +-
> drivers/usb/dwc3/gadget.c | 340 ++++++++++++++++++++++++++++------------------
> drivers/usb/dwc3/gadget.h | 22 ++-
> 6 files changed, 549 insertions(+), 173 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 18/64] usb: dwc3: import from kernel v4.15
2026-05-07 9:27 ` [RFC PATCH v2 18/64] usb: dwc3: import from kernel v4.15 Jens Wiklander
@ 2026-05-08 15:27 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:27 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.15
>
> Sync Linux kernel dwc3 changes from v4.14 to v4.15.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.14..v4.15
> Commits imported:
> ec5bb87e4e2a usb: dwc3: gadget: Fix PCM1 for ISOC EP with ep->mult less than 3
> a0d8c4cfdf31 usb: dwc3: of-simple: set dev_pm_ops
> ded600ea9fb5 usb: dwc3: of-simple: fix missing clk_disable_unprepare
> 8722e095f5a4 usb: dwc3: gadget: Wait longer for controller to end command processing
> 894025f24bd0 Merge tag 'usb-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
> b5ea47570b5e USB: dwc3: Remove redundant license text
> 5fd54ace4721 USB: add SPDX identifiers to all remaining files in drivers/usb/
> 644cbbc3ab4d usb: dwc3: core: Notify current USB mode to USB3 PHY as well
> 8eed00b237a2 usb: dwc3: pci: Runtime resume child device from wq
> 689bf72c6e0d usb: dwc3: Don't reinitialize core during host bus-suspend/resume
> 06c47e6286d5 usb: dwc3: of-simple: Add support to get resets for the device
> d6d9c2a3eac2 usb: dwc3: of-simple: Re-order resource handling in remove
> 86763723eee8 usb: dwc3: ep0: Clean up unused variables
> 7fdca766499b usb: dwc3: gadget: simplify __dwc3_gadget_kick_transfer() prototype
> 502a37b98a7b usb: dwc3: gadget: cache frame number in struct dwc3_ep
> 64e010802997 usb: dwc3: gadget: simplify __dwc3_gadget_ep_queue()
> ccb94ebf9e65 usb: dwc3: gadget: check for lack of TRBs a bit earlier
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 74 ++++++++++++++++++---------------------------
> drivers/usb/dwc3/core.h | 12 ++------
> drivers/usb/dwc3/debug.h | 10 +------
> drivers/usb/dwc3/ep0.c | 30 +------------------
> drivers/usb/dwc3/gadget.c | 76 ++++++++++++++---------------------------------
> drivers/usb/dwc3/gadget.h | 10 +------
> drivers/usb/dwc3/io.h | 10 +------
> 7 files changed, 59 insertions(+), 163 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 19/64] usb: dwc3: import from kernel v4.16
2026-05-07 9:27 ` [RFC PATCH v2 19/64] usb: dwc3: import from kernel v4.16 Jens Wiklander
@ 2026-05-08 15:28 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:28 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.16
>
> Sync Linux kernel dwc3 changes from v4.15 to v4.16.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.15..v4.16
> Commits imported:
> 084a804e0120 usb: dwc3: Fix lock-up on ID change during system suspend/resume
> [...]
>
> drivers/usb/dwc3/core.c | 93 ++++++++++++++++++++++++++++++++++-------
> drivers/usb/dwc3/core.h | 26 ++++++++-----
> drivers/usb/dwc3/debug.h | 9 +++++
> drivers/usb/dwc3/ep0.c | 13 ++++---
> drivers/usb/dwc3/gadget.c | 8 +++-
> 5 files changed, 106 insertions(+), 43 deletions(-)
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> @@ -221,6 +222,9 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
> * for 10 times instead.
> */
> if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
> + phy_exit(dwc->usb3_generic_phy);
> + phy_exit(dwc->usb2_generic_phy);
> +
> retries = 10;
Kernel commit 00b42170c86f ('usb: dwc3: Undo PHY init if soft reset
fails') puts these phy_exit() calls just before 'return -ETIMEDOUT;'
at the end of the function.
Regards,
Simon
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 20/64] usb: dwc3: import from kernel v4.17
2026-05-07 9:27 ` [RFC PATCH v2 20/64] usb: dwc3: import from kernel v4.17 Jens Wiklander
@ 2026-05-08 15:28 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:28 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.17
>
> Sync Linux kernel dwc3 changes from v4.16 to v4.17.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.16..v4.17
> Commits imported:
> 96bd39df29c2 usb: dwc3: gadget: Fix list_del corruption in dwc3_ep_dequeue
> 9cffd15de0c3 usb: dwc3: gadget: dwc3_gadget_del_and_unmap_request() can be static
> c91815b59624 usb: dwc3: gadget: never call ->complete() from ->ep_queue()
> 6d23ee9caa67 Merge tag 'usb-for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
> 7d11c3ac6669 usb: dwc3: core: Fix broken system suspend/resume on AM437x
> 2f3090c6a8f2 usb: dwc3: Check controller type before setting speed
> 80b776340c78 usb: dwc3: Dump LSP and BMU debug info
> 938a5ad1d305 usb: dwc3: Check for ESS TX/RX threshold config
> 6743e817a4de usb: dwc3: Add DWC_usb31 GTXTHRCFG reg fields
> 01b0e2cc7d89 usb: dwc3: gadget: Check IP revision for GRXTHRCFG
> 2fbc5bdc8fd1 usb: dwc3: Add DWC_usb31 GRXTHRCFG bit fields
> d548a61767fa usb: dwc3: Check IP revision for GTXFIFOSIZ
> 0cab8d26d6e5 usb: dwc3: Update DWC_usb31 GTXFIFOSIZ reg fields
> fab383333877 usb: dwc3: Add SoftReset PHY synchonization delay
> cabdf83dadfb usb: dwc3: pci: Properly cleanup resource
> de948a74ad6f usb: dwc3: Makefile: fix link error on randconfig
> a8f25c36f732 Merge branch 4.16-rc6 into usb-next
> 7642d8386ac7 usb: dwc3: ep0: remove redundant assignment
> 498f0478aba4 usb: dwc3: Prevent indefinite sleep in _dwc3_set_mode during suspend/resume
> f09cc79b4b33 usb: dwc3: add dual role support using OTG block
> daaecc6541d0 usb: dwc3: prevent setting PRTCAP to OTG from debugfs
> 4cff75c7fe3d usb: dwc3: core.h: add some register definitions
> e8284db48f1f usb: dwc3: of-simple: add support for the Amlogic Meson GXL and AXG SoCs
> ff0a632f0875 usb: dwc3: of-simple: add support for shared and pulsed reset lines
> 6d5b53c1fd5c usb: dwc3: debugfs: Re-use DEFINE_SHOW_ATTRIBUTE() macro
> 134d1fd44221 Merge 4.16-rc3 into usb-next
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 132 ++++++++++++++++++++++++++++++++++++++---
> drivers/usb/dwc3/core.h | 146 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/dwc3/ep0.c | 2 +-
> drivers/usb/dwc3/gadget.c | 84 ++++++++++++++++++--------
> 4 files changed, 331 insertions(+), 33 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 21/64] usb: dwc3: import from kernel v4.18
2026-05-07 9:27 ` [RFC PATCH v2 21/64] usb: dwc3: import from kernel v4.18 Jens Wiklander
@ 2026-05-08 15:28 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:28 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.18
>
> Sync Linux kernel dwc3 changes from v4.17 to v4.18.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.17..v4.18
> Commits imported:
> 9566a7c72f4f usb: dwc3: gadget: remove redundant variable maxpacket
> 9925e6ebe5c2 usb: dwc3: qcom: mark PM functions as __maybe_unused
> 93ef2dc0c4f7 usb: dwc3: Fix error return code in dwc3_qcom_probe()
> 615277779f41 usb: dwc3: Only call clk_bulk_get() on devicetree instantiated devices
> 896e518883f1 usb: dwc3: of-simple: fix use-after-free on remove
> 00908693c481 usb: dwc3: pci: add support for Intel IceLake
> 535c8dc5c480 USB: dwc3: no need to check return value of debugfs_create functions
> 3bea1cfcff11 usb: dwc3: Remove DEBUG define from Qualcomm DWC3 glue driver
> 05645366f389 usb: dwc3: gadget: Fix list_del corruption in dwc3_ep_dequeue
> 691025107eb7 usb: dwc3: gadget: dwc3_gadget_del_and_unmap_request() can be static
> edbbfe19444e usb: dwc3: dwc3_get_extcon() can be static
> 31a2f5a7e136 usb: dwc3: gadget: init req->{direction,epnum} from alloc_request()
> d7ca7e1896b2 usb: dwc3: gadget: initialize transfer index from send_gadget_ep_cmd()
> 4439661d64f5 usb: dwc3: gadget: assign resource_index inside get_transfer_index()
> f62afb4929a8 usb: dwc3: gadget: always start isochronous aligned to dep->interval
> d513320f1fd0 usb: dwc3: gadget: don't issue End Transfer if we have started reqs
> e1d542f712e1 usb: dwc3: debug: decode uFrame from event too
> 42626919efab usb: dwc3: gadget: move dwc3_calc_trbs_left() in place of prototype
> 20d1d43fa09a usb: dwc3: gadget: move set_xfer_resource() in place of prototype
> b07c2db83f92 usb: dwc3: gadget: remove a few more dwc arguments
> a2d23f08038e usb: dwc3: gadget: combine modify & restore into single argument
> 8f1c99cd24b0 usb: dwc3: gadget: refactor dwc3_gadget_init_endpoints()
> f38e35dd84e2 usb: dwc3: gadget: split dwc3_gadget_ep_cleanup_completed_requests()
> 6d9d22dac114 usb: dwc3: gadget: get rid of the length variable
> fee73e6144d8 usb: dwc3: gadget: reduce scope of ret variable
> 6afbdb57732b usb: dwc3: gadget: one declaration per line
> e0c42ce590fe usb: dwc3: gadget: simplify IOC handling
> d80fe1b6e34d usb: dwc3: gadget: simplify short packet event
> b27972b508d1 usb: dwc3: trace: print out event status too
> 8b3b7b66c0e6 usb: dwc3: gadget: simplify unaligned and zlp handling
> cf35fc336e66 usb: dwc3: gadget: remove unnecessary 'chain' variable
> 021595963a3c usb: dwc3: gadget: remove PENDING handling from cleanup_completed
> d36929538f8b usb: dwc3: gadget: split scatterlist and linear handlers
> d5044a04b529 usb: dwc3: gadget: simplify isoc case on cleanup_completed_requests
> fe990cea8ed5 usb: dwc3: gadget: simplify queueing of isoc transfers
> 4d588a145b3e usb: dwc3: gadget: remove duplicated missed isoc handling
> 6d8a019614f3 usb: dwc3: gadget: check for Missed Isoc from event status
> 58f0218a271d usb: dwc3: gadget: remove unnecessary 'ioc' variable
> 12a3a4ada816 usb: dwc3: gadget: make cleanup_completed_requests() return nothing
> 5f2e7975f0df usb: dwc3: gadget: remove DWC3_EP_BUSY flag
> 1912cbc60f44 usb: dwc3: gadget: start removing BUSY flag
> ee3638b897b9 usb: dwc3: gadget: update dep->frame_number from XferInprogress too
> 5828cada9908 usb: dwc3: gadget: always use frame number from XferNotReady
> 8f608e8ab628 usb: dwc3: gadget: remove unnecessary 'dwc' parameter
> 320338651d33 usb: dwc3: gadget: move handler closer to calling site
> ed27442e5093 usb: dwc3: gadget: rename dwc3_gadget_start_isoc()
> a24a6ab1493a usb: dwc3: gadget: remove some pointless checks
> 0bd0f6d201eb usb: dwc3: gadget: remove allocated/queued request tracking
> 66f5dd5a0379 usb: dwc3: gadget: rename done_trbs and done_reqs
> a861282f5037 usb: dwc3: gadget: don't kick transfer all the time
> fbea935accf4 usb: dwc3: gadget: rename dwc3_endpoint_transfer_complete()
> 742a4fff5f29 usb: dwc3: gadget: XferComplete only for EP0
> 38408464aa76 usb: dwc3: gadget: XferNotReady is Isoc-only
> 52fcc0bead0f usb: dwc3: gadget: pre-issue Start Transfer for Interrupt EPs too
> c96e6725db9d usb: dwc3: gadget: Correct the logic for queuing sgs
> a31e63b608ff usb: dwc3: gadget: Correct handling of scattergather lists
> 5f0b74e54890 USB: dwc3: get extcon device by OF graph bindings
> fe8abf332b8f usb: dwc3: support clocks and resets for DWC3 core
> 44feb8e6ea2d usb: dwc3: use local copy of resource to fix-up register offset
> bcb128777af5 usb: dwc3: core: Suspend PHYs on runtime suspend in host mode
> a4333c3a6ba9 usb: dwc3: Add Qualcomm DWC3 glue driver
> e362098f0e12 usb: dwc3: of-simple: Add compatible for Allwinner H6 platform
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 157 ++++++++--
> drivers/usb/dwc3/core.h | 25 +-
> drivers/usb/dwc3/debug.h | 26 +-
> drivers/usb/dwc3/ep0.c | 9 +-
> drivers/usb/dwc3/gadget.c | 773 ++++++++++++++++++++++------------------------
> drivers/usb/dwc3/gadget.h | 5 +-
> 6 files changed, 531 insertions(+), 464 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 22/64] usb: dwc3: import from kernel v4.19
2026-05-07 9:27 ` [RFC PATCH v2 22/64] usb: dwc3: import from kernel v4.19 Jens Wiklander
@ 2026-05-08 15:28 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:28 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.19
>
> Sync Linux kernel dwc3 changes from v4.18 to v4.19.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.18..v4.19
> Commits imported:
> bfa150f37f80 Merge tag 'fixes-for-v4.19-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus
> 4e3121abcf53 usb/dwc3/gadget: fix kernel-doc parameter warning
> b497fff6f59e usb: dwc3: pci: Fix return value check in dwc3_byt_enable_ulpi_refclock()
> 66174b6998a6 usb: dwc3: of-simple: avoid unused function warnings
> 45dd7af410b7 Merge tag 'usb-for-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> 4ea438da76f4 usb: dwc3: gadget: Check MaxPacketSize from descriptor
> b138e23d3dff usb: dwc3: core: Enable AutoRetry feature in the controller
> a77004681148 usb: dwc3: Set default mode for dwc_usb31
> c31d983beaf0 usb: dwc3: pci: Intel Merrifield can be host
> 1a7b12f69a94 usb: dwc3: pci: Supply device properties via driver data
> d9612c2f0449 usb: dwc3: Enable undefined length INCR burst type
> d635db5508b0 usb: dwc3: add global soc bus configuration reg0
> 87d852de94d6 usb: dwc3: Describe 'wakeup_work' field of struct dwc3_pci
> 9a7faac36502 usb: dwc3: change stream event enable bit back to 13
> 76251db86561 usb: dwc3: of-simple: reset host controller at suspend/resume
> 211f658b7b40 usb: dwc3: pci: Use devm functions to get the phy GPIOs
> 7740d04d901d usb: dwc3: pci: Enable ULPI Refclk on platforms where the firmware does not
> 5741022cbdf3 usb: dwc3: pci: Add GPIO lookup table on platforms without ACPI GPIO resources
> 3fe314ca8c97 usb: dwc3: Add a glue driver for Synopsys HAPS platform
> db9fc500e85a usb: dwc3: gadget: remove redundant variable maxpacket
> 714c95ce8bad Merge 4.18-rc3 into usb-next
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/dwc3/core.h | 17 +++++++
> drivers/usb/dwc3/gadget.c | 3 +-
> drivers/usb/dwc3/gadget.h | 2 +-
> 4 files changed, 137 insertions(+), 3 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 23/64] usb: dwc3: import from kernel v4.20
2026-05-07 9:27 ` [RFC PATCH v2 23/64] usb: dwc3: import from kernel v4.20 Jens Wiklander
@ 2026-05-08 15:28 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:28 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v4.20
>
> Sync Linux kernel dwc3 changes from v4.19 to v4.20.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.19..v4.20
> Commits imported:
> 38317f5c0f2f Revert "usb: dwc3: gadget: skip Set/Clear Halt when invalid"
> 2fc6d4be35fb usb: dwc3: gadget: fix ISOC TRB type on unaligned transfers
> 7b412b04a0c7 usb: dwc3: Fix NULL pointer exception in dwc3_pci_remove()
> ba3a51ac32eb usb: dwc3: gadget: Properly check last unaligned/zero chain TRB
> 08fd9a82fda8 usb: dwc3: core: Clean up ULPI device
> 6af19fd10595 usb: dwc3: Fix spelling of 'optimizations'
> 87dd96111b0b usb: dwc3: gadget: Check ENBLSLPM before sending ep command
> 3def4031b3e3 usb: dwc3: add EXTCON dependency for qcom
> 4c19cc14064d usb: dwc3: exynos: Add support for Exynos5433 variant with all clocks
> 9f2168367a0a usb: dwc3: exynos: Rework clock handling and prepare for new variants
> 1e041b6f313a usb: dwc3: exynos: Remove dead code
> 755801cb9feb Merge tag 'uvcg-20180925' of git://linuxtv.org/pinchartl/media into testing/next
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 3 ++-
> drivers/usb/dwc3/gadget.c | 42 +++++++++++++++++++++++-------------------
> 2 files changed, 25 insertions(+), 20 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 24/64] usb: dwc3: import from kernel v5.0
2026-05-07 9:27 ` [RFC PATCH v2 24/64] usb: dwc3: import from kernel v5.0 Jens Wiklander
@ 2026-05-08 15:31 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:31 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.0
>
> Sync Linux kernel dwc3 changes from v4.20 to v5.0.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v4.20..v5.0
> Commits imported:
> 512e6fb589bc usb: dwc3: exynos: Fix error handling of clk_prepare_enable
> [...]
>
> drivers/usb/dwc3/core.c | 19 ++-
> drivers/usb/dwc3/core.h | 60 ++++++--
> drivers/usb/dwc3/debug.h | 29 ++++
> drivers/usb/dwc3/gadget.c | 348 ++++++++++++++++++++++++++++++----------------
> drivers/usb/dwc3/gadget.h | 15 ++
> 5 files changed, 336 insertions(+), 135 deletions(-)
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> @@ -919,6 +940,7 @@ struct dwc3_scratchpad_array {
> * @u1u2: only used on revisions <1.83a for workaround
> * @maximum_speed: maximum speed requested (mainly for testing purposes)
> * @ip: controller's ID
> + * @version_type: VERSIONTYPE register contents, a sub release of a revision
> * @revision: revision register contents
> * @version_type: VERSIONTYPE register contents, a sub release of a revision
There is a duplicate line here.
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> @@ -1139,6 +1166,17 @@ struct dwc3 {
> #define DWC3_REVISION_IS_DWC31 0x80000000
> #define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
> #define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
> +#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
> +#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
> +
> + u32 version_type;
> +
> +#define DWC31_VERSIONTYPE_EA01 0x65613031
> +#define DWC31_VERSIONTYPE_EA02 0x65613032
> +#define DWC31_VERSIONTYPE_EA03 0x65613033
> +#define DWC31_VERSIONTYPE_EA04 0x65613034
> +#define DWC31_VERSIONTYPE_EA05 0x65613035
> +#define DWC31_VERSIONTYPE_EA06 0x65613036
> @@ -1098,6 +1124,7 @@ struct dwc3 {
> #define DWC3_REVISION_300A 0x5533300a
> #define DWC3_REVISION_310A 0x5533310a
> #define DWC3_REVISION_220A 0x5533220a
> +#define DWC3_REVISION_330A 0x5533330a
These two chunks seem to result in duplicate symbols.
Regards,
Simon
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 25/64] usb: dwc3: import from kernel v5.1
2026-05-07 9:27 ` [RFC PATCH v2 25/64] usb: dwc3: import from kernel v5.1 Jens Wiklander
@ 2026-05-08 15:31 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:31 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.1
>
> Sync Linux kernel dwc3 changes from v5.0 to v5.1.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v5.0..v5.1
> Commits imported:
> 7ae622c978db usb: dwc3: pci: add support for Comet Lake PCH ID
> 98bba546e82d usb: dwc3: drd: Defer probe if extcon device is not found
> dc8b2a691dc8 Merge tag 'usb-for-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> c7152763f02e usb: dwc3: Reset num_trbs after skipping
> c5353b225df9 usb: dwc3: gadget: don't enable interrupt when disabling endpoint
> 15e99b13b44b Merge 5.0-rc6 into usb-next
> 9c7ebc99b382 usb: dwc3: haps: Workaround matching VID PID
> 1381a5113caf usb: dwc3: debug: purge usage of strcat
> 7790b3556fcc usb: dwc3: trace: pass trace buffer size to decoding functions
> 3aec99154db3 usb: dwc3: gadget: remove DWC3_EP_END_TRANSFER_PENDING
> 9f45581f5eec usb: dwc3: gadget: early giveback if End Transfer already completed
> 974a1368c33e usb: dwc3: gadget: don't use resource_index as a flag
> acbfa6c26f21 usb: dwc3: gadget: clear DWC3_EP_TRANSFER_STARTED on cmd complete
> 7c3d7dc89e57 usb: dwc3: gadget: remove req->started flag
> b2b6d601365a usb: dwc3: gadget: prevent dwc3_request from being queued twice
> a3af5e3ad3f1 usb: dwc3: gadget: add dwc3_request status tracking
> eca6b49430c6 usb: dwc3: keystone: Add support for ti,am654-dwc3
> 169e3b68cadb usb: dwc3: gadget: Fix OTG events when gadget driver isn't loaded
> 54c9da1bcec4 usb: dwc3: qcom: Add support for MSM8998
> c9381e185fdc Merge 5.0-rc4 into usb-next
> cae8dc3b685f USB: add missing SPDX lines to Kconfig and Makefiles
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.h | 12 +++-
> drivers/usb/dwc3/debug.h | 156 +++++++++++++++++++++++++---------------------
> drivers/usb/dwc3/gadget.c | 65 ++++++++++---------
> drivers/usb/dwc3/gadget.h | 4 +-
> 4 files changed, 130 insertions(+), 107 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 26/64] usb: dwc3: import from kernel v5.2
2026-05-07 9:27 ` [RFC PATCH v2 26/64] usb: dwc3: import from kernel v5.2 Jens Wiklander
@ 2026-05-08 15:31 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:31 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.2
>
> Sync Linux kernel dwc3 changes from v5.1 to v5.2.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v5.1..v5.2
> Commits imported:
> a2d635decbfa Merge tag 'drm-next-2019-05-09' of git://anongit.freedesktop.org/drm/drm
> 2e487d280525 usb: dwc3: Rename DWC3_DCTL_LPM_ERRATA
> 8d791929b2fb usb: dwc3: Fix default lpm_nyet_threshold value
> dd24f9b604d3 usb: dwc3: debug: Print GET_STATUS(device) tracepoint
> b873e2d0ea1e usb: dwc3: Do core validation early on probe
> c729969b2b69 usb: dwc3: gadget: Set lpm_capable
> 41a91c606e7d usb: dwc3: move synchronize_irq() out of the spinlock protected block
> 75ecb9dd56a7 usb: dwc3: Free resource immediately after use
> c0c61471ef86 usb: dwc3: of-simple: Convert to bulk clk API
> c99993376f72 usb: dwc3: Add Amlogic G12A DWC3 glue
> 67130830ce42 usb: dwc3: Allow building USB_DWC3_QCOM without EXTCON
> 6e865c723014 Merge tag 'drm/tegra/for-5.2-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next
> f06ddb53096b BackMerge v5.1-rc5 into drm-next
> f31d5c24fb2e reset: Add acquired flag to of_reset_control_array_get()
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 18 +++++++++++-------
> drivers/usb/dwc3/core.h | 3 +--
> drivers/usb/dwc3/debug.h | 3 +++
> drivers/usb/dwc3/gadget.c | 5 ++---
> 4 files changed, 17 insertions(+), 12 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 27/64] usb: dwc3: import from kernel v5.3
2026-05-07 9:27 ` [RFC PATCH v2 27/64] usb: dwc3: import from kernel v5.3 Jens Wiklander
@ 2026-05-08 15:31 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:31 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.3
>
> Sync Linux kernel dwc3 changes from v5.2 to v5.3.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v5.2..v5.3
> Commits imported:
> 332694f8a4f7 Revert "usb:gadget Separated decoding functions from dwc3 driver."
> 3db1b636c07e usb:gadget Separated decoding functions from dwc3 driver.
> b3649dee5fbb usb: dwc3: pci: add support for TigerLake Devices
> aa23ce847dda usb: dwc3: remove unused @lock member of dwc3_ep struct
> dbb0569de852 usb: dwc3: pci: Add Support for Intel Elkhart Lake Devices
> 729dcffd1ed3 usb: dwc3: gadget: Add support for disabling U1 and U2 entries
> 4d20a6107050 usb: dwc3: qcom: Use of_clk_get_parent_count()
> dc1b5d9aed17 usb: dwc3: Fix core validation in probe, move after clocks are enabled
> 7f5d6a469617 usb: dwc3: qcom: Improve error handling
> a6e456209d08 usb: dwc3: qcom: Start USB in 'host mode' on the SDM845
> 2bc02355f8ba usb: dwc3: qcom: Add support for booting with ACPI
> f90db10779ad usb: dwc3: meson-g12a: Add support for IRQ based OTG switching
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 16 +++++++++++-----
> drivers/usb/dwc3/core.h | 6 ++++--
> drivers/usb/dwc3/ep0.c | 9 ++++++++-
> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
> drivers/usb/dwc3/gadget.h | 6 ++++++
> 5 files changed, 49 insertions(+), 10 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 28/64] usb: dwc3: import from kernel v5.4
2026-05-07 9:27 ` [RFC PATCH v2 28/64] usb: dwc3: import from kernel v5.4 Jens Wiklander
@ 2026-05-08 15:31 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:31 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.4
>
> Sync Linux kernel dwc3 changes from v5.3 to v5.4.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v5.3..v5.4
> Commits imported:
> d8eca64eec71 usb: dwc3: gadget: fix race when disabling ep with cancelled xfers
> a7d9874c6f3f usb: dwc3: remove the call trace of USBx_GFLADJ
> 9bbfceea12a8 usb: dwc3: pci: prevent memory leak in dwc3_pci_probe
> a51bab592fbb usb: dwc3: select CONFIG_REGMAP_MMIO
> d819f6584c20 usb: dwc3: Remove dev_err() on platform_get_irq() failure
> f146b40ba1f0 usb: dwc3: Switch to platform_get_irq_byname_optional()
> a0a465569b45 usb: dwc3: remove generic PHY calibrate() calls
> 96e46dcfb853 Merge tag 'usb-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> 17b63704ec7c usb: dwc3: gadget: Workaround Mirosoft's BESL check
> 91f255a26bce usb: common: Separated decoding functions from dwc3 driver.
> 54fb5ba647f8 usb: dwc3: gadget: Set BESL config parameter
> 16fe4f304015 usb: dwc3: Separate field holding multiple properties
> 1cf084d161f4 usb: dwc3: meson-g12a: fix suspend resume regulator unbalanced disables
> bceb418b65b9 usb: dwc3: st: Add of_dev_put() in probe function
> e36721b90144 usb: dwc3: st: Add of_node_put() before return in probe function
> c09b73cfac2a usb: dwc3: don't set gadget->is_otg flag
> 240b65dc1e89 usb: dwc3: Use clk_bulk_prepare_enable()
> 03bf32bbb607 usb: dwc3: Use devres to get clocks
> 3cd703f483d1 usb: dwc3: remove generic PHYs forwarding for XHCI device
> 4749e0e61241 usb: dwc3: Update soft-reset wait polling rate
> b2a3974253d3 usb: dwc3: omap: squash include/linux/platform_data/dwc3-omap.h
> a6e5e6794a53 usb: dwc3: Switch to use device_property_count_u32()
> 5b76f6a0b0f8 usb: dwc3: keystone: use devm_platform_ioremap_resource() to simplify code
> 58dd0bad2554 usb: dwc3: omap: use devm_platform_ioremap_resource() to simplify code
> c6e4999cd930 usb: dwc3: meson-g12a: use devm_platform_ioremap_resource() to simplify code
> d3523b631403 usb: dwc3: meson-g12a: use devm_platform_ioremap_resource() to simplify code
> b33f37064b74 usb: Remove dev_err() usage after platform_get_irq()
> 41b57327b270 usb: dwc3: Switch to use device_property_count_u32()
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 49 +++------
> drivers/usb/dwc3/core.h | 2 +
> drivers/usb/dwc3/debug.h | 252 ----------------------------------------------
> drivers/usb/dwc3/gadget.c | 37 +++++--
> 4 files changed, 46 insertions(+), 294 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 29/64] usb: dwc3: import from kernel v5.5
2026-05-07 9:27 ` [RFC PATCH v2 29/64] usb: dwc3: import from kernel v5.5 Jens Wiklander
@ 2026-05-08 15:31 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:31 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.5
>
> Sync Linux kernel dwc3 changes from v5.4 to v5.5.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v5.4..v5.5
> Commits imported:
> ea0d762775e2 usb: dwc3: gadget: Fix request complete check
> 3c3caae4cd6e usb: dwc3: pci: add ID for the Intel Comet Lake -H variant
> 2d7b78f59e02 usb: dwc3: ep0: Clear started flag on completion
> d3abda5a98a1 usb: dwc3: gadget: Clear started flag for non-IOC
> 8c7d4b7b3d43 usb: dwc3: gadget: Fix logical condition
> c3afa2224345 usb: dwc3: Fix Kconfig indentation
> 7170d1a4cc4d Merge tag 'usb-for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
> dcf5a2c390a3 usb: dwc3: create debugfs directory under usb root
> 16c8373ecf7b Merge 5.4-rc6 into usb-next
> 726b4fba94be usb: dwc3: of-simple: add a shutdown
> 038761ce68c2 usb: dwc3: debug: Remove newline printout
> 9ba3aca8fe82 usb: dwc3: Disable phy suspend after power-on reset
> bf7c1d95af67 Merge 5.4-rc3 into usb-next
> c2c1c66b5d43 usb: Fix Kconfig indentation
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 37 +++++++++++++++++++++++++++++++++++++
> drivers/usb/dwc3/debug.h | 4 ++--
> drivers/usb/dwc3/ep0.c | 8 ++++++++
> drivers/usb/dwc3/gadget.c | 12 +++++++++++-
> 4 files changed, 58 insertions(+), 3 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 30/64] usb: dwc3: import from kernel v5.6
2026-05-07 9:27 ` [RFC PATCH v2 30/64] usb: dwc3: import from kernel v5.6 Jens Wiklander
@ 2026-05-08 15:36 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:36 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: import from kernel v5.6
>
> Sync Linux kernel dwc3 changes from v5.5 to v5.6.
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Skipping unused files:
> debugfs.c drd.c dwc3-exynos.c dwc3-haps.c dwc3-imx8mp.c dwc3-keystone.c
> dwc3-octeon.c dwc3-of-simple.c dwc3-pci.c dwc3-qcom.c dwc3-qcom-legacy.c
> dwc3-rtk.c dwc3-st.c dwc3-xilinx.c host.c trace.c trace.h ulpi.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> List of commits: git log --oneline v5.5..v5.6
> Commits imported:
> dad2aff3e827 usb: dwc3: gadget: Update chain bit correctly when using sg list
> [...]
>
> drivers/usb/dwc3/core.c | 3 +++
> drivers/usb/dwc3/core.h | 2 ++
> drivers/usb/dwc3/debug.h | 39 +++++++++++++---------------------
> drivers/usb/dwc3/ep0.c | 4 +++-
> drivers/usb/dwc3/gadget.c | 54 ++++++++++++++++++++++++++++++++---------------
> drivers/usb/dwc3/gadget.h | 14 ++++++++++++
> 6 files changed, 74 insertions(+), 42 deletions(-)
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> @@ -167,6 +167,9 @@ static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
> static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
> {
> dwc3_ep_inc_trb(&dep->trb_dequeue);
> + /* set no action before sending new link state change */
> + dwc3_writel(dwc->regs, DWC3_DCTL, reg);
> +
> }
This hunk is in the wrong function - it should be dwc3_gadget_set_link_state()
I'm wondering whether this is intentional, i.e. that you are not too
worried about the accuracy of these individual commits and are mostly
just concerned with the final result. That seems OK to me, since this
is just such a large effort, so I will skip past these.
Regards,
Simon
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import
2026-05-07 9:27 ` [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import Jens Wiklander
@ 2026-05-08 15:40 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:40 UTC (permalink / raw)
To: jens.wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Jens,
On 2026-05-07T09:27:07, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> usb: dwc3: restore to original v3.19-rc1 kernel import
>
> Restore the dwc3 source files to the state of the original import in
> commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel to
> u-boot").
>
> The following files are preserved accross the import:
> Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c
> dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c
> dwc3-layerscape.c ti_usb_phy.c
>
> Note that this is a raw import and doesn't build.
> A fixup commit at the end of the series fixes that.
>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
>
> drivers/usb/dwc3/core.c | 1004 ++++++++++++++++--------------------
> drivers/usb/dwc3/core.h | 171 ++----
> drivers/usb/dwc3/debug.c | 32 ++
> drivers/usb/dwc3/debug.h | 228 ++++++++
> drivers/usb/dwc3/dwc3-am62.c | 125 -----
> drivers/usb/dwc3/ep0.c | 217 ++++----
> drivers/usb/dwc3/gadget.c | 482 +++++++++--------
> drivers/usb/dwc3/gadget.h | 16 +-
> drivers/usb/dwc3/io.h | 54 +-
> drivers/usb/dwc3/linux-compat.h | 16 -
> drivers/usb/dwc3/platform_data.h | 47 ++
> drivers/usb/dwc3/samsung_usb_phy.c | 77 ---
> 12 files changed, 1214 insertions(+), 1255 deletions(-)
> diff --git a/drivers/usb/dwc3/samsung_usb_phy.c b/drivers/usb/dwc3/samsung_usb_phy.c
> deleted file mode 100644
> index 3563070cb85..00000000000
> --- a/drivers/usb/dwc3/samsung_usb_phy.c
> +++ /dev/null
> @@ -1,77 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -/**
> - * samsung_usb_phy.c - DesignWare USB3 (DWC3) PHY handling file
Doesn't this mean that odroid-xu3's USB is removed?
(CONFIG_USB_DWC3_PHY_SAMSUNG). Does it come back later?
Regards,
Simon
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (63 preceding siblings ...)
2026-05-07 9:28 ` [RFC PATCH v2 64/64] usb: fix build after resync of DWC3 with " Jens Wiklander
@ 2026-05-08 15:42 ` Simon Glass
2026-05-08 16:03 ` Tom Rini
2026-05-11 6:31 ` Michal Simek
2026-05-11 8:27 ` Anshul Dalal
` (2 subsequent siblings)
67 siblings, 2 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-08 15:42 UTC (permalink / raw)
To: Jens Wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier
Hi Jens,
On Thu, 7 May 2026 at 03:28, Jens Wiklander <jens.wiklander@linaro.org> wrote:
>
> Hi,
>
> This is a follow-up to Jerome's patchset [1], addressing previous feedback
> regarding the monolithic nature of the driver update.
>
> The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> to u-boot"). Since then, not many kernel changes have been ported back into
> U-Boot.
>
> This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> a clear audit trail and maintain bisectability, I have structured the
> series as follows:
>
> 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> a "snapshot" import of the drivers/usb/dwc3 directory for every major
> kernel version (v3.19 through v6.16-rc7).
> 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> required for U-Boot integration.
>
> The final diff is identical to [1]. I decided to stick with that for now to
> focus on the method of how we import or update the code.
>
> Note that this is compile-tested only. The CI pipeline on source.denx.de
> was used as an OK/NOK indicator [2].
>
> The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> this diff is identical to the previous, it should still work on that
> platform. With the help of a custom build script [3] and with an additional
> patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> platforms using the DWC3 driver are still OK, but at least there is some
> hope. If this breaks your platform I'd like to know, and if you can send a
> fix it's even better.
>
> I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> were a few conflicts, even when backing out the original U-Boot patches on
> top of the original v3.19-rc1 import. However, the resulting state still
> diverged significantly from [1].
>
> Instead, I've imported each new kernel in a separate commit. That way it's
> very clear which kernel patches are included. Since there aren't too many
> patches for each kernel I'm listing the relevant commits in the U-Boot
> commit message for easier reference. I did this with a script so it's easy
> to make changes, if the approach is OK but we need to tune it. With this
> approach it should be easy to tell if a Fixes patch for the kernel might
> also be needed here.
>
> Thanks,
> Jens
>
> [1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
> [2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
> [3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
> [4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
>
> RFC v1 -> RFC v2:
> - Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
> dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
> from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
>
> Jens Wiklander (60):
> usb: dwc3: restore to original v3.19-rc1 kernel import
> usb: dwc3: import from kernel v3.19
> usb: dwc3: import from kernel v4.0
> usb: dwc3: import from kernel v4.1
> usb: dwc3: import from kernel v4.2
> usb: dwc3: import from kernel v4.3
> usb: dwc3: import from kernel v4.4
> usb: dwc3: import from kernel v4.5
> usb: dwc3: import from kernel v4.6
> usb: dwc3: import from kernel v4.7
> usb: dwc3: import from kernel v4.8
> usb: dwc3: import from kernel v4.9
> usb: dwc3: import from kernel v4.10
> usb: dwc3: import from kernel v4.11
> usb: dwc3: import from kernel v4.12
> usb: dwc3: import from kernel v4.13
> usb: dwc3: import from kernel v4.14
> usb: dwc3: import from kernel v4.15
> usb: dwc3: import from kernel v4.16
> usb: dwc3: import from kernel v4.17
> usb: dwc3: import from kernel v4.18
> usb: dwc3: import from kernel v4.19
> usb: dwc3: import from kernel v4.20
> usb: dwc3: import from kernel v5.0
> usb: dwc3: import from kernel v5.1
> usb: dwc3: import from kernel v5.2
> usb: dwc3: import from kernel v5.3
> usb: dwc3: import from kernel v5.4
> usb: dwc3: import from kernel v5.5
> usb: dwc3: import from kernel v5.6
> usb: dwc3: import from kernel v5.7
> usb: dwc3: import from kernel v5.8
> usb: dwc3: import from kernel v5.9
> usb: dwc3: import from kernel v5.10
> usb: dwc3: import from kernel v5.11
> usb: dwc3: import from kernel v5.12
> usb: dwc3: import from kernel v5.13
> usb: dwc3: import from kernel v5.14
> usb: dwc3: import from kernel v5.15
> usb: dwc3: import from kernel v5.16
> usb: dwc3: import from kernel v5.17
> usb: dwc3: import from kernel v5.18
> usb: dwc3: import from kernel v5.19
> usb: dwc3: import from kernel v6.0
> usb: dwc3: import from kernel v6.1
> usb: dwc3: import from kernel v6.2
> usb: dwc3: import from kernel v6.3
> usb: dwc3: import from kernel v6.4
> usb: dwc3: import from kernel v6.5
> usb: dwc3: import from kernel v6.6
> usb: dwc3: import from kernel v6.7
> usb: dwc3: import from kernel v6.8
> usb: dwc3: import from kernel v6.9
> usb: dwc3: import from kernel v6.10
> usb: dwc3: import from kernel v6.11
> usb: dwc3: import from kernel v6.12
> usb: dwc3: import from kernel v6.13
> usb: dwc3: import from kernel v6.14
> usb: dwc3: import from kernel v6.15
> usb: dwc3: import from kernel v6.16-rc7
>
> Jerome Forissier (4):
> usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
> usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
> usb: udc: re-import udc-core.c from kernel v6.16-rc7
> usb: fix build after resync of DWC3 with kernel v6.16-rc7
>
> drivers/usb/cdns3/ep0.c | 8 +-
> drivers/usb/common/common.c | 23 +
> drivers/usb/dwc3/Makefile | 57 +-
> drivers/usb/dwc3/core.c | 1780 ++++++----
> drivers/usb/dwc3/core.h | 1120 +++++--
> drivers/usb/dwc3/debug.h | 432 +++
> drivers/usb/dwc3/dwc3-omap.c | 2 -
> drivers/usb/dwc3/ep0.c | 737 +++--
> drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
> drivers/usb/dwc3/gadget.h | 124 +-
> drivers/usb/dwc3/glue.h | 36 +
> drivers/usb/dwc3/io.h | 29 +-
> drivers/usb/dwc3/linux-compat.h | 16 -
> drivers/usb/dwc3/samsung_usb_phy.c | 77 -
> drivers/usb/dwc3/ti_usb_phy.c | 2 -
> drivers/usb/gadget/at91_udc.c | 46 -
> drivers/usb/gadget/atmel_usba_udc.c | 102 +-
> drivers/usb/gadget/ci_udc.c | 419 ---
> drivers/usb/gadget/composite.c | 2 +-
> drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
> drivers/usb/gadget/epautoconf.c | 289 +-
> drivers/usb/gadget/ether.c | 21 +-
> drivers/usb/gadget/f_acm.c | 20 +-
> drivers/usb/gadget/f_fastboot.c | 10 +-
> drivers/usb/gadget/f_mass_storage.c | 5 +-
> drivers/usb/gadget/f_rockusb.c | 11 +-
> drivers/usb/gadget/f_sdp.c | 12 +-
> drivers/usb/gadget/f_thor.c | 19 +-
> drivers/usb/gadget/udc/Makefile | 1 +
> drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
> drivers/usb/host/xhci-dwc3.c | 4 +-
> drivers/usb/host/xhci-ext-caps.h | 158 +
> drivers/usb/host/xhci-exynos5.c | 2 +-
> drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
> drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
> drivers/usb/musb-new/musb_uboot.c | 39 -
> include/dm/device_compat.h | 13 +
> include/dm/read.h | 46 +
> include/linux/compat.h | 15 +
> include/linux/usb/ch9.h | 25 +-
> include/linux/usb/gadget.h | 520 +--
> include/linux/usb/otg.h | 10 +
> include/linux/usb/phy.h | 56 +
> 43 files changed, 7659 insertions(+), 4091 deletions(-)
> create mode 100644 drivers/usb/dwc3/debug.h
> create mode 100644 drivers/usb/dwc3/glue.h
> delete mode 100644 drivers/usb/dwc3/linux-compat.h
> delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
> create mode 100644 drivers/usb/host/xhci-ext-caps.h
>
>
> base-commit: 0f865ab5d68484b4dc1724809de1be06edc85df1
> --
> 2.43.0
>
This makes me wonder what USB tests we have. Some boards use USB for
Ethernet, but are there any storage tests, etc.?
Regards,
Simon
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-08 15:42 ` [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Simon Glass
@ 2026-05-08 16:03 ` Tom Rini
2026-05-11 13:13 ` Simon Glass
2026-05-11 6:31 ` Michal Simek
1 sibling, 1 reply; 102+ messages in thread
From: Tom Rini @ 2026-05-08 16:03 UTC (permalink / raw)
To: Simon Glass
Cc: Jens Wiklander, u-boot, Marek Vasut, Ilias Apalodimas,
Andre Przywara, Andrew Goodbody, Anshul Dalal, Bin Meng,
Casey Connolly, Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream,
Ion Agorria, Junhui Liu, Kongyang Liu, Lukasz Majewski,
Mattijs Korpershoek, Neil Armstrong, Patrice Chotard,
Quentin Schulz, Rasmus Villemoes, Ryder Lee, Stephan Gerhold,
Svyatoslav Ryhel, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
[-- Attachment #1: Type: text/plain, Size: 9799 bytes --]
On Fri, May 08, 2026 at 09:42:40AM -0600, Simon Glass wrote:
> Hi Jens,
>
> On Thu, 7 May 2026 at 03:28, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> >
> > Hi,
> >
> > This is a follow-up to Jerome's patchset [1], addressing previous feedback
> > regarding the monolithic nature of the driver update.
> >
> > The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> > ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> > to u-boot"). Since then, not many kernel changes have been ported back into
> > U-Boot.
> >
> > This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> > a clear audit trail and maintain bisectability, I have structured the
> > series as follows:
> >
> > 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> > to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> > 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> > a "snapshot" import of the drivers/usb/dwc3 directory for every major
> > kernel version (v3.19 through v6.16-rc7).
> > 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> > re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> > required for U-Boot integration.
> >
> > The final diff is identical to [1]. I decided to stick with that for now to
> > focus on the method of how we import or update the code.
> >
> > Note that this is compile-tested only. The CI pipeline on source.denx.de
> > was used as an OK/NOK indicator [2].
> >
> > The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> > this diff is identical to the previous, it should still work on that
> > platform. With the help of a custom build script [3] and with an additional
> > patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> > plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> > platforms using the DWC3 driver are still OK, but at least there is some
> > hope. If this breaks your platform I'd like to know, and if you can send a
> > fix it's even better.
> >
> > I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> > were a few conflicts, even when backing out the original U-Boot patches on
> > top of the original v3.19-rc1 import. However, the resulting state still
> > diverged significantly from [1].
> >
> > Instead, I've imported each new kernel in a separate commit. That way it's
> > very clear which kernel patches are included. Since there aren't too many
> > patches for each kernel I'm listing the relevant commits in the U-Boot
> > commit message for easier reference. I did this with a script so it's easy
> > to make changes, if the approach is OK but we need to tune it. With this
> > approach it should be easy to tell if a Fixes patch for the kernel might
> > also be needed here.
> >
> > Thanks,
> > Jens
> >
> > [1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
> > [2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
> > [3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
> > [4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
> >
> > RFC v1 -> RFC v2:
> > - Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
> > dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
> > from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
> >
> > Jens Wiklander (60):
> > usb: dwc3: restore to original v3.19-rc1 kernel import
> > usb: dwc3: import from kernel v3.19
> > usb: dwc3: import from kernel v4.0
> > usb: dwc3: import from kernel v4.1
> > usb: dwc3: import from kernel v4.2
> > usb: dwc3: import from kernel v4.3
> > usb: dwc3: import from kernel v4.4
> > usb: dwc3: import from kernel v4.5
> > usb: dwc3: import from kernel v4.6
> > usb: dwc3: import from kernel v4.7
> > usb: dwc3: import from kernel v4.8
> > usb: dwc3: import from kernel v4.9
> > usb: dwc3: import from kernel v4.10
> > usb: dwc3: import from kernel v4.11
> > usb: dwc3: import from kernel v4.12
> > usb: dwc3: import from kernel v4.13
> > usb: dwc3: import from kernel v4.14
> > usb: dwc3: import from kernel v4.15
> > usb: dwc3: import from kernel v4.16
> > usb: dwc3: import from kernel v4.17
> > usb: dwc3: import from kernel v4.18
> > usb: dwc3: import from kernel v4.19
> > usb: dwc3: import from kernel v4.20
> > usb: dwc3: import from kernel v5.0
> > usb: dwc3: import from kernel v5.1
> > usb: dwc3: import from kernel v5.2
> > usb: dwc3: import from kernel v5.3
> > usb: dwc3: import from kernel v5.4
> > usb: dwc3: import from kernel v5.5
> > usb: dwc3: import from kernel v5.6
> > usb: dwc3: import from kernel v5.7
> > usb: dwc3: import from kernel v5.8
> > usb: dwc3: import from kernel v5.9
> > usb: dwc3: import from kernel v5.10
> > usb: dwc3: import from kernel v5.11
> > usb: dwc3: import from kernel v5.12
> > usb: dwc3: import from kernel v5.13
> > usb: dwc3: import from kernel v5.14
> > usb: dwc3: import from kernel v5.15
> > usb: dwc3: import from kernel v5.16
> > usb: dwc3: import from kernel v5.17
> > usb: dwc3: import from kernel v5.18
> > usb: dwc3: import from kernel v5.19
> > usb: dwc3: import from kernel v6.0
> > usb: dwc3: import from kernel v6.1
> > usb: dwc3: import from kernel v6.2
> > usb: dwc3: import from kernel v6.3
> > usb: dwc3: import from kernel v6.4
> > usb: dwc3: import from kernel v6.5
> > usb: dwc3: import from kernel v6.6
> > usb: dwc3: import from kernel v6.7
> > usb: dwc3: import from kernel v6.8
> > usb: dwc3: import from kernel v6.9
> > usb: dwc3: import from kernel v6.10
> > usb: dwc3: import from kernel v6.11
> > usb: dwc3: import from kernel v6.12
> > usb: dwc3: import from kernel v6.13
> > usb: dwc3: import from kernel v6.14
> > usb: dwc3: import from kernel v6.15
> > usb: dwc3: import from kernel v6.16-rc7
> >
> > Jerome Forissier (4):
> > usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
> > usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
> > usb: udc: re-import udc-core.c from kernel v6.16-rc7
> > usb: fix build after resync of DWC3 with kernel v6.16-rc7
> >
> > drivers/usb/cdns3/ep0.c | 8 +-
> > drivers/usb/common/common.c | 23 +
> > drivers/usb/dwc3/Makefile | 57 +-
> > drivers/usb/dwc3/core.c | 1780 ++++++----
> > drivers/usb/dwc3/core.h | 1120 +++++--
> > drivers/usb/dwc3/debug.h | 432 +++
> > drivers/usb/dwc3/dwc3-omap.c | 2 -
> > drivers/usb/dwc3/ep0.c | 737 +++--
> > drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
> > drivers/usb/dwc3/gadget.h | 124 +-
> > drivers/usb/dwc3/glue.h | 36 +
> > drivers/usb/dwc3/io.h | 29 +-
> > drivers/usb/dwc3/linux-compat.h | 16 -
> > drivers/usb/dwc3/samsung_usb_phy.c | 77 -
> > drivers/usb/dwc3/ti_usb_phy.c | 2 -
> > drivers/usb/gadget/at91_udc.c | 46 -
> > drivers/usb/gadget/atmel_usba_udc.c | 102 +-
> > drivers/usb/gadget/ci_udc.c | 419 ---
> > drivers/usb/gadget/composite.c | 2 +-
> > drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
> > drivers/usb/gadget/epautoconf.c | 289 +-
> > drivers/usb/gadget/ether.c | 21 +-
> > drivers/usb/gadget/f_acm.c | 20 +-
> > drivers/usb/gadget/f_fastboot.c | 10 +-
> > drivers/usb/gadget/f_mass_storage.c | 5 +-
> > drivers/usb/gadget/f_rockusb.c | 11 +-
> > drivers/usb/gadget/f_sdp.c | 12 +-
> > drivers/usb/gadget/f_thor.c | 19 +-
> > drivers/usb/gadget/udc/Makefile | 1 +
> > drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
> > drivers/usb/host/xhci-dwc3.c | 4 +-
> > drivers/usb/host/xhci-ext-caps.h | 158 +
> > drivers/usb/host/xhci-exynos5.c | 2 +-
> > drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
> > drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
> > drivers/usb/musb-new/musb_uboot.c | 39 -
> > include/dm/device_compat.h | 13 +
> > include/dm/read.h | 46 +
> > include/linux/compat.h | 15 +
> > include/linux/usb/ch9.h | 25 +-
> > include/linux/usb/gadget.h | 520 +--
> > include/linux/usb/otg.h | 10 +
> > include/linux/usb/phy.h | 56 +
> > 43 files changed, 7659 insertions(+), 4091 deletions(-)
> > create mode 100644 drivers/usb/dwc3/debug.h
> > create mode 100644 drivers/usb/dwc3/glue.h
> > delete mode 100644 drivers/usb/dwc3/linux-compat.h
> > delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
> > create mode 100644 drivers/usb/host/xhci-ext-caps.h
> >
> >
> > base-commit: 0f865ab5d68484b4dc1724809de1be06edc85df1
> > --
> > 2.43.0
> >
>
> This makes me wonder what USB tests we have. Some boards use USB for
> Ethernet, but are there any storage tests, etc.?
We've had test/py/tests/test_usb.py for two years now, and so yes, we
have some hardware tests for USB functionality. I haven't tried getting
them enabled in the sage labe, but it's on the TODO list. Along with our
DFU tests, again.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-08 15:42 ` [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Simon Glass
2026-05-08 16:03 ` Tom Rini
@ 2026-05-11 6:31 ` Michal Simek
1 sibling, 0 replies; 102+ messages in thread
From: Michal Simek @ 2026-05-11 6:31 UTC (permalink / raw)
To: Simon Glass, Jens Wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier
On 5/8/26 17:42, Simon Glass wrote:
> Hi Jens,
>
> On Thu, 7 May 2026 at 03:28, Jens Wiklander <jens.wiklander@linaro.org> wrote:
>>
>> Hi,
>>
>> This is a follow-up to Jerome's patchset [1], addressing previous feedback
>> regarding the monolithic nature of the driver update.
>>
>> The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
>> ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
>> to u-boot"). Since then, not many kernel changes have been ported back into
>> U-Boot.
>>
>> This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
>> a clear audit trail and maintain bisectability, I have structured the
>> series as follows:
>>
>> 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
>> to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
>> 2. Milestone Imports: A sequence of 50+ commits follows, each performing
>> a "snapshot" import of the drivers/usb/dwc3 directory for every major
>> kernel version (v3.19 through v6.16-rc7).
>> 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
>> re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
>> required for U-Boot integration.
>>
>> The final diff is identical to [1]. I decided to stick with that for now to
>> focus on the method of how we import or update the code.
>>
>> Note that this is compile-tested only. The CI pipeline on source.denx.de
>> was used as an OK/NOK indicator [2].
>>
>> The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
>> this diff is identical to the previous, it should still work on that
>> platform. With the help of a custom build script [3] and with an additional
>> patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
>> plugged into one of its USB 3.0 ports. It certainly doesn't mean all
>> platforms using the DWC3 driver are still OK, but at least there is some
>> hope. If this breaks your platform I'd like to know, and if you can send a
>> fix it's even better.
>>
>> I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
>> were a few conflicts, even when backing out the original U-Boot patches on
>> top of the original v3.19-rc1 import. However, the resulting state still
>> diverged significantly from [1].
>>
>> Instead, I've imported each new kernel in a separate commit. That way it's
>> very clear which kernel patches are included. Since there aren't too many
>> patches for each kernel I'm listing the relevant commits in the U-Boot
>> commit message for easier reference. I did this with a script so it's easy
>> to make changes, if the approach is OK but we need to tune it. With this
>> approach it should be easy to tell if a Fixes patch for the kernel might
>> also be needed here.
>>
>> Thanks,
>> Jens
>>
>> [1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
>> [2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
>> [3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
>> [4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
>>
>> RFC v1 -> RFC v2:
>> - Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
>> dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
>> from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
>>
>> Jens Wiklander (60):
>> usb: dwc3: restore to original v3.19-rc1 kernel import
>> usb: dwc3: import from kernel v3.19
>> usb: dwc3: import from kernel v4.0
>> usb: dwc3: import from kernel v4.1
>> usb: dwc3: import from kernel v4.2
>> usb: dwc3: import from kernel v4.3
>> usb: dwc3: import from kernel v4.4
>> usb: dwc3: import from kernel v4.5
>> usb: dwc3: import from kernel v4.6
>> usb: dwc3: import from kernel v4.7
>> usb: dwc3: import from kernel v4.8
>> usb: dwc3: import from kernel v4.9
>> usb: dwc3: import from kernel v4.10
>> usb: dwc3: import from kernel v4.11
>> usb: dwc3: import from kernel v4.12
>> usb: dwc3: import from kernel v4.13
>> usb: dwc3: import from kernel v4.14
>> usb: dwc3: import from kernel v4.15
>> usb: dwc3: import from kernel v4.16
>> usb: dwc3: import from kernel v4.17
>> usb: dwc3: import from kernel v4.18
>> usb: dwc3: import from kernel v4.19
>> usb: dwc3: import from kernel v4.20
>> usb: dwc3: import from kernel v5.0
>> usb: dwc3: import from kernel v5.1
>> usb: dwc3: import from kernel v5.2
>> usb: dwc3: import from kernel v5.3
>> usb: dwc3: import from kernel v5.4
>> usb: dwc3: import from kernel v5.5
>> usb: dwc3: import from kernel v5.6
>> usb: dwc3: import from kernel v5.7
>> usb: dwc3: import from kernel v5.8
>> usb: dwc3: import from kernel v5.9
>> usb: dwc3: import from kernel v5.10
>> usb: dwc3: import from kernel v5.11
>> usb: dwc3: import from kernel v5.12
>> usb: dwc3: import from kernel v5.13
>> usb: dwc3: import from kernel v5.14
>> usb: dwc3: import from kernel v5.15
>> usb: dwc3: import from kernel v5.16
>> usb: dwc3: import from kernel v5.17
>> usb: dwc3: import from kernel v5.18
>> usb: dwc3: import from kernel v5.19
>> usb: dwc3: import from kernel v6.0
>> usb: dwc3: import from kernel v6.1
>> usb: dwc3: import from kernel v6.2
>> usb: dwc3: import from kernel v6.3
>> usb: dwc3: import from kernel v6.4
>> usb: dwc3: import from kernel v6.5
>> usb: dwc3: import from kernel v6.6
>> usb: dwc3: import from kernel v6.7
>> usb: dwc3: import from kernel v6.8
>> usb: dwc3: import from kernel v6.9
>> usb: dwc3: import from kernel v6.10
>> usb: dwc3: import from kernel v6.11
>> usb: dwc3: import from kernel v6.12
>> usb: dwc3: import from kernel v6.13
>> usb: dwc3: import from kernel v6.14
>> usb: dwc3: import from kernel v6.15
>> usb: dwc3: import from kernel v6.16-rc7
>>
>> Jerome Forissier (4):
>> usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
>> usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
>> usb: udc: re-import udc-core.c from kernel v6.16-rc7
>> usb: fix build after resync of DWC3 with kernel v6.16-rc7
>>
>> drivers/usb/cdns3/ep0.c | 8 +-
>> drivers/usb/common/common.c | 23 +
>> drivers/usb/dwc3/Makefile | 57 +-
>> drivers/usb/dwc3/core.c | 1780 ++++++----
>> drivers/usb/dwc3/core.h | 1120 +++++--
>> drivers/usb/dwc3/debug.h | 432 +++
>> drivers/usb/dwc3/dwc3-omap.c | 2 -
>> drivers/usb/dwc3/ep0.c | 737 +++--
>> drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
>> drivers/usb/dwc3/gadget.h | 124 +-
>> drivers/usb/dwc3/glue.h | 36 +
>> drivers/usb/dwc3/io.h | 29 +-
>> drivers/usb/dwc3/linux-compat.h | 16 -
>> drivers/usb/dwc3/samsung_usb_phy.c | 77 -
>> drivers/usb/dwc3/ti_usb_phy.c | 2 -
>> drivers/usb/gadget/at91_udc.c | 46 -
>> drivers/usb/gadget/atmel_usba_udc.c | 102 +-
>> drivers/usb/gadget/ci_udc.c | 419 ---
>> drivers/usb/gadget/composite.c | 2 +-
>> drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
>> drivers/usb/gadget/epautoconf.c | 289 +-
>> drivers/usb/gadget/ether.c | 21 +-
>> drivers/usb/gadget/f_acm.c | 20 +-
>> drivers/usb/gadget/f_fastboot.c | 10 +-
>> drivers/usb/gadget/f_mass_storage.c | 5 +-
>> drivers/usb/gadget/f_rockusb.c | 11 +-
>> drivers/usb/gadget/f_sdp.c | 12 +-
>> drivers/usb/gadget/f_thor.c | 19 +-
>> drivers/usb/gadget/udc/Makefile | 1 +
>> drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
>> drivers/usb/host/xhci-dwc3.c | 4 +-
>> drivers/usb/host/xhci-ext-caps.h | 158 +
>> drivers/usb/host/xhci-exynos5.c | 2 +-
>> drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
>> drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
>> drivers/usb/musb-new/musb_uboot.c | 39 -
>> include/dm/device_compat.h | 13 +
>> include/dm/read.h | 46 +
>> include/linux/compat.h | 15 +
>> include/linux/usb/ch9.h | 25 +-
>> include/linux/usb/gadget.h | 520 +--
>> include/linux/usb/otg.h | 10 +
>> include/linux/usb/phy.h | 56 +
>> 43 files changed, 7659 insertions(+), 4091 deletions(-)
>> create mode 100644 drivers/usb/dwc3/debug.h
>> create mode 100644 drivers/usb/dwc3/glue.h
>> delete mode 100644 drivers/usb/dwc3/linux-compat.h
>> delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
>> create mode 100644 drivers/usb/host/xhci-ext-caps.h
>>
>>
>> base-commit: 0f865ab5d68484b4dc1724809de1be06edc85df1
Why is it based on such a old commit ID?
Thanks,
Michal
commit 0f865ab5d68484b4dc1724809de1be06edc85df1
Author: Tom Rini <trini@konsulko.com>
AuthorDate: Mon Oct 20 13:55:08 2025 -0600
Commit: Tom Rini <trini@konsulko.com>
CommitDate: Mon Oct 20 13:55:37 2025 -0600
spl: Restore args file being default in falcon mode
When falcon mode is enabled and SPL_OS_BOOT_SECURE is not enabled,
restore the previous default behavior of having an args file be
expected. Platforms which are using a FIT image here and do not need
this can update at their convenience to disable this option now.
Fixes: b1a3ed068869 ("spl: make args file optional in falcon mode")
Signed-off-by: Tom Rini <trini@konsulko.com>
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 64/64] usb: fix build after resync of DWC3 with kernel v6.16-rc7
2026-05-07 9:28 ` [RFC PATCH v2 64/64] usb: fix build after resync of DWC3 with " Jens Wiklander
@ 2026-05-11 8:25 ` Anshul Dalal
0 siblings, 0 replies; 102+ messages in thread
From: Anshul Dalal @ 2026-05-11 8:25 UTC (permalink / raw)
To: Jens Wiklander, u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Jerome Forissier
On Thu May 7, 2026 at 2:58 PM IST, Jens Wiklander wrote:
> From: Jerome Forissier <jerome.forissier@linaro.org>
>
> Fix build errors after the re-sync of the DWC3 driver with the kernel.
> U-Boot has different needs than the kernel: buses, interrupts, internal
> APIs (DMA, traces, DT...) so many adaptations are required. This commit
> re-introduces many of the changes that were done locally after the
> initial import of the DWC3 code from kernel 3.19-rc1 11 years ago, as
> well as other fixes. This is compile-tested only.
>
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
> ---
> drivers/usb/cdns3/ep0.c | 8 +-
> drivers/usb/common/common.c | 23 +
> drivers/usb/dwc3/Makefile | 57 +-
> drivers/usb/dwc3/core.c | 1761 ++++++------------------
> drivers/usb/dwc3/core.h | 39 +-
> drivers/usb/dwc3/dwc3-am62.c | 424 +-----
> drivers/usb/dwc3/dwc3-omap.c | 2 -
> drivers/usb/dwc3/ep0.c | 110 +-
> drivers/usb/dwc3/gadget.c | 786 +++--------
> drivers/usb/dwc3/gadget.h | 4 +-
> drivers/usb/dwc3/io.h | 28 +-
> drivers/usb/dwc3/ti_usb_phy.c | 2 -
> drivers/usb/gadget/at91_udc.c | 46 -
> drivers/usb/gadget/atmel_usba_udc.c | 102 +-
> drivers/usb/gadget/ci_udc.c | 419 ------
> drivers/usb/gadget/composite.c | 2 +-
> drivers/usb/gadget/dwc2_udc_otg.c | 174 +--
> drivers/usb/gadget/epautoconf.c | 2 -
> drivers/usb/gadget/ether.c | 21 +-
> drivers/usb/gadget/f_acm.c | 20 +-
> drivers/usb/gadget/f_fastboot.c | 10 +-
> drivers/usb/gadget/f_mass_storage.c | 5 +-
> drivers/usb/gadget/f_rockusb.c | 11 +-
> drivers/usb/gadget/f_sdp.c | 12 +-
> drivers/usb/gadget/f_thor.c | 19 +-
> drivers/usb/gadget/udc/Makefile | 1 +
> drivers/usb/gadget/udc/udc-core.c | 1021 +++-----------
> drivers/usb/host/xhci-dwc3.c | 4 +-
> drivers/usb/host/xhci-exynos5.c | 2 +-
> drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
> drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
> drivers/usb/musb-new/musb_uboot.c | 39 -
> include/dm/device_compat.h | 13 +
> include/dm/read.h | 46 +
> include/linux/compat.h | 15 +
> include/linux/usb/ch9.h | 25 +-
> include/linux/usb/gadget.h | 520 ++-----
> include/linux/usb/otg.h | 10 +
> include/linux/usb/phy.h | 56 +
> 39 files changed, 1347 insertions(+), 4532 deletions(-)
>
[snip]
> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> index a619cd374fb4..ae1725756c0e 100644
> --- a/drivers/usb/dwc3/Makefile
> +++ b/drivers/usb/dwc3/Makefile
> @@ -1,18 +1,59 @@
> -# SPDX-License-Identifier: GPL-2.0+
> -
> +# SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_USB_DWC3) += dwc3.o
>
> dwc3-y := core.o
>
> -obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o
> +ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
> + dwc3-y += host.o
> +endif
> +
> +ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
> + dwc3-y += gadget.o ep0.o
> +endif
> +
> +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
> + dwc3-y += drd.o
> +endif
> +
> +ifneq ($(CONFIG_USB_DWC3_ULPI),)
> + dwc3-y += ulpi.o
> +endif
> +
> +ifneq ($(CONFIG_DEBUG_FS),)
> + dwc3-y += debugfs.o
> +endif
> +
> +##
> +# Platform-specific glue layers go here
> +#
> +# NOTICE: Make sure your glue layer doesn't depend on anything
> +# which is arch-specific and that it compiles on all situations.
> +#
> +# We want to keep this requirement in order to be able to compile
> +# the entire driver (with all its glue layers) on several architectures
> +# and make sure it compiles fine. This will also help with allmodconfig
> +# and allyesconfig builds.
> +##
>
> -obj-$(CONFIG_$(PHASE_)USB_DWC3_AM62) += dwc3-am62.o
> +obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o
Why is this change being made here? We need dwc3-am62 driver at SPL
stage for DFU boot on K3 devices. The Kconfig should have a
corresponding CONFIG_SPL_USB_DWC3_AM62 symbol as well.
Regards,
Anshul
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (64 preceding siblings ...)
2026-05-08 15:42 ` [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Simon Glass
@ 2026-05-11 8:27 ` Anshul Dalal
2026-05-11 14:58 ` Tom Rini
2026-05-12 14:52 ` Alexey Charkov
2026-05-14 11:49 ` Michal Simek
67 siblings, 1 reply; 102+ messages in thread
From: Anshul Dalal @ 2026-05-11 8:27 UTC (permalink / raw)
To: Jens Wiklander, u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Chintan Vankar, Siddharth Vadapalli
On Thu May 7, 2026 at 2:57 PM IST, Jens Wiklander wrote:
> Hi,
>
> This is a follow-up to Jerome's patchset [1], addressing previous feedback
> regarding the monolithic nature of the driver update.
>
> The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> to u-boot"). Since then, not many kernel changes have been ported back into
> U-Boot.
>
> This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> a clear audit trail and maintain bisectability, I have structured the
> series as follows:
>
> 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> a "snapshot" import of the drivers/usb/dwc3 directory for every major
> kernel version (v3.19 through v6.16-rc7).
> 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> required for U-Boot integration.
>
> The final diff is identical to [1]. I decided to stick with that for now to
> focus on the method of how we import or update the code.
>
> Note that this is compile-tested only. The CI pipeline on source.denx.de
> was used as an OK/NOK indicator [2].
>
> The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> this diff is identical to the previous, it should still work on that
> platform. With the help of a custom build script [3] and with an additional
> patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> platforms using the DWC3 driver are still OK, but at least there is some
> hope. If this breaks your platform I'd like to know, and if you can send a
> fix it's even better.
>
> I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> were a few conflicts, even when backing out the original U-Boot patches on
> top of the original v3.19-rc1 import. However, the resulting state still
> diverged significantly from [1].
>
> Instead, I've imported each new kernel in a separate commit. That way it's
> very clear which kernel patches are included. Since there aren't too many
> patches for each kernel I'm listing the relevant commits in the U-Boot
> commit message for easier reference. I did this with a script so it's easy
> to make changes, if the approach is OK but we need to tune it. With this
> approach it should be easy to tell if a Fixes patch for the kernel might
> also be needed here.
Hi Jens,
Thanks for the work though I wasn't able to test out the series since it
doesn't apply cleanly on master.
Could you respin with a rebase?
CC+: Chintan, Siddharth
Regards,
Anshul
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-08 16:03 ` Tom Rini
@ 2026-05-11 13:13 ` Simon Glass
0 siblings, 0 replies; 102+ messages in thread
From: Simon Glass @ 2026-05-11 13:13 UTC (permalink / raw)
To: Tom Rini
Cc: Jens Wiklander, u-boot, Marek Vasut, Ilias Apalodimas,
Andre Przywara, Andrew Goodbody, Anshul Dalal, Bin Meng,
Casey Connolly, Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream,
Ion Agorria, Junhui Liu, Kongyang Liu, Lukasz Majewski,
Mattijs Korpershoek, Neil Armstrong, Patrice Chotard,
Quentin Schulz, Rasmus Villemoes, Ryder Lee, Stephan Gerhold,
Svyatoslav Ryhel, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
Hi Tom,
On Fri, 8 May 2026 at 10:03, Tom Rini <trini@konsulko.com> wrote:
>
> On Fri, May 08, 2026 at 09:42:40AM -0600, Simon Glass wrote:
> > Hi Jens,
> >
> > On Thu, 7 May 2026 at 03:28, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> > >
> > > Hi,
> > >
> > > This is a follow-up to Jerome's patchset [1], addressing previous feedback
> > > regarding the monolithic nature of the driver update.
> > >
> > > The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> > > ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> > > to u-boot"). Since then, not many kernel changes have been ported back into
> > > U-Boot.
> > >
> > > This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> > > a clear audit trail and maintain bisectability, I have structured the
> > > series as follows:
> > >
> > > 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> > > to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> > > 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> > > a "snapshot" import of the drivers/usb/dwc3 directory for every major
> > > kernel version (v3.19 through v6.16-rc7).
> > > 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> > > re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> > > required for U-Boot integration.
> > >
> > > The final diff is identical to [1]. I decided to stick with that for now to
> > > focus on the method of how we import or update the code.
> > >
> > > Note that this is compile-tested only. The CI pipeline on source.denx.de
> > > was used as an OK/NOK indicator [2].
> > >
> > > The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> > > this diff is identical to the previous, it should still work on that
> > > platform. With the help of a custom build script [3] and with an additional
> > > patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> > > plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> > > platforms using the DWC3 driver are still OK, but at least there is some
> > > hope. If this breaks your platform I'd like to know, and if you can send a
> > > fix it's even better.
> > >
> > > I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> > > were a few conflicts, even when backing out the original U-Boot patches on
> > > top of the original v3.19-rc1 import. However, the resulting state still
> > > diverged significantly from [1].
> > >
> > > Instead, I've imported each new kernel in a separate commit. That way it's
> > > very clear which kernel patches are included. Since there aren't too many
> > > patches for each kernel I'm listing the relevant commits in the U-Boot
> > > commit message for easier reference. I did this with a script so it's easy
> > > to make changes, if the approach is OK but we need to tune it. With this
> > > approach it should be easy to tell if a Fixes patch for the kernel might
> > > also be needed here.
> > >
> > > Thanks,
> > > Jens
> > >
> > > [1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
> > > [2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
> > > [3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
> > > [4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
> > >
> > > RFC v1 -> RFC v2:
> > > - Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
> > > dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
> > > from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
> > >
> > > Jens Wiklander (60):
> > > usb: dwc3: restore to original v3.19-rc1 kernel import
> > > usb: dwc3: import from kernel v3.19
> > > usb: dwc3: import from kernel v4.0
> > > usb: dwc3: import from kernel v4.1
> > > usb: dwc3: import from kernel v4.2
> > > usb: dwc3: import from kernel v4.3
> > > usb: dwc3: import from kernel v4.4
> > > usb: dwc3: import from kernel v4.5
> > > usb: dwc3: import from kernel v4.6
> > > usb: dwc3: import from kernel v4.7
> > > usb: dwc3: import from kernel v4.8
> > > usb: dwc3: import from kernel v4.9
> > > usb: dwc3: import from kernel v4.10
> > > usb: dwc3: import from kernel v4.11
> > > usb: dwc3: import from kernel v4.12
> > > usb: dwc3: import from kernel v4.13
> > > usb: dwc3: import from kernel v4.14
> > > usb: dwc3: import from kernel v4.15
> > > usb: dwc3: import from kernel v4.16
> > > usb: dwc3: import from kernel v4.17
> > > usb: dwc3: import from kernel v4.18
> > > usb: dwc3: import from kernel v4.19
> > > usb: dwc3: import from kernel v4.20
> > > usb: dwc3: import from kernel v5.0
> > > usb: dwc3: import from kernel v5.1
> > > usb: dwc3: import from kernel v5.2
> > > usb: dwc3: import from kernel v5.3
> > > usb: dwc3: import from kernel v5.4
> > > usb: dwc3: import from kernel v5.5
> > > usb: dwc3: import from kernel v5.6
> > > usb: dwc3: import from kernel v5.7
> > > usb: dwc3: import from kernel v5.8
> > > usb: dwc3: import from kernel v5.9
> > > usb: dwc3: import from kernel v5.10
> > > usb: dwc3: import from kernel v5.11
> > > usb: dwc3: import from kernel v5.12
> > > usb: dwc3: import from kernel v5.13
> > > usb: dwc3: import from kernel v5.14
> > > usb: dwc3: import from kernel v5.15
> > > usb: dwc3: import from kernel v5.16
> > > usb: dwc3: import from kernel v5.17
> > > usb: dwc3: import from kernel v5.18
> > > usb: dwc3: import from kernel v5.19
> > > usb: dwc3: import from kernel v6.0
> > > usb: dwc3: import from kernel v6.1
> > > usb: dwc3: import from kernel v6.2
> > > usb: dwc3: import from kernel v6.3
> > > usb: dwc3: import from kernel v6.4
> > > usb: dwc3: import from kernel v6.5
> > > usb: dwc3: import from kernel v6.6
> > > usb: dwc3: import from kernel v6.7
> > > usb: dwc3: import from kernel v6.8
> > > usb: dwc3: import from kernel v6.9
> > > usb: dwc3: import from kernel v6.10
> > > usb: dwc3: import from kernel v6.11
> > > usb: dwc3: import from kernel v6.12
> > > usb: dwc3: import from kernel v6.13
> > > usb: dwc3: import from kernel v6.14
> > > usb: dwc3: import from kernel v6.15
> > > usb: dwc3: import from kernel v6.16-rc7
> > >
> > > Jerome Forissier (4):
> > > usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
> > > usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
> > > usb: udc: re-import udc-core.c from kernel v6.16-rc7
> > > usb: fix build after resync of DWC3 with kernel v6.16-rc7
> > >
> > > drivers/usb/cdns3/ep0.c | 8 +-
> > > drivers/usb/common/common.c | 23 +
> > > drivers/usb/dwc3/Makefile | 57 +-
> > > drivers/usb/dwc3/core.c | 1780 ++++++----
> > > drivers/usb/dwc3/core.h | 1120 +++++--
> > > drivers/usb/dwc3/debug.h | 432 +++
> > > drivers/usb/dwc3/dwc3-omap.c | 2 -
> > > drivers/usb/dwc3/ep0.c | 737 +++--
> > > drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
> > > drivers/usb/dwc3/gadget.h | 124 +-
> > > drivers/usb/dwc3/glue.h | 36 +
> > > drivers/usb/dwc3/io.h | 29 +-
> > > drivers/usb/dwc3/linux-compat.h | 16 -
> > > drivers/usb/dwc3/samsung_usb_phy.c | 77 -
> > > drivers/usb/dwc3/ti_usb_phy.c | 2 -
> > > drivers/usb/gadget/at91_udc.c | 46 -
> > > drivers/usb/gadget/atmel_usba_udc.c | 102 +-
> > > drivers/usb/gadget/ci_udc.c | 419 ---
> > > drivers/usb/gadget/composite.c | 2 +-
> > > drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
> > > drivers/usb/gadget/epautoconf.c | 289 +-
> > > drivers/usb/gadget/ether.c | 21 +-
> > > drivers/usb/gadget/f_acm.c | 20 +-
> > > drivers/usb/gadget/f_fastboot.c | 10 +-
> > > drivers/usb/gadget/f_mass_storage.c | 5 +-
> > > drivers/usb/gadget/f_rockusb.c | 11 +-
> > > drivers/usb/gadget/f_sdp.c | 12 +-
> > > drivers/usb/gadget/f_thor.c | 19 +-
> > > drivers/usb/gadget/udc/Makefile | 1 +
> > > drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
> > > drivers/usb/host/xhci-dwc3.c | 4 +-
> > > drivers/usb/host/xhci-ext-caps.h | 158 +
> > > drivers/usb/host/xhci-exynos5.c | 2 +-
> > > drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
> > > drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
> > > drivers/usb/musb-new/musb_uboot.c | 39 -
> > > include/dm/device_compat.h | 13 +
> > > include/dm/read.h | 46 +
> > > include/linux/compat.h | 15 +
> > > include/linux/usb/ch9.h | 25 +-
> > > include/linux/usb/gadget.h | 520 +--
> > > include/linux/usb/otg.h | 10 +
> > > include/linux/usb/phy.h | 56 +
> > > 43 files changed, 7659 insertions(+), 4091 deletions(-)
> > > create mode 100644 drivers/usb/dwc3/debug.h
> > > create mode 100644 drivers/usb/dwc3/glue.h
> > > delete mode 100644 drivers/usb/dwc3/linux-compat.h
> > > delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
> > > create mode 100644 drivers/usb/host/xhci-ext-caps.h
> > >
> > >
> > > base-commit: 0f865ab5d68484b4dc1724809de1be06edc85df1
> > > --
> > > 2.43.0
> > >
> >
> > This makes me wonder what USB tests we have. Some boards use USB for
> > Ethernet, but are there any storage tests, etc.?
>
> We've had test/py/tests/test_usb.py for two years now, and so yes, we
> have some hardware tests for USB functionality. I haven't tried getting
> them enabled in the sage labe, but it's on the TODO list. Along with our
> DFU tests, again.
OK, good. We could add some tests to the SJG lab too, perhaps.
Regards,
SImon
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-11 8:27 ` Anshul Dalal
@ 2026-05-11 14:58 ` Tom Rini
0 siblings, 0 replies; 102+ messages in thread
From: Tom Rini @ 2026-05-11 14:58 UTC (permalink / raw)
To: Anshul Dalal
Cc: Jens Wiklander, u-boot, Marek Vasut, Ilias Apalodimas,
Andre Przywara, Andrew Goodbody, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier,
Chintan Vankar, Siddharth Vadapalli
[-- Attachment #1: Type: text/plain, Size: 3248 bytes --]
On Mon, May 11, 2026 at 01:57:25PM +0530, Anshul Dalal wrote:
> On Thu May 7, 2026 at 2:57 PM IST, Jens Wiklander wrote:
> > Hi,
> >
> > This is a follow-up to Jerome's patchset [1], addressing previous feedback
> > regarding the monolithic nature of the driver update.
> >
> > The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> > ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> > to u-boot"). Since then, not many kernel changes have been ported back into
> > U-Boot.
> >
> > This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> > a clear audit trail and maintain bisectability, I have structured the
> > series as follows:
> >
> > 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> > to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> > 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> > a "snapshot" import of the drivers/usb/dwc3 directory for every major
> > kernel version (v3.19 through v6.16-rc7).
> > 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> > re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> > required for U-Boot integration.
> >
> > The final diff is identical to [1]. I decided to stick with that for now to
> > focus on the method of how we import or update the code.
> >
> > Note that this is compile-tested only. The CI pipeline on source.denx.de
> > was used as an OK/NOK indicator [2].
> >
> > The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> > this diff is identical to the previous, it should still work on that
> > platform. With the help of a custom build script [3] and with an additional
> > patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> > plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> > platforms using the DWC3 driver are still OK, but at least there is some
> > hope. If this breaks your platform I'd like to know, and if you can send a
> > fix it's even better.
> >
> > I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> > were a few conflicts, even when backing out the original U-Boot patches on
> > top of the original v3.19-rc1 import. However, the resulting state still
> > diverged significantly from [1].
> >
> > Instead, I've imported each new kernel in a separate commit. That way it's
> > very clear which kernel patches are included. Since there aren't too many
> > patches for each kernel I'm listing the relevant commits in the U-Boot
> > commit message for easier reference. I did this with a script so it's easy
> > to make changes, if the approach is OK but we need to tune it. With this
> > approach it should be easy to tell if a Fixes patch for the kernel might
> > also be needed here.
>
> Hi Jens,
>
> Thanks for the work though I wasn't able to test out the series since it
> doesn't apply cleanly on master.
>
> Could you respin with a rebase?
For something this big and still RFC, perhaps a pointer to a public tree
is OK, until there's some substantive feedback from Marek?
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (65 preceding siblings ...)
2026-05-11 8:27 ` Anshul Dalal
@ 2026-05-12 14:52 ` Alexey Charkov
2026-05-14 11:49 ` Michal Simek
67 siblings, 0 replies; 102+ messages in thread
From: Alexey Charkov @ 2026-05-12 14:52 UTC (permalink / raw)
To: Jens Wiklander
Cc: u-boot, Marek Vasut, Ilias Apalodimas, Andre Przywara,
Andrew Goodbody, Anshul Dalal, Bin Meng, Casey Connolly,
Chunfeng Yun, Eddie Cai, GSS_MTK_Uboot_upstream, Ion Agorria,
Junhui Liu, Kongyang Liu, Lukasz Majewski, Mattijs Korpershoek,
Neil Armstrong, Patrice Chotard, Quentin Schulz, Rasmus Villemoes,
Ryder Lee, Simon Glass, Stephan Gerhold, Svyatoslav Ryhel,
Tom Rini, Varadarajan Narayanan, Weijie Gao, Zixun LI,
Jerome Forissier
On Tue, May 12, 2026 at 6:44 PM Jens Wiklander
<jens.wiklander@linaro.org> wrote:
>
> Hi,
>
> This is a follow-up to Jerome's patchset [1], addressing previous feedback
> regarding the monolithic nature of the driver update.
>
> The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> to u-boot"). Since then, not many kernel changes have been ported back into
> U-Boot.
>
> This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> a clear audit trail and maintain bisectability, I have structured the
> series as follows:
>
> 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> a "snapshot" import of the drivers/usb/dwc3 directory for every major
> kernel version (v3.19 through v6.16-rc7).
> 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> required for U-Boot integration.
>
> The final diff is identical to [1]. I decided to stick with that for now to
> focus on the method of how we import or update the code.
>
> Note that this is compile-tested only. The CI pipeline on source.denx.de
> was used as an OK/NOK indicator [2].
>
> The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> this diff is identical to the previous, it should still work on that
> platform. With the help of a custom build script [3] and with an additional
> patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> platforms using the DWC3 driver are still OK, but at least there is some
> hope. If this breaks your platform I'd like to know, and if you can send a
> fix it's even better.
>
> I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> were a few conflicts, even when backing out the original U-Boot patches on
> top of the original v3.19-rc1 import. However, the resulting state still
> diverged significantly from [1].
>
> Instead, I've imported each new kernel in a separate commit. That way it's
> very clear which kernel patches are included. Since there aren't too many
> patches for each kernel I'm listing the relevant commits in the U-Boot
> commit message for easier reference. I did this with a script so it's easy
> to make changes, if the approach is OK but we need to tune it. With this
> approach it should be easy to tell if a Fixes patch for the kernel might
> also be needed here.
>
> Thanks,
> Jens
>
> [1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
> [2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
> [3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
> [4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
>
> RFC v1 -> RFC v2:
> - Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
> dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
> from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
>
> Jens Wiklander (60):
> usb: dwc3: restore to original v3.19-rc1 kernel import
> usb: dwc3: import from kernel v3.19
> usb: dwc3: import from kernel v4.0
> usb: dwc3: import from kernel v4.1
> usb: dwc3: import from kernel v4.2
> usb: dwc3: import from kernel v4.3
> usb: dwc3: import from kernel v4.4
> usb: dwc3: import from kernel v4.5
> usb: dwc3: import from kernel v4.6
> usb: dwc3: import from kernel v4.7
> usb: dwc3: import from kernel v4.8
> usb: dwc3: import from kernel v4.9
> usb: dwc3: import from kernel v4.10
> usb: dwc3: import from kernel v4.11
> usb: dwc3: import from kernel v4.12
> usb: dwc3: import from kernel v4.13
> usb: dwc3: import from kernel v4.14
> usb: dwc3: import from kernel v4.15
> usb: dwc3: import from kernel v4.16
> usb: dwc3: import from kernel v4.17
> usb: dwc3: import from kernel v4.18
> usb: dwc3: import from kernel v4.19
> usb: dwc3: import from kernel v4.20
> usb: dwc3: import from kernel v5.0
> usb: dwc3: import from kernel v5.1
> usb: dwc3: import from kernel v5.2
> usb: dwc3: import from kernel v5.3
> usb: dwc3: import from kernel v5.4
> usb: dwc3: import from kernel v5.5
> usb: dwc3: import from kernel v5.6
> usb: dwc3: import from kernel v5.7
> usb: dwc3: import from kernel v5.8
> usb: dwc3: import from kernel v5.9
> usb: dwc3: import from kernel v5.10
> usb: dwc3: import from kernel v5.11
> usb: dwc3: import from kernel v5.12
> usb: dwc3: import from kernel v5.13
> usb: dwc3: import from kernel v5.14
> usb: dwc3: import from kernel v5.15
> usb: dwc3: import from kernel v5.16
> usb: dwc3: import from kernel v5.17
> usb: dwc3: import from kernel v5.18
> usb: dwc3: import from kernel v5.19
> usb: dwc3: import from kernel v6.0
> usb: dwc3: import from kernel v6.1
> usb: dwc3: import from kernel v6.2
> usb: dwc3: import from kernel v6.3
> usb: dwc3: import from kernel v6.4
> usb: dwc3: import from kernel v6.5
> usb: dwc3: import from kernel v6.6
> usb: dwc3: import from kernel v6.7
> usb: dwc3: import from kernel v6.8
> usb: dwc3: import from kernel v6.9
> usb: dwc3: import from kernel v6.10
> usb: dwc3: import from kernel v6.11
> usb: dwc3: import from kernel v6.12
> usb: dwc3: import from kernel v6.13
> usb: dwc3: import from kernel v6.14
> usb: dwc3: import from kernel v6.15
> usb: dwc3: import from kernel v6.16-rc7
>
> Jerome Forissier (4):
> usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
> usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
> usb: udc: re-import udc-core.c from kernel v6.16-rc7
> usb: fix build after resync of DWC3 with kernel v6.16-rc7
>
> drivers/usb/cdns3/ep0.c | 8 +-
> drivers/usb/common/common.c | 23 +
> drivers/usb/dwc3/Makefile | 57 +-
> drivers/usb/dwc3/core.c | 1780 ++++++----
> drivers/usb/dwc3/core.h | 1120 +++++--
> drivers/usb/dwc3/debug.h | 432 +++
> drivers/usb/dwc3/dwc3-omap.c | 2 -
> drivers/usb/dwc3/ep0.c | 737 +++--
> drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
> drivers/usb/dwc3/gadget.h | 124 +-
> drivers/usb/dwc3/glue.h | 36 +
> drivers/usb/dwc3/io.h | 29 +-
> drivers/usb/dwc3/linux-compat.h | 16 -
> drivers/usb/dwc3/samsung_usb_phy.c | 77 -
> drivers/usb/dwc3/ti_usb_phy.c | 2 -
> drivers/usb/gadget/at91_udc.c | 46 -
> drivers/usb/gadget/atmel_usba_udc.c | 102 +-
> drivers/usb/gadget/ci_udc.c | 419 ---
> drivers/usb/gadget/composite.c | 2 +-
> drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
> drivers/usb/gadget/epautoconf.c | 289 +-
> drivers/usb/gadget/ether.c | 21 +-
> drivers/usb/gadget/f_acm.c | 20 +-
> drivers/usb/gadget/f_fastboot.c | 10 +-
> drivers/usb/gadget/f_mass_storage.c | 5 +-
> drivers/usb/gadget/f_rockusb.c | 11 +-
> drivers/usb/gadget/f_sdp.c | 12 +-
> drivers/usb/gadget/f_thor.c | 19 +-
> drivers/usb/gadget/udc/Makefile | 1 +
> drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
> drivers/usb/host/xhci-dwc3.c | 4 +-
> drivers/usb/host/xhci-ext-caps.h | 158 +
> drivers/usb/host/xhci-exynos5.c | 2 +-
> drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
> drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
> drivers/usb/musb-new/musb_uboot.c | 39 -
> include/dm/device_compat.h | 13 +
> include/dm/read.h | 46 +
> include/linux/compat.h | 15 +
> include/linux/usb/ch9.h | 25 +-
> include/linux/usb/gadget.h | 520 +--
> include/linux/usb/otg.h | 10 +
> include/linux/usb/phy.h | 56 +
> 43 files changed, 7659 insertions(+), 4091 deletions(-)
> create mode 100644 drivers/usb/dwc3/debug.h
> create mode 100644 drivers/usb/dwc3/glue.h
> delete mode 100644 drivers/usb/dwc3/linux-compat.h
> delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
> create mode 100644 drivers/usb/host/xhci-ext-caps.h
Hi Jens,
This worked great on a Rockchip RK3576 board. No extra tricks [1] were
needed to initialize USB. Both host and gadget modes work out of the
box (I connected a USB flash drive to the board and exported it to a
computer via `ums 0 usb 0`). Starting, stopping, and restarting works.
Everything looks great.
Tested-by: Alexey Charkov <alchark@flipper.net>
Thanks a lot for your work!
Best regards,
Alexey
[1] https://lore.kernel.org/u-boot/20260427-rk3576-usb-gadget-v1-1-982fd1de08db@flipper.net/
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
` (66 preceding siblings ...)
2026-05-12 14:52 ` Alexey Charkov
@ 2026-05-14 11:49 ` Michal Simek
67 siblings, 0 replies; 102+ messages in thread
From: Michal Simek @ 2026-05-14 11:49 UTC (permalink / raw)
To: Jens Wiklander, u-boot, Marek Vasut
Cc: Ilias Apalodimas, Andre Przywara, Andrew Goodbody, Anshul Dalal,
Bin Meng, Casey Connolly, Chunfeng Yun, Eddie Cai,
GSS_MTK_Uboot_upstream, Ion Agorria, Junhui Liu, Kongyang Liu,
Lukasz Majewski, Mattijs Korpershoek, Neil Armstrong,
Patrice Chotard, Quentin Schulz, Rasmus Villemoes, Ryder Lee,
Simon Glass, Stephan Gerhold, Svyatoslav Ryhel, Tom Rini,
Varadarajan Narayanan, Weijie Gao, Zixun LI, Jerome Forissier
On 5/7/26 11:27, Jens Wiklander wrote:
> Hi,
>
> This is a follow-up to Jerome's patchset [1], addressing previous feedback
> regarding the monolithic nature of the driver update.
>
> The DWC3 USB driver was forked from the Linux kernel v3.19-rc1 eleven years
> ago by commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel
> to u-boot"). Since then, not many kernel changes have been ported back into
> U-Boot.
>
> This series synchronizes the DWC3 core with Linux v6.16-rc7. To provide
> a clear audit trail and maintain bisectability, I have structured the
> series as follows:
>
> 1. Restore to Baseline: The first commit reverts U-Boot-specific changes
> to drivers/usb/dwc3 to return the directory to a clean v3.19-rc1 state.
> 2. Milestone Imports: A sequence of 50+ commits follows, each performing
> a "snapshot" import of the drivers/usb/dwc3 directory for every major
> kernel version (v3.19 through v6.16-rc7).
> 3. U-Boot Adaptation: The final commits (based on Jerome's original work)
> re-introduce the necessary glue code, XHCI/UDC updates, and build fixes
> required for U-Boot integration.
>
> The final diff is identical to [1]. I decided to stick with that for now to
> focus on the method of how we import or update the code.
>
> Note that this is compile-tested only. The CI pipeline on source.denx.de
> was used as an OK/NOK indicator [2].
>
> The previous patchset was tested on xilinx_zynqmp_kria_defconfig and since
> this diff is identical to the previous, it should still work on that
> platform. With the help of a custom build script [3] and with an additional
> patch [4], I could boot the Kria KV260 board and make it detect a USB SSD
> plugged into one of its USB 3.0 ports. It certainly doesn't mean all
> platforms using the DWC3 driver are still OK, but at least there is some
> hope. If this breaks your platform I'd like to know, and if you can send a
> fix it's even better.
>
> I tried cherry-picking all the 1000+ patches in v3.19-rc1..v6.16-rc7. There
> were a few conflicts, even when backing out the original U-Boot patches on
> top of the original v3.19-rc1 import. However, the resulting state still
> diverged significantly from [1].
>
> Instead, I've imported each new kernel in a separate commit. That way it's
> very clear which kernel patches are included. Since there aren't too many
> patches for each kernel I'm listing the relevant commits in the U-Boot
> commit message for easier reference. I did this with a script so it's easy
> to make changes, if the approach is OK but we need to tune it. With this
> approach it should be easy to tell if a Fixes patch for the kernel might
> also be needed here.
>
> Thanks,
> Jens
>
> [1] https://lore.kernel.org/all/20251121153812.2076440-1-jerome.forissier@linaro.org/
> [2] https://source.denx.de/u-boot/custodians/u-boot-tpm/-/pipelines/30004
> [3] https://source.denx.de/u-boot/u-boot-extras/-/raw/master/contrib/jforissier/zynqmp_kv260_build.sh
> [4] https://gitlab.com/Linaro/trustedsubstrate/meta-ts/-/raw/master/meta-trustedsubstrate/recipes-bsp/u-boot/u-boot/zynqmp-kria-starter/0001-kv260-fix-usb.patch
>
> RFC v1 -> RFC v2:
> - Split the v1 ("usb: dwc3: re-import from kernel v6.16-rc7") into ("usb:
> dwc3: restore to original v3.19-rc1 kernel import"), ("usb: dwc3: import
> from kernel v3.19)..("usb: dwc3: import from kernel v6.16-rc7")
>
> Jens Wiklander (60):
> usb: dwc3: restore to original v3.19-rc1 kernel import
> usb: dwc3: import from kernel v3.19
> usb: dwc3: import from kernel v4.0
> usb: dwc3: import from kernel v4.1
> usb: dwc3: import from kernel v4.2
> usb: dwc3: import from kernel v4.3
> usb: dwc3: import from kernel v4.4
> usb: dwc3: import from kernel v4.5
> usb: dwc3: import from kernel v4.6
> usb: dwc3: import from kernel v4.7
> usb: dwc3: import from kernel v4.8
> usb: dwc3: import from kernel v4.9
> usb: dwc3: import from kernel v4.10
> usb: dwc3: import from kernel v4.11
> usb: dwc3: import from kernel v4.12
> usb: dwc3: import from kernel v4.13
> usb: dwc3: import from kernel v4.14
> usb: dwc3: import from kernel v4.15
> usb: dwc3: import from kernel v4.16
> usb: dwc3: import from kernel v4.17
> usb: dwc3: import from kernel v4.18
> usb: dwc3: import from kernel v4.19
> usb: dwc3: import from kernel v4.20
> usb: dwc3: import from kernel v5.0
> usb: dwc3: import from kernel v5.1
> usb: dwc3: import from kernel v5.2
> usb: dwc3: import from kernel v5.3
> usb: dwc3: import from kernel v5.4
> usb: dwc3: import from kernel v5.5
> usb: dwc3: import from kernel v5.6
> usb: dwc3: import from kernel v5.7
> usb: dwc3: import from kernel v5.8
> usb: dwc3: import from kernel v5.9
> usb: dwc3: import from kernel v5.10
> usb: dwc3: import from kernel v5.11
> usb: dwc3: import from kernel v5.12
> usb: dwc3: import from kernel v5.13
> usb: dwc3: import from kernel v5.14
> usb: dwc3: import from kernel v5.15
> usb: dwc3: import from kernel v5.16
> usb: dwc3: import from kernel v5.17
> usb: dwc3: import from kernel v5.18
> usb: dwc3: import from kernel v5.19
> usb: dwc3: import from kernel v6.0
> usb: dwc3: import from kernel v6.1
> usb: dwc3: import from kernel v6.2
> usb: dwc3: import from kernel v6.3
> usb: dwc3: import from kernel v6.4
> usb: dwc3: import from kernel v6.5
> usb: dwc3: import from kernel v6.6
> usb: dwc3: import from kernel v6.7
> usb: dwc3: import from kernel v6.8
> usb: dwc3: import from kernel v6.9
> usb: dwc3: import from kernel v6.10
> usb: dwc3: import from kernel v6.11
> usb: dwc3: import from kernel v6.12
> usb: dwc3: import from kernel v6.13
> usb: dwc3: import from kernel v6.14
> usb: dwc3: import from kernel v6.15
> usb: dwc3: import from kernel v6.16-rc7
>
> Jerome Forissier (4):
> usb: host: re-import xhci-ext-caps.h from kernel v6.16-rc7
> usb: gadget: re-import epautoconf.c from kernel v6.16-rc7
> usb: udc: re-import udc-core.c from kernel v6.16-rc7
> usb: fix build after resync of DWC3 with kernel v6.16-rc7
>
> drivers/usb/cdns3/ep0.c | 8 +-
> drivers/usb/common/common.c | 23 +
> drivers/usb/dwc3/Makefile | 57 +-
> drivers/usb/dwc3/core.c | 1780 ++++++----
> drivers/usb/dwc3/core.h | 1120 +++++--
> drivers/usb/dwc3/debug.h | 432 +++
> drivers/usb/dwc3/dwc3-omap.c | 2 -
> drivers/usb/dwc3/ep0.c | 737 +++--
> drivers/usb/dwc3/gadget.c | 4152 ++++++++++++++++--------
> drivers/usb/dwc3/gadget.h | 124 +-
> drivers/usb/dwc3/glue.h | 36 +
> drivers/usb/dwc3/io.h | 29 +-
> drivers/usb/dwc3/linux-compat.h | 16 -
> drivers/usb/dwc3/samsung_usb_phy.c | 77 -
> drivers/usb/dwc3/ti_usb_phy.c | 2 -
> drivers/usb/gadget/at91_udc.c | 46 -
> drivers/usb/gadget/atmel_usba_udc.c | 102 +-
> drivers/usb/gadget/ci_udc.c | 419 ---
> drivers/usb/gadget/composite.c | 2 +-
> drivers/usb/gadget/dwc2_udc_otg.c | 174 +-
> drivers/usb/gadget/epautoconf.c | 289 +-
> drivers/usb/gadget/ether.c | 21 +-
> drivers/usb/gadget/f_acm.c | 20 +-
> drivers/usb/gadget/f_fastboot.c | 10 +-
> drivers/usb/gadget/f_mass_storage.c | 5 +-
> drivers/usb/gadget/f_rockusb.c | 11 +-
> drivers/usb/gadget/f_sdp.c | 12 +-
> drivers/usb/gadget/f_thor.c | 19 +-
> drivers/usb/gadget/udc/Makefile | 1 +
> drivers/usb/gadget/udc/udc-core.c | 1096 ++++++-
> drivers/usb/host/xhci-dwc3.c | 4 +-
> drivers/usb/host/xhci-ext-caps.h | 158 +
> drivers/usb/host/xhci-exynos5.c | 2 +-
> drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 +-
> drivers/usb/musb-new/musb_gadget_ep0.c | 24 +-
> drivers/usb/musb-new/musb_uboot.c | 39 -
> include/dm/device_compat.h | 13 +
> include/dm/read.h | 46 +
> include/linux/compat.h | 15 +
> include/linux/usb/ch9.h | 25 +-
> include/linux/usb/gadget.h | 520 +--
> include/linux/usb/otg.h | 10 +
> include/linux/usb/phy.h | 56 +
> 43 files changed, 7659 insertions(+), 4091 deletions(-)
> create mode 100644 drivers/usb/dwc3/debug.h
> create mode 100644 drivers/usb/dwc3/glue.h
> delete mode 100644 drivers/usb/dwc3/linux-compat.h
> delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c
> create mode 100644 drivers/usb/host/xhci-ext-caps.h
>
>
> base-commit: 0f865ab5d68484b4dc1724809de1be06edc85df1
We have run usb tests and we can't see any issue with this series.
We will retest when this approach is approved and rebase on the latest/greatest.
Thanks,
Michal
^ permalink raw reply [flat|nested] 102+ messages in thread
end of thread, other threads:[~2026-05-14 11:50 UTC | newest]
Thread overview: 102+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 9:27 [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 01/64] usb: dwc3: restore to original v3.19-rc1 kernel import Jens Wiklander
2026-05-08 15:40 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 02/64] usb: dwc3: import from kernel v3.19 Jens Wiklander
2026-05-08 15:25 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 03/64] usb: dwc3: import from kernel v4.0 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 04/64] usb: dwc3: import from kernel v4.1 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 05/64] usb: dwc3: import from kernel v4.2 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 06/64] usb: dwc3: import from kernel v4.3 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 07/64] usb: dwc3: import from kernel v4.4 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 08/64] usb: dwc3: import from kernel v4.5 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 09/64] usb: dwc3: import from kernel v4.6 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 10/64] usb: dwc3: import from kernel v4.7 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 11/64] usb: dwc3: import from kernel v4.8 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 12/64] usb: dwc3: import from kernel v4.9 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 13/64] usb: dwc3: import from kernel v4.10 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 14/64] usb: dwc3: import from kernel v4.11 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 15/64] usb: dwc3: import from kernel v4.12 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 16/64] usb: dwc3: import from kernel v4.13 Jens Wiklander
2026-05-08 15:26 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 17/64] usb: dwc3: import from kernel v4.14 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 18/64] usb: dwc3: import from kernel v4.15 Jens Wiklander
2026-05-08 15:27 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 19/64] usb: dwc3: import from kernel v4.16 Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 20/64] usb: dwc3: import from kernel v4.17 Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 21/64] usb: dwc3: import from kernel v4.18 Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 22/64] usb: dwc3: import from kernel v4.19 Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 23/64] usb: dwc3: import from kernel v4.20 Jens Wiklander
2026-05-08 15:28 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 24/64] usb: dwc3: import from kernel v5.0 Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 25/64] usb: dwc3: import from kernel v5.1 Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 26/64] usb: dwc3: import from kernel v5.2 Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 27/64] usb: dwc3: import from kernel v5.3 Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 28/64] usb: dwc3: import from kernel v5.4 Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 29/64] usb: dwc3: import from kernel v5.5 Jens Wiklander
2026-05-08 15:31 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 30/64] usb: dwc3: import from kernel v5.6 Jens Wiklander
2026-05-08 15:36 ` Simon Glass
2026-05-07 9:27 ` [RFC PATCH v2 31/64] usb: dwc3: import from kernel v5.7 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 32/64] usb: dwc3: import from kernel v5.8 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 33/64] usb: dwc3: import from kernel v5.9 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 34/64] usb: dwc3: import from kernel v5.10 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 35/64] usb: dwc3: import from kernel v5.11 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 36/64] usb: dwc3: import from kernel v5.12 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 37/64] usb: dwc3: import from kernel v5.13 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 38/64] usb: dwc3: import from kernel v5.14 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 39/64] usb: dwc3: import from kernel v5.15 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 40/64] usb: dwc3: import from kernel v5.16 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 41/64] usb: dwc3: import from kernel v5.17 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 42/64] usb: dwc3: import from kernel v5.18 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 43/64] usb: dwc3: import from kernel v5.19 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 44/64] usb: dwc3: import from kernel v6.0 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 45/64] usb: dwc3: import from kernel v6.1 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 46/64] usb: dwc3: import from kernel v6.2 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 47/64] usb: dwc3: import from kernel v6.3 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 48/64] usb: dwc3: import from kernel v6.4 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 49/64] usb: dwc3: import from kernel v6.5 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 50/64] usb: dwc3: import from kernel v6.6 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 51/64] usb: dwc3: import from kernel v6.7 Jens Wiklander
2026-05-07 9:27 ` [RFC PATCH v2 52/64] usb: dwc3: import from kernel v6.8 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 53/64] usb: dwc3: import from kernel v6.9 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 54/64] usb: dwc3: import from kernel v6.10 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 55/64] usb: dwc3: import from kernel v6.11 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 56/64] usb: dwc3: import from kernel v6.12 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 57/64] usb: dwc3: import from kernel v6.13 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 58/64] usb: dwc3: import from kernel v6.14 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 59/64] usb: dwc3: import from kernel v6.15 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 60/64] usb: dwc3: import from kernel v6.16-rc7 Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 61/64] usb: host: re-import xhci-ext-caps.h " Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 62/64] usb: gadget: re-import epautoconf.c " Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 63/64] usb: udc: re-import udc-core.c " Jens Wiklander
2026-05-07 9:28 ` [RFC PATCH v2 64/64] usb: fix build after resync of DWC3 with " Jens Wiklander
2026-05-11 8:25 ` Anshul Dalal
2026-05-08 15:42 ` [RFC PATCH v2 00/64] drivers: usb: dwc3: sync code with Linux v6.16-rc7 Simon Glass
2026-05-08 16:03 ` Tom Rini
2026-05-11 13:13 ` Simon Glass
2026-05-11 6:31 ` Michal Simek
2026-05-11 8:27 ` Anshul Dalal
2026-05-11 14:58 ` Tom Rini
2026-05-12 14:52 ` Alexey Charkov
2026-05-14 11:49 ` Michal Simek
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.