[RFC PATCH v2 43/64] usb: dwc3: import from kernel v5.19
Jens Wiklander
jens.wiklander at linaro.org
Thu May 7 11:27:50 CEST 2026
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 at 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 at 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
More information about the U-Boot
mailing list