aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/phy/amlogic/Kconfig12
-rw-r--r--drivers/phy/amlogic/Makefile1
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb3.c283
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c3
-rw-r--r--drivers/usb/cdns3/ep0.c7
-rw-r--r--drivers/usb/cdns3/gadget.c13
-rw-r--r--drivers/usb/dwc2/core.c23
-rw-r--r--drivers/usb/dwc2/core.h4
-rw-r--r--drivers/usb/dwc2/core_intr.c7
-rw-r--r--drivers/usb/dwc2/hw.h1
-rw-r--r--drivers/usb/dwc2/params.c19
-rw-r--r--drivers/usb/dwc2/platform.c39
-rw-r--r--drivers/usb/dwc3/core.c62
-rw-r--r--drivers/usb/dwc3/core.h83
-rw-r--r--drivers/usb/dwc3/debug.h4
-rw-r--r--drivers/usb/dwc3/debugfs.c14
-rw-r--r--drivers/usb/dwc3/drd.c6
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c41
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c420
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c30
-rw-r--r--drivers/usb/dwc3/gadget.c469
-rw-r--r--drivers/usb/dwc3/gadget.h2
-rw-r--r--drivers/usb/dwc3/host.c2
-rw-r--r--drivers/usb/dwc3/io.h2
-rw-r--r--drivers/usb/dwc3/trace.h2
-rw-r--r--drivers/usb/gadget/composite.c78
-rw-r--r--drivers/usb/gadget/configfs.c14
-rw-r--r--drivers/usb/gadget/function/f_acm.c16
-rw-r--r--drivers/usb/gadget/function/f_eem.c2
-rw-r--r--drivers/usb/gadget/function/f_fs.c2
-rw-r--r--drivers/usb/gadget/function/f_serial.c16
-rw-r--r--drivers/usb/gadget/function/f_tcm.c3
-rw-r--r--drivers/usb/gadget/function/f_uvc.h2
-rw-r--r--drivers/usb/gadget/function/rndis.h2
-rw-r--r--drivers/usb/gadget/function/u_audio.h2
-rw-r--r--drivers/usb/gadget/function/u_ecm.h2
-rw-r--r--drivers/usb/gadget/function/u_eem.h2
-rw-r--r--drivers/usb/gadget/function/u_ether.h2
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h2
-rw-r--r--drivers/usb/gadget/function/u_fs.h2
-rw-r--r--drivers/usb/gadget/function/u_gether.h2
-rw-r--r--drivers/usb/gadget/function/u_hid.h2
-rw-r--r--drivers/usb/gadget/function/u_midi.h2
-rw-r--r--drivers/usb/gadget/function/u_ncm.h2
-rw-r--r--drivers/usb/gadget/function/u_phonet.h2
-rw-r--r--drivers/usb/gadget/function/u_printer.h2
-rw-r--r--drivers/usb/gadget/function/u_rndis.h2
-rw-r--r--drivers/usb/gadget/function/u_serial.c57
-rw-r--r--drivers/usb/gadget/function/u_serial.h4
-rw-r--r--drivers/usb/gadget/function/u_tcm.h2
-rw-r--r--drivers/usb/gadget/function/u_uac1.h2
-rw-r--r--drivers/usb/gadget/function/u_uac1_legacy.h2
-rw-r--r--drivers/usb/gadget/function/u_uac2.h2
-rw-r--r--drivers/usb/gadget/function/u_uvc.h2
-rw-r--r--drivers/usb/gadget/function/uvc.h4
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.h2
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c4
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.h2
-rw-r--r--drivers/usb/gadget/function/uvc_video.c76
-rw-r--r--drivers/usb/gadget/function/uvc_video.h4
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c14
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c16
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/hub.c236
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/vhub.h12
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c112
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h12
-rw-r--r--drivers/usb/gadget/udc/core.c2
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c27
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c4
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c11
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c2
-rw-r--r--drivers/usb/gadget/udc/max3420_udc.c2
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c2
-rw-r--r--drivers/usb/gadget/udc/net2272.c2
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c2
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c4
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c140
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c1
-rw-r--r--drivers/usb/gadget/usbstring.c24
79 files changed, 1630 insertions, 868 deletions
diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
index 5ec53874d1ea..617cf073e9aa 100644
--- a/drivers/phy/amlogic/Kconfig
+++ b/drivers/phy/amlogic/Kconfig
@@ -27,18 +27,6 @@ config PHY_MESON_GXL_USB2
GXL and GXM SoCs.
If unsure, say N.
-config PHY_MESON_GXL_USB3
- tristate "Meson GXL and GXM USB3 PHY drivers"
- default ARCH_MESON
- depends on OF && (ARCH_MESON || COMPILE_TEST)
- depends on USB_SUPPORT
- select GENERIC_PHY
- select REGMAP_MMIO
- help
- Enable this to support the Meson USB3 PHY and OTG detection
- IP block found in Meson GXL and GXM SoCs.
- If unsure, say N.
-
config PHY_MESON_G12A_USB2
tristate "Meson G12A USB2 PHY driver"
default ARCH_MESON
diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
index e2baa133f7af..99702a45e9be 100644
--- a/drivers/phy/amlogic/Makefile
+++ b/drivers/phy/amlogic/Makefile
@@ -2,7 +2,6 @@
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o
obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o
-obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o
obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o
obj-$(CONFIG_PHY_MESON_AXG_PCIE) += phy-meson-axg-pcie.o
obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
deleted file mode 100644
index c0e9e4c16149..000000000000
--- a/drivers/phy/amlogic/phy-meson-gxl-usb3.c
+++ /dev/null
@@ -1,283 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Meson GXL USB3 PHY and OTG mode detection driver
- *
- * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- */
-
-#include <linux/bitfield.h>
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/phy/phy.h>
-#include <linux/regmap.h>
-#include <linux/reset.h>
-#include <linux/platform_device.h>
-
-#define USB_R0 0x00
- #define USB_R0_P30_FSEL_MASK GENMASK(5, 0)
- #define USB_R0_P30_PHY_RESET BIT(6)
- #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7)
- #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8)
- #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9)
- #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14)
- #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
- #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
- #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
- #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
- #define USB_R0_U2D_ACT BIT(31)
-
-#define USB_R1 0x04
- #define USB_R1_U3H_BIGENDIAN_GS BIT(0)
- #define USB_R1_U3H_PME_ENABLE BIT(1)
- #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2)
- #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7)
- #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12)
- #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
- #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
- #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
- #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
- #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
-
-#define USB_R2 0x08
- #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0)
- #define USB_R2_P30_CR_READ BIT(16)
- #define USB_R2_P30_CR_WRITE BIT(17)
- #define USB_R2_P30_CR_CAP_ADDR BIT(18)
- #define USB_R2_P30_CR_CAP_DATA BIT(19)
- #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
- #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
-
-#define USB_R3 0x0c
- #define USB_R3_P30_SSC_ENABLE BIT(0)
- #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
- #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
- #define USB_R3_P30_REF_SSP_EN BIT(13)
- #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16)
- #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19)
- #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24)
-
-#define USB_R4 0x10
- #define USB_R4_P21_PORT_RESET_0 BIT(0)
- #define USB_R4_P21_SLEEP_M0 BIT(1)
- #define USB_R4_MEM_PD_MASK GENMASK(3, 2)
- #define USB_R4_P21_ONLY BIT(4)
-
-#define USB_R5 0x14
- #define USB_R5_ID_DIG_SYNC BIT(0)
- #define USB_R5_ID_DIG_REG BIT(1)
- #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
- #define USB_R5_ID_DIG_EN_0 BIT(4)
- #define USB_R5_ID_DIG_EN_1 BIT(5)
- #define USB_R5_ID_DIG_CURR BIT(6)
- #define USB_R5_ID_DIG_IRQ BIT(7)
- #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
- #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
-
-/* read-only register */
-#define USB_R6 0x18
- #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0)
- #define USB_R6_P30_CR_ACK BIT(16)
-
-struct phy_meson_gxl_usb3_priv {
- struct regmap *regmap;
- enum phy_mode mode;
- struct clk *clk_phy;
- struct clk *clk_peripheral;
- struct reset_control *reset;
-};
-
-static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
- .reg_bits = 8,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = USB_R6,
-};
-
-static int phy_meson_gxl_usb3_power_on(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
- USB_R5_ID_DIG_EN_0);
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
- USB_R5_ID_DIG_EN_1);
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
- FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
-
- return 0;
-}
-
-static int phy_meson_gxl_usb3_power_off(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
-
- return 0;
-}
-
-static int phy_meson_gxl_usb3_set_mode(struct phy *phy,
- enum phy_mode mode, int submode)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- switch (mode) {
- case PHY_MODE_USB_HOST:
- regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
- regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
- 0);
- break;
-
- case PHY_MODE_USB_DEVICE:
- regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
- USB_R0_U2D_ACT);
- regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
- USB_R4_P21_SLEEP_M0);
- break;
-
- default:
- dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
- return -EINVAL;
- }
-
- priv->mode = mode;
-
- return 0;
-}
-
-static int phy_meson_gxl_usb3_init(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
- int ret;
-
- ret = reset_control_reset(priv->reset);
- if (ret)
- goto err;
-
- ret = clk_prepare_enable(priv->clk_phy);
- if (ret)
- goto err;
-
- ret = clk_prepare_enable(priv->clk_peripheral);
- if (ret)
- goto err_disable_clk_phy;
-
- ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0);
- if (ret)
- goto err_disable_clk_peripheral;
-
- regmap_update_bits(priv->regmap, USB_R1,
- USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
- FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
-
- return 0;
-
-err_disable_clk_peripheral:
- clk_disable_unprepare(priv->clk_peripheral);
-err_disable_clk_phy:
- clk_disable_unprepare(priv->clk_phy);
-err:
- return ret;
-}
-
-static int phy_meson_gxl_usb3_exit(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- clk_disable_unprepare(priv->clk_peripheral);
- clk_disable_unprepare(priv->clk_phy);
-
- return 0;
-}
-
-static const struct phy_ops phy_meson_gxl_usb3_ops = {
- .power_on = phy_meson_gxl_usb3_power_on,
- .power_off = phy_meson_gxl_usb3_power_off,
- .set_mode = phy_meson_gxl_usb3_set_mode,
- .init = phy_meson_gxl_usb3_init,
- .exit = phy_meson_gxl_usb3_exit,
- .owner = THIS_MODULE,
-};
-
-static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct phy_meson_gxl_usb3_priv *priv;
- struct resource *res;
- struct phy *phy;
- struct phy_provider *phy_provider;
- void __iomem *base;
- int ret;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- priv->regmap = devm_regmap_init_mmio(dev, base,
- &phy_meson_gxl_usb3_regmap_conf);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
- priv->clk_phy = devm_clk_get(dev, "phy");
- if (IS_ERR(priv->clk_phy))
- return PTR_ERR(priv->clk_phy);
-
- priv->clk_peripheral = devm_clk_get(dev, "peripheral");
- if (IS_ERR(priv->clk_peripheral))
- return PTR_ERR(priv->clk_peripheral);
-
- priv->reset = devm_reset_control_array_get_shared(dev);
- if (IS_ERR(priv->reset))
- return PTR_ERR(priv->reset);
-
- /*
- * default to host mode as hardware defaults and/or boot-loader
- * behavior can result in this PHY starting up in device mode. this
- * default and the initialization in phy_meson_gxl_usb3_init ensure
- * that we reproducibly start in a known mode on all devices.
- */
- priv->mode = PHY_MODE_USB_HOST;
-
- phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
- if (IS_ERR(phy)) {
- ret = PTR_ERR(phy);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to create PHY\n");
-
- return ret;
- }
-
- phy_set_drvdata(phy, priv);
-
- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-
- return PTR_ERR_OR_ZERO(phy_provider);
-}
-
-static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
- { .compatible = "amlogic,meson-gxl-usb3-phy", },
- { },
-};
-MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
-
-static struct platform_driver phy_meson_gxl_usb3_driver = {
- .probe = phy_meson_gxl_usb3_probe,
- .driver = {
- .name = "phy-meson-gxl-usb3",
- .of_match_table = phy_meson_gxl_usb3_of_match,
- },
-};
-module_platform_driver(phy_meson_gxl_usb3_driver);
-
-MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 5685ba11480b..e701ab56b0a7 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -138,7 +138,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
error = pm_runtime_get_sync(dev);
if (error < 0) {
dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
- goto err_get;
+ goto err;
}
/* assert RESET */
@@ -185,7 +185,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
err:
pm_runtime_put_sync(data->dev);
-err_get:
pm_runtime_disable(data->dev);
return error;
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
index e71240b386b4..82645a2a0f52 100644
--- a/drivers/usb/cdns3/ep0.c
+++ b/drivers/usb/cdns3/ep0.c
@@ -332,13 +332,6 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
- cdns3_ep0_complete_setup(priv_dev, 0, 1);
- /**
- * Little delay to give the controller some time
- * for sending status stage.
- * This time should be less then 3ms.
- */
- mdelay(1);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index 559e5c01c2b0..5e24c2e57c0d 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -512,8 +512,8 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
}
static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep,
- struct cdns3_request *priv_req)
+ struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
{
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
priv_req->flags & REQUEST_INTERNAL) {
@@ -552,8 +552,8 @@ static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_d
}
static int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep,
- struct cdns3_request *priv_req)
+ struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
{
int deferred = 0;
@@ -1905,7 +1905,7 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
}
static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
+ struct cdns3_endpoint *priv_ep)
{
if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER)
return;
@@ -1926,7 +1926,7 @@ static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
}
static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
+ struct cdns3_endpoint *priv_ep)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
@@ -3069,6 +3069,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
priv_dev->gadget.name = "usb-ss-gadget";
priv_dev->gadget.sg_supported = 1;
priv_dev->gadget.quirk_avoids_skb_reserve = 1;
+ priv_dev->gadget.irq = cdns->dev_irq;
spin_lock_init(&priv_dev->lock);
INIT_WORK(&priv_dev->pending_status_wq,
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 78a4925aa118..fec17a2d2447 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -524,10 +524,25 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
greset |= GRSTCTL_CSFTRST;
dwc2_writel(hsotg, greset, GRSTCTL);
- if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) {
- dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
- __func__);
- return -EBUSY;
+ if ((hsotg->hw_params.snpsid & DWC2_CORE_REV_MASK) <
+ (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL,
+ GRSTCTL_CSFTRST, 10000)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST\n",
+ __func__);
+ return -EBUSY;
+ }
+ } else {
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
+ GRSTCTL_CSFTRST_DONE, 10000)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST_DONE\n",
+ __func__);
+ return -EBUSY;
+ }
+ greset = dwc2_readl(hsotg, GRSTCTL);
+ greset &= ~GRSTCTL_CSFTRST;
+ greset |= GRSTCTL_CSFTRST_DONE;
+ dwc2_writel(hsotg, greset, GRSTCTL);
}
/* Wait for AHB master IDLE state */
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 668d1ad646a4..132d687f1590 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1103,8 +1103,10 @@ struct dwc2_hsotg {
#define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
#define DWC2_CORE_REV_4_00a 0x4f54400a
+#define DWC2_CORE_REV_4_20a 0x4f54420a
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
+#define DWC2_CORE_REV_MASK 0x0000ffff
/* DWC OTG HW Core ID */
#define DWC2_OTG_ID 0x4f540000
@@ -1309,6 +1311,8 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg);
+
/*
* Common core Functions.
* The following functions support managing the DWC_otg controller in either
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 876ff31261d5..55f1d14fc414 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -416,10 +416,13 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev, "exit power_down failed\n");
+ /* Change to L0 state */
+ hsotg->lx_state = DWC2_L0;
call_gadget(hsotg, resume);
+ } else {
+ /* Change to L0 state */
+ hsotg->lx_state = DWC2_L0;
}
- /* Change to L0 state */
- hsotg->lx_state = DWC2_L0;
} else {
if (hsotg->params.power_down)
return;
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 864b76a0b954..c3d6dde2aca4 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -126,6 +126,7 @@
#define GRSTCTL HSOTG_REG(0x010)
#define GRSTCTL_AHBIDLE BIT(31)
#define GRSTCTL_DMAREQ BIT(30)
+#define GRSTCTL_CSFTRST_DONE BIT(29)
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
#define GRSTCTL_TXFNUM_SHIFT 6
#define GRSTCTL_TXFNUM_LIMIT 0x1f
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 8ccc83f7eb3f..ce736d67c7c3 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -782,25 +782,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
u32 grxfsiz;
- /*
- * Attempt to ensure this device is really a DWC_otg Controller.
- * Read and verify the GSNPSID register contents. The value should be
- * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
- */
-
- hw->snpsid = dwc2_readl(hsotg, GSNPSID);
- if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
- (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
- (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
- dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
- hw->snpsid);
- return -ENODEV;
- }
-
- dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
- hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
- hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
-
hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
hwcfg3 = dwc2_readl(hsotg, GHWCFG3);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 69972750e161..e571c8ae65ec 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -363,6 +363,37 @@ static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
}
/**
+ * Check core version
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ */
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+
+ /*
+ * Attempt to ensure this device is really a DWC_otg Controller.
+ * Read and verify the GSNPSID register contents. The value should be
+ * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
+ */
+
+ hw->snpsid = dwc2_readl(hsotg, GSNPSID);
+ if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
+ dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
+ hw->snpsid);
+ return -ENODEV;
+ }
+
+ dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
+ hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
+ hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
+ return 0;
+}
+
+/**
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
* driver
*
@@ -445,6 +476,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
"snps,need-phy-for-wake");
/*
+ * Before performing any core related operations
+ * check core version.
+ */
+ retval = dwc2_check_core_version(hsotg);
+ if (retval)
+ goto error;
+
+ /*
* Reset before dwc2_get_hwparams() then it could get power-on real
* reset value form registers.
*/
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/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 4fe8b1e1485c..6d9de334e46a 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -635,13 +635,18 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
+ int mdwidth;
u32 val;
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
/* Convert to bytes */
- val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+ val *= mdwidth;
val >>= 3;
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -654,13 +659,18 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
+ int mdwidth;
u32 val;
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
/* Convert to bytes */
- val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+ val *= mdwidth;
val >>= 3;
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 7db1ffc92bbd..2e483448d695 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -56,7 +56,7 @@ static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
spin_lock(&dwc->lock);
if (dwc->otg_restart_host) {
dwc3_otg_host_init(dwc);
- dwc->otg_restart_host = 0;
+ dwc->otg_restart_host = false;
}
spin_unlock(&dwc->lock);
@@ -82,7 +82,7 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
!(reg & DWC3_OEVT_DEVICEMODE))
- dwc->otg_restart_host = 1;
+ dwc->otg_restart_host = true;
dwc3_writel(dwc->regs, DWC3_OEVT, reg);
ret = IRQ_WAKE_THREAD;
}
@@ -653,6 +653,6 @@ void dwc3_drd_exit(struct dwc3 *dwc)
break;
}
- if (!dwc->edev)
+ if (dwc->otg_irq)
free_irq(dwc->otg_irq, dwc);
}
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index 1e14a6f4884b..6505f7bd69e2 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -14,6 +14,7 @@
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
/* USBSS register offsets */
@@ -34,6 +35,7 @@
struct dwc3_keystone {
struct device *dev;
void __iomem *usbss;
+ struct phy *usb3_phy;
};
static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
@@ -95,8 +97,38 @@ static int kdwc3_probe(struct platform_device *pdev)
if (IS_ERR(kdwc->usbss))
return PTR_ERR(kdwc->usbss);
- pm_runtime_enable(kdwc->dev);
+ /* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
+ kdwc->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
+ if (IS_ERR(kdwc->usb3_phy)) {
+ error = PTR_ERR(kdwc->usb3_phy);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "couldn't get usb3 phy: %d\n", error);
+
+ return error;
+ }
+
+ phy_pm_runtime_get_sync(kdwc->usb3_phy);
+ error = phy_reset(kdwc->usb3_phy);
+ if (error < 0) {
+ dev_err(dev, "usb3 phy reset failed: %d\n", error);
+ return error;
+ }
+
+ error = phy_init(kdwc->usb3_phy);
+ if (error < 0) {
+ dev_err(dev, "usb3 phy init failed: %d\n", error);
+ return error;
+ }
+
+ error = phy_power_on(kdwc->usb3_phy);
+ if (error < 0) {
+ dev_err(dev, "usb3 phy power on failed: %d\n", error);
+ phy_exit(kdwc->usb3_phy);
+ return error;
+ }
+
+ pm_runtime_enable(kdwc->dev);
error = pm_runtime_get_sync(kdwc->dev);
if (error < 0) {
dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
@@ -138,6 +170,9 @@ err_core:
err_irq:
pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
+ phy_power_off(kdwc->usb3_phy);
+ phy_exit(kdwc->usb3_phy);
+ phy_pm_runtime_put_sync(kdwc->usb3_phy);
return error;
}
@@ -163,6 +198,10 @@ static int kdwc3_remove(struct platform_device *pdev)
pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
+ phy_power_off(kdwc->usb3_phy);
+ phy_exit(kdwc->usb3_phy);
+ phy_pm_runtime_put_sync(kdwc->usb3_phy);
+
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index b81d085bc534..bd744e82cad4 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -30,7 +30,7 @@
#include <linux/usb/role.h>
#include <linux/regulator/consumer.h>
-/* USB2 Ports Control Registers */
+/* USB2 Ports Control Registers, offsets are per-port */
#define U2P_REG_SIZE 0x20
@@ -50,14 +50,16 @@
/* USB Glue Control Registers */
-#define USB_R0 0x80
+#define G12A_GLUE_OFFSET 0x80
+
+#define USB_R0 0x00
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
#define USB_R0_U2D_ACT BIT(31)
-#define USB_R1 0x84
+#define USB_R1 0x04
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
#define USB_R1_U3H_PME_ENABLE BIT(1)
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
@@ -69,23 +71,23 @@
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
-#define USB_R2 0x88
+#define USB_R2 0x08
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
-#define USB_R3 0x8c
+#define USB_R3 0x0c
#define USB_R3_P30_SSC_ENABLE BIT(0)
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
#define USB_R3_P30_REF_SSP_EN BIT(13)
-#define USB_R4 0x90
+#define USB_R4 0x10
#define USB_R4_P21_PORT_RESET_0 BIT(0)
#define USB_R4_P21_SLEEP_M0 BIT(1)
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
#define USB_R4_P21_ONLY BIT(4)
-#define USB_R5 0x94
+#define USB_R5 0x14
#define USB_R5_ID_DIG_SYNC BIT(0)
#define USB_R5_ID_DIG_REG BIT(1)
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
@@ -96,15 +98,12 @@
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
-enum {
- USB2_HOST_PHY = 0,
- USB2_OTG_PHY,
- USB3_HOST_PHY,
- PHY_COUNT,
-};
+#define PHY_COUNT 3
+#define USB2_OTG_PHY 1
-static const char *phy_names[PHY_COUNT] = {
- "usb2-phy0", "usb2-phy1", "usb3-phy0",
+static struct clk_bulk_data meson_gxl_clocks[] = {
+ { .id = "usb_ctrl" },
+ { .id = "ddr" },
};
static struct clk_bulk_data meson_g12a_clocks[] = {
@@ -117,27 +116,133 @@ static struct clk_bulk_data meson_a1_clocks[] = {
{ .id = "xtal_usb_ctrl" },
};
+static const char *meson_gxm_phy_names[] = {
+ "usb2-phy0", "usb2-phy1", "usb2-phy2",
+};
+
+static const char *meson_g12a_phy_names[] = {
+ "usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+/*
+ * Amlogic A1 has a single physical PHY, in slot 1, but still has the
+ * two U2 PHY controls register blocks like G12A.
+ * Handling the first PHY on slot 1 would need a large amount of code
+ * changes, and the current management is generic enough to handle it
+ * correctly when only the "usb2-phy1" phy is specified on-par with the
+ * DT bindings.
+ */
+static const char *meson_a1_phy_names[] = {
+ "usb2-phy0", "usb2-phy1"
+};
+
+struct dwc3_meson_g12a;
+
struct dwc3_meson_g12a_drvdata {
bool otg_switch_supported;
+ bool otg_phy_host_port_disable;
struct clk_bulk_data *clks;
int num_clks;
+ const char **phy_names;
+ int num_phys;
+ int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
+ int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+ int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+ int (*usb_init)(struct dwc3_meson_g12a *priv);
+ int (*usb_post_init)(struct dwc3_meson_g12a *priv);
+};
+
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base);
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base);
+
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode);
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode);
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
+
+/*
+ * For GXL and GXM SoCs:
+ * USB Phy muxing between the DWC2 Device controller and the DWC3 Host
+ * controller is buggy when switching from Device to Host when USB port
+ * is unpopulated, it causes the DWC3 to hard crash.
+ * When populated (including OTG switching with ID pin), the switch works
+ * like a charm like on the G12A platforms.
+ * In order to still switch from Host to Device on an USB Type-A port,
+ * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
+ * controller from the port, but when used the DWC3 controller must be
+ * reset to recover usage of the port.
+ */
+
+static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
+ .otg_switch_supported = true,
+ .otg_phy_host_port_disable = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_gxl_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
+ .otg_switch_supported = true,
+ .otg_phy_host_port_disable = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_gxm_phy_names,
+ .num_phys = ARRAY_SIZE(meson_gxm_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_gxl_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.otg_switch_supported = true,
.clks = meson_g12a_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_g12a_phy_names,
+ .num_phys = ARRAY_SIZE(meson_g12a_phy_names),
+ .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
};
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
.otg_switch_supported = false,
.clks = meson_a1_clocks,
.num_clks = ARRAY_SIZE(meson_a1_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
};
struct dwc3_meson_g12a {
struct device *dev;
- struct regmap *regmap;
+ struct regmap *u2p_regmap[PHY_COUNT];
+ struct regmap *usb_glue_regmap;
struct reset_control *reset;
struct phy *phys[PHY_COUNT];
enum usb_dr_mode otg_mode;
@@ -150,49 +255,78 @@ struct dwc3_meson_g12a {
const struct dwc3_meson_g12a_drvdata *drvdata;
};
-static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
- int i, enum phy_mode mode)
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode)
+{
+ return phy_set_mode(priv->phys[i], mode);
+}
+
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode)
+{
+ /* On GXL PHY must be started in device mode for DWC2 init */
+ return priv->drvdata->set_phy_mode(priv, i,
+ (i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
+ : PHY_MODE_USB_HOST);
+}
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode)
{
if (mode == PHY_MODE_USB_HOST)
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE,
U2P_R0_HOST_DEVICE);
else
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE, 0);
+
+ return 0;
}
-static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode)
{
- int i;
+ int ret;
- if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
- priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
- else
- priv->otg_phy_mode = PHY_MODE_USB_HOST;
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_POWER_ON_RESET,
+ U2P_R0_POWER_ON_RESET);
- for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
- if (!priv->phys[i])
- continue;
+ if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+ U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+
+ ret = priv->drvdata->set_phy_mode(priv, i, mode);
+ } else
+ ret = priv->drvdata->set_phy_mode(priv, i,
+ PHY_MODE_USB_HOST);
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_POWER_ON_RESET,
- U2P_R0_POWER_ON_RESET);
+ if (ret)
+ return ret;
- if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
- regmap_update_bits(priv->regmap,
- U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
- U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_POWER_ON_RESET, 0);
- dwc3_meson_g12a_usb2_set_mode(priv, i,
- priv->otg_phy_mode);
- } else
- dwc3_meson_g12a_usb2_set_mode(priv, i,
- PHY_MODE_USB_HOST);
+ return 0;
+}
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_POWER_ON_RESET, 0);
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
+{
+ int i, ret;
+
+ for (i = 0; i < priv->drvdata->num_phys; ++i) {
+ if (!priv->phys[i])
+ continue;
+
+ if (!strstr(priv->drvdata->phy_names[i], "usb2"))
+ continue;
+
+ ret = priv->drvdata->usb2_init_phy(priv, i, mode);
+ if (ret)
+ return ret;
}
return 0;
@@ -200,7 +334,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
{
- regmap_update_bits(priv->regmap, USB_R3,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R3,
USB_R3_P30_SSC_RANGE_MASK |
USB_R3_P30_REF_SSP_EN,
USB_R3_P30_SSC_ENABLE |
@@ -208,61 +342,77 @@ static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
USB_R3_P30_REF_SSP_EN);
udelay(2);
- regmap_update_bits(priv->regmap, USB_R2,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
- regmap_update_bits(priv->regmap, USB_R2,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
udelay(2);
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
}
-static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
+static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
{
- if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
- regmap_update_bits(priv->regmap, USB_R0,
+ if (mode == PHY_MODE_USB_DEVICE) {
+ if (priv->otg_mode != USB_DR_MODE_OTG &&
+ priv->drvdata->otg_phy_host_port_disable)
+ /* Isolate the OTG PHY port from the Host Controller */
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+ USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+ FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+ BIT(USB2_OTG_PHY)));
+
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
- regmap_update_bits(priv->regmap, USB_R0,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
- regmap_update_bits(priv->regmap, USB_R4,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
} else {
- regmap_update_bits(priv->regmap, USB_R0,
+ if (priv->otg_mode != USB_DR_MODE_OTG &&
+ priv->drvdata->otg_phy_host_port_disable) {
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+ USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
+ msleep(500);
+ }
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, 0);
- regmap_update_bits(priv->regmap, USB_R4,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, 0);
}
}
-static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
{
int ret;
- ret = dwc3_meson_g12a_usb2_init(priv);
+ ret = dwc3_meson_g12a_usb2_init(priv, mode);
if (ret)
return ret;
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_0,
USB_R5_ID_DIG_EN_0);
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_1,
USB_R5_ID_DIG_EN_1);
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_TH_MASK,
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
@@ -270,12 +420,13 @@ static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
if (priv->usb3_ports)
dwc3_meson_g12a_usb3_init(priv);
- dwc3_meson_g12a_usb_otg_apply_mode(priv);
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
-static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
+static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
+ .name = "usb-glue",
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
@@ -284,17 +435,19 @@ static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
{
+ const char *phy_name;
int i;
- for (i = 0 ; i < PHY_COUNT ; ++i) {
- priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
+ for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
+ phy_name = priv->drvdata->phy_names[i];
+ priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
if (!priv->phys[i])
continue;
if (IS_ERR(priv->phys[i]))
return PTR_ERR(priv->phys[i]);
- if (i == USB3_HOST_PHY)
+ if (strstr(phy_name, "usb3"))
priv->usb3_ports++;
else
priv->usb2_ports++;
@@ -310,7 +463,7 @@ static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
{
u32 reg;
- regmap_read(priv->regmap, USB_R5, &reg);
+ regmap_read(priv->usb_glue_regmap, USB_R5, &reg);
if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
return PHY_MODE_USB_DEVICE;
@@ -342,9 +495,11 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
priv->otg_phy_mode = mode;
- dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+ ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
+ if (ret)
+ return ret;
- dwc3_meson_g12a_usb_otg_apply_mode(priv);
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
@@ -364,6 +519,13 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
if (mode == priv->otg_phy_mode)
return 0;
+ if (priv->drvdata->otg_phy_host_port_disable)
+ dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
+ "SoC, when manual switching from "\
+ "Host to device, DWC3 controller "\
+ "will need to be resetted in order "\
+ "to recover usage of the Host port");
+
return dwc3_meson_g12a_otg_mode_set(priv, mode);
}
@@ -386,7 +548,8 @@ static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
dev_warn(priv->dev, "Failed to switch OTG mode\n");
}
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
+ USB_R5_ID_DIG_IRQ, 0);
return IRQ_HANDLED;
}
@@ -421,7 +584,7 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
@@ -457,6 +620,77 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
return 0;
}
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base)
+{
+ /* GXL controls the PHY mode in the PHY registers unlike G12A */
+ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
+ &phy_meson_g12a_usb_glue_regmap_conf);
+ if (IS_ERR(priv->usb_glue_regmap))
+ return PTR_ERR(priv->usb_glue_regmap);
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base)
+{
+ int i;
+
+ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
+ base + G12A_GLUE_OFFSET,
+ &phy_meson_g12a_usb_glue_regmap_conf);
+ if (IS_ERR(priv->usb_glue_regmap))
+ return PTR_ERR(priv->usb_glue_regmap);
+
+ /* Create a regmap for each USB2 PHY control register set */
+ for (i = 0; i < priv->usb2_ports; i++) {
+ struct regmap_config u2p_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = U2P_R1,
+ };
+
+ u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
+ "u2p-%d", i);
+ if (!u2p_regmap_config.name)
+ return -ENOMEM;
+
+ priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
+ base + (i * U2P_REG_SIZE),
+ &u2p_regmap_config);
+ if (IS_ERR(priv->u2p_regmap[i]))
+ return PTR_ERR(priv->u2p_regmap[i]);
+ }
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+ return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
+}
+
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
+{
+ return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
+}
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
+{
+ int ret;
+
+ ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
+ priv->otg_phy_mode);
+ if (ret)
+ return ret;
+
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
+
+ return 0;
+}
+
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
{
struct dwc3_meson_g12a *priv;
@@ -473,10 +707,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- priv->regmap = devm_regmap_init_mmio(dev, base,
- &phy_meson_g12a_usb3_regmap_conf);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
+ priv->drvdata = of_device_get_match_data(&pdev->dev);
+
+ priv->dev = dev;
+ ret = priv->drvdata->setup_regmaps(priv, base);
+ if (ret)
+ return ret;
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
@@ -485,8 +721,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
priv->vbus = NULL;
}
- priv->drvdata = of_device_get_match_data(&pdev->dev);
-
ret = devm_clk_bulk_get(dev,
priv->drvdata->num_clks,
priv->drvdata->clks);
@@ -499,9 +733,8 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
return ret;
platform_set_drvdata(pdev, priv);
- priv->dev = dev;
- priv->reset = devm_reset_control_get(dev, NULL);
+ priv->reset = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->reset)) {
ret = PTR_ERR(priv->reset);
dev_err(dev, "failed to get device reset, err=%d\n", ret);
@@ -525,7 +758,14 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
/* Get dr_mode */
priv->otg_mode = usb_get_dr_mode(dev);
- dwc3_meson_g12a_usb_init(priv);
+ if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+ priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
+ else
+ priv->otg_phy_mode = PHY_MODE_USB_HOST;
+
+ ret = priv->drvdata->usb_init(priv);
+ if (ret)
+ goto err_disable_clks;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -541,6 +781,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
goto err_phys_exit;
}
+ if (priv->drvdata->usb_post_init) {
+ ret = priv->drvdata->usb_post_init(priv);
+ if (ret)
+ goto err_phys_power;
+ }
+
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_phys_power;
@@ -642,7 +888,9 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
reset_control_deassert(priv->reset);
- dwc3_meson_g12a_usb_init(priv);
+ ret = priv->drvdata->usb_init(priv);
+ if (ret)
+ return ret;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -675,6 +923,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
static const struct of_device_id dwc3_meson_g12a_match[] = {
{
+ .compatible = "amlogic,meson-gxl-usb-ctrl",
+ .data = &gxl_drvdata,
+ },
+ {
+ .compatible = "amlogic,meson-gxm-usb-ctrl",
+ .data = &gxm_drvdata,
+ },
+ {
.compatible = "amlogic,meson-g12a-usb-ctrl",
.data = &g12a_drvdata,
},
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index e64754be47b4..8852fbfdead4 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -27,7 +27,6 @@ struct dwc3_of_simple {
struct clk_bulk_data *clks;
int num_clocks;
struct reset_control *resets;
- bool pulse_resets;
bool need_reset;
};
@@ -38,7 +37,6 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
int ret;
- bool shared_resets = false;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
@@ -54,13 +52,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
simple->need_reset = true;
- if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
- of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
- shared_resets = true;
- simple->pulse_resets = true;
- }
-
- simple->resets = of_reset_control_array_get(np, shared_resets, true,
+ simple->resets = of_reset_control_array_get(np, false, true,
true);
if (IS_ERR(simple->resets)) {
ret = PTR_ERR(simple->resets);
@@ -68,15 +60,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
return ret;
}
- if (simple->pulse_resets) {
- ret = reset_control_reset(simple->resets);
- if (ret)
- goto err_resetc_put;
- } else {
- ret = reset_control_deassert(simple->resets);
- if (ret)
- goto err_resetc_put;
- }
+ ret = reset_control_deassert(simple->resets);
+ if (ret)
+ goto err_resetc_put;
ret = clk_bulk_get_all(simple->dev, &simple->clks);
if (ret < 0)
@@ -102,8 +88,7 @@ err_clk_put:
clk_bulk_put_all(simple->num_clocks, simple->clks);
err_resetc_assert:
- if (!simple->pulse_resets)
- reset_control_assert(simple->resets);
+ reset_control_assert(simple->resets);
err_resetc_put:
reset_control_put(simple->resets);
@@ -118,8 +103,7 @@ static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple)
clk_bulk_put_all(simple->num_clocks, simple->clks);
simple->num_clocks = 0;
- if (!simple->pulse_resets)
- reset_control_assert(simple->resets);
+ reset_control_assert(simple->resets);
reset_control_put(simple->resets);
@@ -191,8 +175,6 @@ static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
- { .compatible = "amlogic,meson-axg-dwc3" },
- { .compatible = "amlogic,meson-gxl-dwc3" },
{ .compatible = "allwinner,sun50i-h6-dwc3" },
{ /* Sentinel */ }
};
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, &params);
}
+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, &params);
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, &params);
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(&params, 0, sizeof(params));
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+ 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/host.c b/drivers/usb/dwc3/host.c
index 86dbd012b984..bef1c1ac2067 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -104,7 +104,7 @@ int dwc3_host_init(struct dwc3 *dwc)
*
* This following flag tells XHCI to do just that.
*/
- if (dwc->revision <= DWC3_REVISION_300A)
+ if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
if (prop_idx) {
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
*
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 3054b89512ff..4c4fc6c41d9b 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* trace.h - DesignWare USB3 DRD Controller Trace Support
*
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index cb4950cf1cdc..5c1eb96a5c57 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -96,40 +96,43 @@ function_descriptors(struct usb_function *f,
}
/**
- * next_ep_desc() - advance to the next EP descriptor
+ * next_desc() - advance to the next desc_type descriptor
* @t: currect pointer within descriptor array
+ * @desc_type: descriptor type
*
- * Return: next EP descriptor or NULL
+ * Return: next desc_type descriptor or NULL
*
- * Iterate over @t until either EP descriptor found or
+ * Iterate over @t until either desc_type descriptor found or
* NULL (that indicates end of list) encountered
*/
static struct usb_descriptor_header**
-next_ep_desc(struct usb_descriptor_header **t)
+next_desc(struct usb_descriptor_header **t, u8 desc_type)
{
for (; *t; t++) {
- if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
+ if ((*t)->bDescriptorType == desc_type)
return t;
}
return NULL;
}
/*
- * for_each_ep_desc()- iterate over endpoint descriptors in the
- * descriptors list
- * @start: pointer within descriptor array.
- * @ep_desc: endpoint descriptor to use as the loop cursor
+ * for_each_desc() - iterate over desc_type descriptors in the
+ * descriptors list
+ * @start: pointer within descriptor array.
+ * @iter_desc: desc_type descriptor to use as the loop cursor
+ * @desc_type: wanted descriptr type
*/
-#define for_each_ep_desc(start, ep_desc) \
- for (ep_desc = next_ep_desc(start); \
- ep_desc; ep_desc = next_ep_desc(ep_desc+1))
+#define for_each_desc(start, iter_desc, desc_type) \
+ for (iter_desc = next_desc(start, desc_type); \
+ iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type))
/**
- * config_ep_by_speed() - configures the given endpoint
+ * config_ep_by_speed_and_alt() - configures the given endpoint
* according to gadget speed.
* @g: pointer to the gadget
* @f: usb function
* @_ep: the endpoint to configure
+ * @alt: alternate setting number
*
* Return: error code, 0 on success
*
@@ -142,11 +145,13 @@ next_ep_desc(struct usb_descriptor_header **t)
* Note: the supplied function should hold all the descriptors
* for supported speeds
*/
-int config_ep_by_speed(struct usb_gadget *g,
- struct usb_function *f,
- struct usb_ep *_ep)
+int config_ep_by_speed_and_alt(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep,
+ u8 alt)
{
struct usb_endpoint_descriptor *chosen_desc = NULL;
+ struct usb_interface_descriptor *int_desc = NULL;
struct usb_descriptor_header **speed_desc = NULL;
struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
@@ -182,8 +187,21 @@ int config_ep_by_speed(struct usb_gadget *g,
default:
speed_desc = f->fs_descriptors;
}
+
+ /* find correct alternate setting descriptor */
+ for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) {
+ int_desc = (struct usb_interface_descriptor *)*d_spd;
+
+ if (int_desc->bAlternateSetting == alt) {
+ speed_desc = d_spd;
+ goto intf_found;
+ }
+ }
+ return -EIO;
+
+intf_found:
/* find descriptors */
- for_each_ep_desc(speed_desc, d_spd) {
+ for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) {
chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
if (chosen_desc->bEndpointAddress == _ep->address)
goto ep_found;
@@ -237,6 +255,32 @@ ep_found:
}
return 0;
}
+EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt);
+
+/**
+ * config_ep_by_speed() - configures the given endpoint
+ * according to gadget speed.
+ * @g: pointer to the gadget
+ * @f: usb function
+ * @_ep: the endpoint to configure
+ *
+ * Return: error code, 0 on success
+ *
+ * This function chooses the right descriptors for a given
+ * endpoint according to gadget speed and saves it in the
+ * endpoint desc field. If the endpoint already has a descriptor
+ * assigned to it - overwrites it with currently corresponding
+ * descriptor. The endpoint maxpacket field is updated according
+ * to the chosen descriptor.
+ * Note: the supplied function should hold all the descriptors
+ * for supported speeds
+ */
+int config_ep_by_speed(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep)
+{
+ return config_ep_by_speed_and_alt(g, f, _ep, 0);
+}
EXPORT_SYMBOL_GPL(config_ep_by_speed);
/**
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 6a9aa4413d64..9dc06a4e1b30 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -13,8 +13,6 @@
int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
{
- unsigned primary_lang;
- unsigned sub_lang;
u16 num;
int ret;
@@ -22,17 +20,7 @@ int check_user_usb_string(const char *name,
if (ret)
return ret;
- primary_lang = num & 0x3ff;
- sub_lang = num >> 10;
-
- /* simple sanity check for valid langid */
- switch (primary_lang) {
- case 0:
- case 0x62 ... 0xfe:
- case 0x100 ... 0x3ff:
- return -EINVAL;
- }
- if (!sub_lang)
+ if (!usb_validate_langid(num))
return -EINVAL;
stringtab_dev->language = num;
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 7c152c28b26c..200596ea9557 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -723,6 +723,20 @@ static void acm_free_func(struct usb_function *f)
kfree(acm);
}
+static void acm_resume(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+
+ gserial_resume(&acm->port);
+}
+
+static void acm_suspend(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+
+ gserial_suspend(&acm->port);
+}
+
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
@@ -750,6 +764,8 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
acm->port_num = opts->port_num;
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
+ acm->port.func.resume = acm_resume;
+ acm->port.func.suspend = acm_suspend;
return &acm->port.func;
}
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index b81a91d504bd..cfcc4e81fb77 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -291,8 +291,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
eem->port.out_ep = ep;
- status = -ENOMEM;
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 10f01f974f67..494f853f2206 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2508,7 +2508,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
- };
+ }
/* Read descriptors */
raw_descs = data;
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 1406255d0865..e62713846350 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -348,6 +348,20 @@ static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
+static void gser_resume(struct usb_function *f)
+{
+ struct f_gser *gser = func_to_gser(f);
+
+ gserial_resume(&gser->port);
+}
+
+static void gser_suspend(struct usb_function *f)
+{
+ struct f_gser *gser = func_to_gser(f);
+
+ gserial_suspend(&gser->port);
+}
+
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
@@ -369,6 +383,8 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
+ gser->port.func.resume = gser_resume;
+ gser->port.func.suspend = gser_suspend;
return &gser->port.func;
}
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 36504931b2d1..2979cbe4d95f 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -531,6 +531,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd)
stream->req_in->sg = se_cmd->t_data_sg;
}
+ stream->req_in->is_last = 1;
stream->req_in->complete = uasp_status_data_cmpl;
stream->req_in->length = se_cmd->data_length;
stream->req_in->context = cmd;
@@ -554,6 +555,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)
*/
iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
iu->status = se_cmd->scsi_status;
+ stream->req_status->is_last = 1;
stream->req_status->context = cmd;
stream->req_status->length = se_cmd->scsi_sense_length + 16;
stream->req_status->buf = iu;
@@ -991,6 +993,7 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
req->sg = se_cmd->t_data_sg;
}
+ req->is_last = 1;
req->complete = usbg_data_write_cmpl;
req->length = se_cmd->data_length;
req->context = cmd;
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index a81a17765558..1db972d4beeb 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* f_uvc.h -- USB Video Class Gadget driver
*
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index c7e3a70ce6c1..f6167f7fea82 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* RNDIS Definitions for Remote NDIS
*
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 81d3d4ed6dfb..5ea6b86f1fda 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_audio.h -- interface to USB gadget "ALSA sound card" utilities
*
diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
index 098ece573a5e..77cfb89932be 100644
--- a/drivers/usb/gadget/function/u_ecm.h
+++ b/drivers/usb/gadget/function/u_ecm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ecm.h
*
diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h
index 921386a375cf..3bd85dfcd71c 100644
--- a/drivers/usb/gadget/function/u_eem.h
+++ b/drivers/usb/gadget/function/u_eem.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_eem.h
*
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 332307d54292..10dd640684e2 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_ether.h -- interface to USB gadget "ethernet link" utilities
*
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index d8b92485b727..bd92b5703013 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ether_configfs.h
*
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index f9b0cf67360d..f102ec23f3af 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_fs.h
*
diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h
index ce4f07626f96..2f7a373ed449 100644
--- a/drivers/usb/gadget/function/u_gether.h
+++ b/drivers/usb/gadget/function/u_gether.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_gether.h
*
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
index 1594bfa312eb..84e6da302499 100644
--- a/drivers/usb/gadget/function/u_hid.h
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_hid.h
*
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index 29bf006c0a13..f6e14af7f566 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_midi.h
*
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 70da3201a1d0..5408854d8407 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ncm.h
*
diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
index 12fb613f85d1..c53233b37192 100644
--- a/drivers/usb/gadget/function/u_phonet.h
+++ b/drivers/usb/gadget/function/u_phonet.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_phonet.h - interface to Phonet
*
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h
index 78797764f478..318205fb778e 100644
--- a/drivers/usb/gadget/function/u_printer.h
+++ b/drivers/usb/gadget/function/u_printer.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_printer.h
*
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index 1e148b76f339..a8c409b2f52f 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_rndis.h
*
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 8167d379e115..3cfc6e2eba71 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -120,6 +120,8 @@ struct gs_port {
wait_queue_head_t drain_wait; /* wait while writes drain */
bool write_busy;
wait_queue_head_t close_wait;
+ bool suspended; /* port suspended */
+ bool start_delayed; /* delay start when suspended */
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -630,13 +632,19 @@ static int gs_open(struct tty_struct *tty, struct file *file)
/* if connected, start the I/O stream */
if (port->port_usb) {
- struct gserial *gser = port->port_usb;
-
- pr_debug("gs_open: start ttyGS%d\n", port->port_num);
- gs_start_io(port);
-
- if (gser->connect)
- gser->connect(gser);
+ /* if port is suspended, wait resume to start I/0 stream */
+ if (!port->suspended) {
+ struct gserial *gser = port->port_usb;
+
+ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+
+ if (gser->connect)
+ gser->connect(gser);
+ } else {
+ pr_debug("delay start of ttyGS%d\n", port->port_num);
+ port->start_delayed = true;
+ }
}
pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
@@ -680,7 +688,7 @@ raced_with_open:
pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
gser = port->port_usb;
- if (gser && gser->disconnect)
+ if (gser && !port->suspended && gser->disconnect)
gser->disconnect(gser);
/* wait for circular write buffer to drain, disconnect, or at
@@ -708,6 +716,7 @@ raced_with_open:
else
kfifo_reset(&port->port_write_buf);
+ port->start_delayed = false;
port->port.count = 0;
port->port.tty = NULL;
@@ -1403,6 +1412,38 @@ void gserial_disconnect(struct gserial *gser)
}
EXPORT_SYMBOL_GPL(gserial_disconnect);
+void gserial_suspend(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->suspended = true;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_suspend);
+
+void gserial_resume(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->suspended = false;
+ if (!port->start_delayed) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ pr_debug("delayed start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+ if (gser->connect)
+ gser->connect(gser);
+ port->start_delayed = false;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_resume);
+
static int userial_init(void)
{
unsigned i;
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index e5b08ab8cf7a..cadb76eecbc7 100644
--- a/drivers/usb/gadget/function/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
*
@@ -68,6 +68,8 @@ ssize_t gserial_get_console(unsigned char port_num, char *page);
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
+void gserial_suspend(struct gserial *p);
+void gserial_resume(struct gserial *p);
/* functions are bound to configurations by a config or gadget driver */
int gser_bind_config(struct usb_configuration *c, u8 port_num);
diff --git a/drivers/usb/gadget/function/u_tcm.h b/drivers/usb/gadget/function/u_tcm.h
index 3f7ccecb0f9b..2cd15d9a1c0d 100644
--- a/drivers/usb/gadget/function/u_tcm.h
+++ b/drivers/usb/gadget/function/u_tcm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_tcm.h
*
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 6f1a9d73defe..39c0e29e1b46 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uac1.h - Utility definitions for UAC1 function
*
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.h b/drivers/usb/gadget/function/u_uac1_legacy.h
index 5c1bdf46fe32..b5df9bcbbeba 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.h
+++ b/drivers/usb/gadget/function/u_uac1_legacy.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
*
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 82048791eb6e..b5035711172d 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uac2.h
*
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 16da49a2fcf2..9a01a7d4f17f 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uvc.h
*
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 1473d25ff17a..23ee25383c1f 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* uvc_gadget.h -- USB Video Class Gadget driver
*
@@ -77,6 +77,8 @@ struct uvc_video {
struct uvc_device *uvc;
struct usb_ep *ep;
+ struct work_struct pump;
+
/* Frame parameters */
u8 bpp;
u32 fcc;
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
index 341391dbc81f..7e1d7ca29bf2 100644
--- a/drivers/usb/gadget/function/uvc_configfs.h
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_configfs.h
*
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 495f0ec663ea..4ca89eab6159 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -169,7 +169,9 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
if (ret < 0)
return ret;
- return uvcg_video_pump(video);
+ schedule_work(&video->pump);
+
+ return ret;
}
static int
diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h
index 452d71059b3f..1576005b61fd 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.h
+++ b/drivers/usb/gadget/function/uvc_v4l2.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_v4l2.h -- USB Video Class Gadget driver
*
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 5c042f380708..633e23d58d86 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -142,44 +142,12 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
return ret;
}
-/*
- * I somehow feel that synchronisation won't be easy to achieve here. We have
- * three events that control USB requests submission:
- *
- * - USB request completion: the completion handler will resubmit the request
- * if a video buffer is available.
- *
- * - USB interface setting selection: in response to a SET_INTERFACE request,
- * the handler will start streaming if a video buffer is available and if
- * video is not currently streaming.
- *
- * - V4L2 buffer queueing: the driver will start streaming if video is not
- * currently streaming.
- *
- * Race conditions between those 3 events might lead to deadlocks or other
- * nasty side effects.
- *
- * The "video currently streaming" condition can't be detected by the irqqueue
- * being empty, as a request can still be in flight. A separate "queue paused"
- * flag is thus needed.
- *
- * The paused flag will be set when we try to retrieve the irqqueue head if the
- * queue is empty, and cleared when we queue a buffer.
- *
- * The USB request completion handler will get the buffer at the irqqueue head
- * under protection of the queue spinlock. If the queue is empty, the streaming
- * paused flag will be set. Right after releasing the spinlock a userspace
- * application can queue a buffer. The flag will then cleared, and the ioctl
- * handler will restart the video stream.
- */
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_video *video = req->context;
struct uvc_video_queue *queue = &video->queue;
- struct uvc_buffer *buf;
unsigned long flags;
- int ret;
switch (req->status) {
case 0:
@@ -188,39 +156,20 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
case -ESHUTDOWN: /* disconnect from host. */
uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
uvcg_queue_cancel(queue, 1);
- goto requeue;
+ break;
default:
uvcg_info(&video->uvc->func,
"VS request completed with status %d.\n",
req->status);
uvcg_queue_cancel(queue, 0);
- goto requeue;
}
- spin_lock_irqsave(&video->queue.irqlock, flags);
- buf = uvcg_queue_head(&video->queue);
- if (buf == NULL) {
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
- goto requeue;
- }
-
- video->encode(req, video, buf);
-
- ret = uvcg_video_ep_queue(video, req);
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
-
- if (ret < 0) {
- uvcg_queue_cancel(queue, 0);
- goto requeue;
- }
-
- return;
-
-requeue:
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
+
+ schedule_work(&video->pump);
}
static int
@@ -294,18 +243,15 @@ error:
* This function fills the available USB requests (listed in req_free) with
* video data from the queued buffers.
*/
-int uvcg_video_pump(struct uvc_video *video)
+static void uvcg_video_pump(struct work_struct *work)
{
+ struct uvc_video *video = container_of(work, struct uvc_video, pump);
struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
- /* FIXME TODO Race between uvcg_video_pump and requests completion
- * handler ???
- */
-
while (1) {
/* Retrieve the first available USB request, protected by the
* request lock.
@@ -313,7 +259,7 @@ int uvcg_video_pump(struct uvc_video *video)
spin_lock_irqsave(&video->req_lock, flags);
if (list_empty(&video->req_free)) {
spin_unlock_irqrestore(&video->req_lock, flags);
- return 0;
+ return;
}
req = list_first_entry(&video->req_free, struct usb_request,
list);
@@ -345,7 +291,7 @@ int uvcg_video_pump(struct uvc_video *video)
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
- return 0;
+ return;
}
/*
@@ -363,6 +309,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
}
if (!enable) {
+ cancel_work_sync(&video->pump);
+ uvcg_queue_cancel(&video->queue, 0);
+
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
if (video->req[i])
usb_ep_dequeue(video->ep, video->req[i]);
@@ -384,7 +333,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
} else
video->encode = uvc_video_encode_isoc;
- return uvcg_video_pump(video);
+ schedule_work(&video->pump);
+
+ return ret;
}
/*
@@ -394,6 +345,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
+ INIT_WORK(&video->pump, uvcg_video_pump);
video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
index dff12103f696..03adeefa343b 100644
--- a/drivers/usb/gadget/function/uvc_video.h
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_video.h -- USB Video Class Gadget driver
*
@@ -14,8 +14,6 @@
struct uvc_video;
-int uvcg_video_pump(struct uvc_video *video);
-
int uvcg_video_enable(struct uvc_video *video, int enable);
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index f18f77584fc2..9ed22c5fb7fe 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -229,18 +229,8 @@ static struct usb_composite_driver msg_driver = {
.unbind = msg_unbind,
};
+module_usb_composite_driver(msg_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
-
-static int __init msg_init(void)
-{
- return usb_composite_probe(&msg_driver);
-}
-module_init(msg_init);
-
-static void __exit msg_cleanup(void)
-{
- usb_composite_unregister(&msg_driver);
-}
-module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index f8d35dd60c34..cdf96911e4b1 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -134,11 +134,15 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
}
/* Handle device interrupts */
- for (i = 0; i < vhub->max_ports; i++) {
- u32 dev_mask = VHUB_IRQ_DEVICE1 << i;
+ if (istat & vhub->port_irq_mask) {
+ unsigned long bitmap = istat;
+ int offset = VHUB_IRQ_DEV1_BIT;
+ int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports;
- if (istat & dev_mask)
+ for_each_set_bit_from(offset, &bitmap, size) {
+ i = offset - VHUB_IRQ_DEV1_BIT;
ast_vhub_dev_irq(&vhub->ports[i].dev);
+ }
}
/* Handle top-level vHub EP0 interrupts */
@@ -332,6 +336,8 @@ static int ast_vhub_probe(struct platform_device *pdev)
spin_lock_init(&vhub->lock);
vhub->pdev = pdev;
+ vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
+ VHUB_IRQ_DEV1_BIT);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vhub->regs = devm_ioremap_resource(&pdev->dev, res);
@@ -402,7 +408,9 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err;
/* Init hub emulation */
- ast_vhub_init_hub(vhub);
+ rc = ast_vhub_init_hub(vhub);
+ if (rc)
+ goto err;
/* Initialize HW */
ast_vhub_init_hw(vhub);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
index 6e565c3dbb5b..6497185ec4e7 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -50,6 +50,7 @@
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
enum {
+ AST_VHUB_STR_INDEX_MAX = 4,
AST_VHUB_STR_MANUF = 3,
AST_VHUB_STR_PRODUCT = 2,
AST_VHUB_STR_SERIAL = 1,
@@ -72,13 +73,6 @@ static const struct usb_device_descriptor ast_vhub_dev_desc = {
.bNumConfigurations = 1,
};
-/* Patches to the above when forcing USB1 mode */
-static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
-{
- desc->bcdUSB = cpu_to_le16(0x0100);
- desc->bDeviceProtocol = 0;
-}
-
/*
* Configuration descriptor: same comments as above
* regarding handling USB1 mode.
@@ -302,31 +296,81 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
if (len > dsize)
len = dsize;
- /* Patch it if forcing USB1 */
- if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
- ast_vhub_patch_dev_desc_usb1(ep->buf);
-
/* Shoot it from the EP buffer */
return ast_vhub_reply(ep, NULL, len);
}
+static struct usb_gadget_strings*
+ast_vhub_str_of_container(struct usb_gadget_string_container *container)
+{
+ return (struct usb_gadget_strings *)container->stash;
+}
+
+static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
+ size_t size)
+{
+ int rc, hdr_len, nlangs, max_langs;
+ struct usb_gadget_strings *lang_str;
+ struct usb_gadget_string_container *container;
+ struct usb_string_descriptor *sdesc = buf;
+
+ nlangs = 0;
+ hdr_len = sizeof(struct usb_descriptor_header);
+ max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
+ list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+ if (nlangs >= max_langs)
+ break;
+
+ lang_str = ast_vhub_str_of_container(container);
+ sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
+ }
+
+ rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
+ sdesc->bLength = rc;
+ sdesc->bDescriptorType = USB_DT_STRING;
+
+ return rc;
+}
+
+static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
+ u16 lang_id)
+{
+ struct usb_gadget_strings *lang_str;
+ struct usb_gadget_string_container *container;
+
+ list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+ lang_str = ast_vhub_str_of_container(container);
+ if (lang_str->language == lang_id)
+ return lang_str;
+ }
+
+ return NULL;
+}
+
static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
u8 string_id, u16 lang_id,
u16 len)
{
- int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
- string_id, ep->buf);
+ int rc;
+ u8 buf[256];
+ struct ast_vhub *vhub = ep->vhub;
+ struct usb_gadget_strings *lang_str;
- /*
- * This should never happen unless we put too big strings in
- * the array above
- */
- BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
+ if (string_id == 0) {
+ rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
+ } else {
+ lang_str = ast_vhub_lookup_string(vhub, lang_id);
+ if (!lang_str)
+ return std_req_stall;
- if (rc < 0)
+ rc = usb_gadget_get_string(lang_str, string_id, buf);
+ }
+
+ if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
return std_req_stall;
/* Shoot it from the EP buffer */
+ memcpy(ep->buf, buf, rc);
return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
}
@@ -832,11 +876,148 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
}
-static void ast_vhub_init_desc(struct ast_vhub *vhub)
+static void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
+ const struct device_node *vhub_np)
+{
+ u16 id;
+ u32 data;
+
+ if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
+ id = (u16)data;
+ vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
+ }
+ if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
+ id = (u16)data;
+ vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
+ }
+ if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
+ id = (u16)data;
+ vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
+ }
+}
+
+static void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
+{
+ vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
+ vhub->vhub_dev_desc.bDeviceProtocol = 0;
+}
+
+static struct usb_gadget_string_container*
+ast_vhub_str_container_alloc(struct ast_vhub *vhub)
+{
+ unsigned int size;
+ struct usb_string *str_array;
+ struct usb_gadget_strings *lang_str;
+ struct usb_gadget_string_container *container;
+
+ size = sizeof(*container);
+ size += sizeof(struct usb_gadget_strings);
+ size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
+ container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
+ if (!container)
+ return ERR_PTR(-ENOMEM);
+
+ lang_str = ast_vhub_str_of_container(container);
+ str_array = (struct usb_string *)(lang_str + 1);
+ lang_str->strings = str_array;
+ return container;
+}
+
+static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
+ const struct usb_gadget_strings *src)
{
+ struct usb_string *src_array = src->strings;
+ struct usb_string *dest_array = dest->strings;
+
+ dest->language = src->language;
+ if (src_array && dest_array) {
+ do {
+ *dest_array = *src_array;
+ dest_array++;
+ src_array++;
+ } while (src_array->s);
+ }
+}
+
+static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
+ const struct usb_gadget_strings *src_str)
+{
+ struct usb_gadget_strings *dest_str;
+ struct usb_gadget_string_container *container;
+
+ container = ast_vhub_str_container_alloc(vhub);
+ if (IS_ERR(container))
+ return PTR_ERR(container);
+
+ dest_str = ast_vhub_str_of_container(container);
+ ast_vhub_str_deep_copy(dest_str, src_str);
+ list_add_tail(&container->list, &vhub->vhub_str_desc);
+
+ return 0;
+}
+
+static const struct {
+ const char *name;
+ u8 id;
+} str_id_map[] = {
+ {"manufacturer", AST_VHUB_STR_MANUF},
+ {"product", AST_VHUB_STR_PRODUCT},
+ {"serial-number", AST_VHUB_STR_SERIAL},
+ {},
+};
+
+static int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
+ const struct device_node *desc_np)
+{
+ u32 langid;
+ int ret = 0;
+ int i, offset;
+ const char *str;
+ struct device_node *child;
+ struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
+ struct usb_gadget_strings lang_str = {
+ .strings = (struct usb_string *)str_array,
+ };
+
+ for_each_child_of_node(desc_np, child) {
+ if (of_property_read_u32(child, "reg", &langid))
+ continue; /* no language identifier specified */
+
+ if (!usb_validate_langid(langid))
+ continue; /* invalid language identifier */
+
+ lang_str.language = langid;
+ for (i = offset = 0; str_id_map[i].name; i++) {
+ str = of_get_property(child, str_id_map[i].name, NULL);
+ if (str) {
+ str_array[offset].s = str;
+ str_array[offset].id = str_id_map[i].id;
+ offset++;
+ }
+ }
+ str_array[offset].id = 0;
+ str_array[offset].s = NULL;
+
+ ret = ast_vhub_str_alloc_add(vhub, &lang_str);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ast_vhub_init_desc(struct ast_vhub *vhub)
+{
+ int ret;
+ struct device_node *desc_np;
+ const struct device_node *vhub_np = vhub->pdev->dev.of_node;
+
/* Initialize vhub Device Descriptor. */
memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
sizeof(vhub->vhub_dev_desc));
+ ast_vhub_of_parse_dev_desc(vhub, vhub_np);
+ if (vhub->force_usb1)
+ ast_vhub_fixup_usb1_dev_desc(vhub);
/* Initialize vhub Configuration Descriptor. */
memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
@@ -848,15 +1029,20 @@ static void ast_vhub_init_desc(struct ast_vhub *vhub)
vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
/* Initialize vhub String Descriptors. */
- memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
- sizeof(vhub->vhub_str_desc));
+ INIT_LIST_HEAD(&vhub->vhub_str_desc);
+ desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
+ if (desc_np)
+ ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
+ else
+ ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
+
+ return ret;
}
-void ast_vhub_init_hub(struct ast_vhub *vhub)
+int ast_vhub_init_hub(struct ast_vhub *vhub)
{
vhub->speed = USB_SPEED_UNKNOWN;
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
- ast_vhub_init_desc(vhub);
+ return ast_vhub_init_desc(vhub);
}
-
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
index fac79ef6d669..2e5a1ef14a75 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -51,14 +51,11 @@
#define VHUB_CTRL_UPSTREAM_CONNECT (1 << 0)
/* IER & ISR */
+#define VHUB_IRQ_DEV1_BIT 9
#define VHUB_IRQ_USB_CMD_DEADLOCK (1 << 18)
#define VHUB_IRQ_EP_POOL_NAK (1 << 17)
#define VHUB_IRQ_EP_POOL_ACK_STALL (1 << 16)
-#define VHUB_IRQ_DEVICE5 (1 << 13)
-#define VHUB_IRQ_DEVICE4 (1 << 12)
-#define VHUB_IRQ_DEVICE3 (1 << 11)
-#define VHUB_IRQ_DEVICE2 (1 << 10)
-#define VHUB_IRQ_DEVICE1 (1 << 9)
+#define VHUB_IRQ_DEVICE1 (1 << (VHUB_IRQ_DEV1_BIT))
#define VHUB_IRQ_BUS_RESUME (1 << 8)
#define VHUB_IRQ_BUS_SUSPEND (1 << 7)
#define VHUB_IRQ_BUS_RESET (1 << 6)
@@ -402,6 +399,7 @@ struct ast_vhub {
/* Per-port info */
struct ast_vhub_port *ports;
u32 max_ports;
+ u32 port_irq_mask;
/* Generic EP data structures */
struct ast_vhub_ep *epns;
@@ -423,7 +421,7 @@ struct ast_vhub {
struct usb_device_descriptor vhub_dev_desc;
struct ast_vhub_full_cdesc vhub_conf_desc;
struct usb_hub_descriptor vhub_hub_desc;
- struct usb_gadget_strings vhub_str_desc;
+ struct list_head vhub_str_desc;
};
/* Standard request handlers result codes */
@@ -533,7 +531,7 @@ int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
__VA_ARGS__)
/* hub.c */
-void ast_vhub_init_hub(struct ast_vhub *vhub);
+int ast_vhub_init_hub(struct ast_vhub *vhub);
enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq);
enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index b771a854e29c..d69f61ff0181 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -2043,10 +2043,56 @@ static const struct usba_udc_errata at91sam9g45_errata = {
.pulse_bias = at91sam9g45_pulse_bias,
};
+static const struct usba_ep_config ep_config_sam9[] __initconst = {
+ { .nr_banks = 1 }, /* ep 0 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
+ { .nr_banks = 3, .can_dma = 1 }, /* ep 3 */
+ { .nr_banks = 3, .can_dma = 1 }, /* ep 4 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
+};
+
+static const struct usba_ep_config ep_config_sama5[] __initconst = {
+ { .nr_banks = 1 }, /* ep 0 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 3 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 4 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 7 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 8 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 9 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 10 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 11 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 12 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 13 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 14 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 15 */
+};
+
+static const struct usba_udc_config udc_at91sam9rl_cfg = {
+ .errata = &at91sam9rl_errata,
+ .config = ep_config_sam9,
+ .num_ep = ARRAY_SIZE(ep_config_sam9),
+};
+
+static const struct usba_udc_config udc_at91sam9g45_cfg = {
+ .errata = &at91sam9g45_errata,
+ .config = ep_config_sam9,
+ .num_ep = ARRAY_SIZE(ep_config_sam9),
+};
+
+static const struct usba_udc_config udc_sama5d3_cfg = {
+ .config = ep_config_sama5,
+ .num_ep = ARRAY_SIZE(ep_config_sama5),
+};
+
static const struct of_device_id atmel_udc_dt_ids[] = {
- { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
- { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
- { .compatible = "atmel,sama5d3-udc" },
+ { .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
+ { .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
+ { .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
{ /* sentinel */ }
};
@@ -2055,18 +2101,19 @@ MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc)
{
- u32 val;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *pp;
int i, ret;
struct usba_ep *eps, *ep;
+ const struct usba_udc_config *udc_config;
match = of_match_node(atmel_udc_dt_ids, np);
if (!match)
return ERR_PTR(-EINVAL);
- udc->errata = match->data;
+ udc_config = match->data;
+ udc->errata = udc_config->errata;
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
if (IS_ERR(udc->pmc))
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
@@ -2082,8 +2129,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
if (fifo_mode == 0) {
pp = NULL;
- while ((pp = of_get_next_child(np, pp)))
- udc->num_ep++;
+ udc->num_ep = udc_config->num_ep;
udc->configured_ep = 1;
} else {
udc->num_ep = usba_config_fifo_table(udc);
@@ -2100,52 +2146,38 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
pp = NULL;
i = 0;
- while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
+ while (i < udc->num_ep) {
+ const struct usba_ep_config *ep_cfg = &udc_config->config[i];
+
ep = &eps[i];
- ret = of_property_read_u32(pp, "reg", &val);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
- goto err;
- }
- ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
+ ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : i;
+
+ /* Only the first EP is 64 bytes */
+ if (ep->index == 0)
+ ep->fifo_size = 64;
+ else
+ ep->fifo_size = 1024;
- ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
- goto err;
- }
if (fifo_mode) {
- if (val < udc->fifo_cfg[i].fifo_size) {
+ if (ep->fifo_size < udc->fifo_cfg[i].fifo_size)
dev_warn(&pdev->dev,
- "Using max fifo-size value from DT\n");
- ep->fifo_size = val;
- } else {
+ "Using default max fifo-size value\n");
+ else
ep->fifo_size = udc->fifo_cfg[i].fifo_size;
- }
- } else {
- ep->fifo_size = val;
}
- ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
- goto err;
- }
+ ep->nr_banks = ep_cfg->nr_banks;
if (fifo_mode) {
- if (val < udc->fifo_cfg[i].nr_banks) {
+ if (ep->nr_banks < udc->fifo_cfg[i].nr_banks)
dev_warn(&pdev->dev,
- "Using max nb-banks value from DT\n");
- ep->nr_banks = val;
- } else {
+ "Using default max nb-banks value\n");
+ else
ep->nr_banks = udc->fifo_cfg[i].nr_banks;
- }
- } else {
- ep->nr_banks = val;
}
- ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
- ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
+ ep->can_dma = ep_cfg->can_dma;
+ ep->can_isoc = ep_cfg->can_isoc;
sprintf(ep->name, "ep%d", ep->index);
ep->ep.name = ep->name;
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index a0225e4543d4..48e332439ed5 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -290,6 +290,12 @@ struct usba_ep {
#endif
};
+struct usba_ep_config {
+ u8 nr_banks;
+ unsigned int can_dma:1;
+ unsigned int can_isoc:1;
+};
+
struct usba_request {
struct usb_request req;
struct list_head queue;
@@ -307,6 +313,12 @@ struct usba_udc_errata {
void (*pulse_bias)(struct usba_udc *udc);
};
+struct usba_udc_config {
+ const struct usba_udc_errata *errata;
+ const struct usba_ep_config *config;
+ const int num_ep;
+};
+
struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 9b11046480fe..2e28dde8376f 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1297,6 +1297,8 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
usb_gadget_disconnect(udc->gadget);
+ if (udc->gadget->irq)
+ synchronize_irq(udc->gadget->irq);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 6e3e3ebf715f..0eeaead5acea 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -187,31 +187,31 @@ static const struct {
USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
/* and now some generic EPs so we have enough in multi config */
- EP_INFO("ep3out",
+ EP_INFO("ep-aout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep4in",
+ EP_INFO("ep-bin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep5out",
+ EP_INFO("ep-cout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep6out",
+ EP_INFO("ep-dout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep7in",
+ EP_INFO("ep-ein",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep8out",
+ EP_INFO("ep-fout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep9in",
+ EP_INFO("ep-gin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep10out",
+ EP_INFO("ep-hout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep11out",
+ EP_INFO("ep-iout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep12in",
+ EP_INFO("ep-jin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep13out",
+ EP_INFO("ep-kout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep14in",
+ EP_INFO("ep-lin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep15out",
+ EP_INFO("ep-mout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
#undef EP_INFO
@@ -427,6 +427,7 @@ static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
/* caller must hold lock */
static void set_link_state(struct dummy_hcd *dum_hcd)
+ __must_hold(&dum->lock)
{
struct dummy *dum = dum_hcd->dum;
unsigned int power_bit;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index febabde62f71..b2638e83bb49 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -2440,8 +2440,8 @@ static int fsl_udc_probe(struct platform_device *pdev)
udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
udc_controller->irq = platform_get_irq(pdev, 0);
- if (!udc_controller->irq) {
- ret = -ENODEV;
+ if (udc_controller->irq <= 0) {
+ ret = udc_controller->irq ? : -ENODEV;
goto err_iounmap;
}
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index cb997b82c008..465d0b7c6522 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -1614,17 +1614,17 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
- struct lpc32xx_udc *udc = ep->udc;
+ struct lpc32xx_udc *udc;
u16 maxpacket;
u32 tmp;
unsigned long flags;
/* Verify EP data */
if ((!_ep) || (!ep) || (!desc) ||
- (desc->bDescriptorType != USB_DT_ENDPOINT)) {
- dev_dbg(udc->dev, "bad ep or descriptor\n");
+ (desc->bDescriptorType != USB_DT_ENDPOINT))
return -EINVAL;
- }
+
+ udc = ep->udc;
maxpacket = usb_endpoint_maxp(desc);
if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) {
dev_dbg(udc->dev, "bad ep descriptor's packet size\n");
@@ -1872,7 +1872,7 @@ static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
{
struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
- struct lpc32xx_udc *udc = ep->udc;
+ struct lpc32xx_udc *udc;
unsigned long flags;
if ((!ep) || (ep->hwep_num <= 1))
@@ -1882,6 +1882,7 @@ static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
if (ep->is_in)
return -EAGAIN;
+ udc = ep->udc;
spin_lock_irqsave(&udc->lock, flags);
if (value == 1) {
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 75d16a8902e6..931e6362a13d 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1667,7 +1667,7 @@ static int m66592_probe(struct platform_device *pdev)
err_add_udc:
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-
+ m66592->ep0_req = NULL;
clean_up3:
if (m66592->pdata->on_chip) {
clk_disable(m66592->clk);
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index 8fbc083b6732..23f33946d80c 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -901,7 +901,7 @@ loop:
}
set_current_state(TASK_RUNNING);
- dev_info(udc->dev, "SPI thread exiting");
+ dev_info(udc->dev, "SPI thread exiting\n");
return 0;
}
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 35e02a8d0091..5bb0568b934e 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -1548,7 +1548,7 @@ static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num,
delegate = true;
/* delegate USB standard requests to the gadget driver */
- if (delegate == true) {
+ if (delegate) {
/* USB requests handled by gadget */
if (setup->wLength) {
/* DATA phase from gadget, STATUS phase from u3d */
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 5af0fe9c61d7..928057b206f1 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -54,7 +54,7 @@ static const char * const ep_name[] = {
*
* If use_dma is disabled, pio will be used instead.
*/
-static bool use_dma = 0;
+static bool use_dma = false;
module_param(use_dma, bool, 0644);
/*
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index bf87c6c0d7f6..4139da885651 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2576,7 +2576,7 @@ omap_ep_setup(char *name, u8 addr, u8 type,
case USB_ENDPOINT_XFER_INT:
ep->ep.caps.type_int = true;
break;
- };
+ }
if (addr & USB_DIR_IN)
ep->ep.caps.dir_in = true;
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 0507a2ca0f55..80002d97b59d 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -251,10 +251,6 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep,
static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
struct s3c2410_ep *ep, int status)
{
- /* Sanity check */
- if (&ep->queue == NULL)
- return;
-
while (!list_empty(&ep->queue)) {
struct s3c2410_request *req;
req = list_entry(ep->queue.next, struct s3c2410_request,
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index dfabc54cdc27..bbe1a04686da 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -158,6 +158,30 @@
#define SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0)
#define SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \
SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK)
+#define SSPX_CORE_CNT56 0x6fc
+#define SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(x) ((x) & \
+ SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK)
+#define SSPX_CORE_CNT57 0x700
+#define SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(x) ((x) & \
+ SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK)
+#define SSPX_CORE_CNT65 0x720
+#define SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(x) ((x) & \
+ SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK)
+#define SSPX_CORE_CNT66 0x724
+#define SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(x) ((x) & \
+ SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK)
+#define SSPX_CORE_CNT67 0x728
+#define SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(x) ((x) & \
+ SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK)
+#define SSPX_CORE_CNT72 0x73c
+#define SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(x) ((x) & \
+ SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK)
#define SSPX_CORE_PADCTL4 0x750
#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0)
#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \
@@ -492,6 +516,7 @@ struct tegra_xudc {
bool powergated;
struct usb_phy **usbphy;
+ struct usb_phy *curr_usbphy;
struct notifier_block vbus_nb;
struct completion disconnect_complete;
@@ -530,6 +555,7 @@ struct tegra_xudc_soc {
bool invalid_seq_num;
bool pls_quirk;
bool port_reset_quirk;
+ bool port_speed_quirk;
bool has_ipfs;
};
@@ -599,6 +625,78 @@ static inline void dump_trb(struct tegra_xudc *xudc, const char *type,
trb->control);
}
+static void tegra_xudc_limit_port_speed(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ /* limit port speed to gen 1 */
+ val = xudc_readl(xudc, SSPX_CORE_CNT56);
+ val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x260);
+ xudc_writel(xudc, val, SSPX_CORE_CNT56);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT57);
+ val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x6D6);
+ xudc_writel(xudc, val, SSPX_CORE_CNT57);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT65);
+ val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0x4B0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT66);
+ val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x4B0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT67);
+ val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x4B0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT67);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT72);
+ val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
+ val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x10);
+ xudc_writel(xudc, val, SSPX_CORE_CNT72);
+}
+
+static void tegra_xudc_restore_port_speed(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ /* restore port speed to gen2 */
+ val = xudc_readl(xudc, SSPX_CORE_CNT56);
+ val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x438);
+ xudc_writel(xudc, val, SSPX_CORE_CNT56);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT57);
+ val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x528);
+ xudc_writel(xudc, val, SSPX_CORE_CNT57);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT65);
+ val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0xE10);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT66);
+ val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x348);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT67);
+ val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x5a0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT67);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT72);
+ val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
+ val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x1c21);
+ xudc_writel(xudc, val, SSPX_CORE_CNT72);
+}
+
static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
{
int err;
@@ -631,6 +729,9 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
reinit_completion(&xudc->disconnect_complete);
+ if (xudc->soc->port_speed_quirk)
+ tegra_xudc_restore_port_speed(xudc);
+
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
@@ -719,6 +820,7 @@ static int tegra_xudc_vbus_notify(struct notifier_block *nb,
if (!xudc->suspended && phy_index != -1) {
xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
+ xudc->curr_usbphy = usbphy;
schedule_work(&xudc->usb_role_sw_work);
}
@@ -2042,6 +2144,20 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
return 0;
}
+static int tegra_xudc_gadget_vbus_draw(struct usb_gadget *gadget,
+ unsigned int m_a)
+{
+ int ret = 0;
+ struct tegra_xudc *xudc = to_xudc(gadget);
+
+ dev_dbg(xudc->dev, "%s: %u mA\n", __func__, m_a);
+
+ if (xudc->curr_usbphy->chg_type == SDP_TYPE)
+ ret = usb_phy_set_power(xudc->curr_usbphy, m_a);
+
+ return ret;
+}
+
static int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on)
{
struct tegra_xudc *xudc = to_xudc(gadget);
@@ -2058,6 +2174,7 @@ static struct usb_gadget_ops tegra_xudc_gadget_ops = {
.pullup = tegra_xudc_gadget_pullup,
.udc_start = tegra_xudc_gadget_start,
.udc_stop = tegra_xudc_gadget_stop,
+ .vbus_draw = tegra_xudc_gadget_vbus_draw,
.set_selfpowered = tegra_xudc_set_selfpowered,
};
@@ -3274,6 +3391,9 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
xudc_writel(xudc, val, BLCG);
}
+ if (xudc->soc->port_speed_quirk)
+ tegra_xudc_limit_port_speed(xudc);
+
/* Set a reasonable U3 exit timer value. */
val = xudc_readl(xudc, SSPX_CORE_PADCTL4);
val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK);
@@ -3506,6 +3626,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
.invalid_seq_num = true,
.pls_quirk = true,
.port_reset_quirk = true,
+ .port_speed_quirk = false,
.has_ipfs = true,
};
@@ -3519,6 +3640,21 @@ static struct tegra_xudc_soc tegra186_xudc_soc_data = {
.invalid_seq_num = false,
.pls_quirk = false,
.port_reset_quirk = false,
+ .port_speed_quirk = false,
+ .has_ipfs = false,
+};
+
+static struct tegra_xudc_soc tegra194_xudc_soc_data = {
+ .clock_names = tegra186_xudc_clock_names,
+ .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
+ .num_phys = 4,
+ .u1_enable = true,
+ .u2_enable = true,
+ .lpm_enable = true,
+ .invalid_seq_num = false,
+ .pls_quirk = false,
+ .port_reset_quirk = false,
+ .port_speed_quirk = true,
.has_ipfs = false,
};
@@ -3531,6 +3667,10 @@ static const struct of_device_id tegra_xudc_of_match[] = {
.compatible = "nvidia,tegra186-xudc",
.data = &tegra186_xudc_soc_data
},
+ {
+ .compatible = "nvidia,tegra194-xudc",
+ .data = &tegra194_xudc_soc_data
+ },
{ }
};
MODULE_DEVICE_TABLE(of, tegra_xudc_of_match);
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index b1cfc8279c3d..709553bdb233 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -1732,6 +1732,7 @@ static void xudc_set_clear_feature(struct xusb_udc *udc)
* Process setup packet and delegate to gadget layer.
*/
static void xudc_handle_setup(struct xusb_udc *udc)
+ __must_hold(&udc->lock)
{
struct xusb_ep *ep0 = &udc->ep[0];
struct usb_ctrlrequest setup;
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 7c24d1ce1088..58a4d3325090 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -65,3 +65,27 @@ usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf)
return buf [0];
}
EXPORT_SYMBOL_GPL(usb_gadget_get_string);
+
+/**
+ * usb_validate_langid - validate usb language identifiers
+ * @lang: usb language identifier
+ *
+ * Returns true for valid language identifier, otherwise false.
+ */
+bool usb_validate_langid(u16 langid)
+{
+ u16 primary_lang = langid & 0x3ff; /* bit [9:0] */
+ u16 sub_lang = langid >> 10; /* bit [15:10] */
+
+ switch (primary_lang) {
+ case 0:
+ case 0x62 ... 0xfe:
+ case 0x100 ... 0x3ff:
+ return false;
+ }
+ if (!sub_lang)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(usb_validate_langid);