chengshan / rpms / kernel

Forked from rpms/kernel 2 years ago
Clone
f2c60e
From patchwork Mon Oct  9 12:00:50 2017
f2c60e
Content-Type: text/plain; charset="utf-8"
f2c60e
MIME-Version: 1.0
f2c60e
Content-Transfer-Encoding: 7bit
f2c60e
Subject: [PATCHv4,1/2] drivers: phy: add calibrate method
f2c60e
From: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
f2c60e
X-Patchwork-Id: 9992829
f2c60e
Message-Id: <1507550451-21324-2-git-send-email-andrzej.p@samsung.com>
f2c60e
To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org,
f2c60e
 linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org
f2c60e
Cc: Mark Rutland <mark.rutland@arm.com>, Felipe Balbi <balbi@kernel.org>,
f2c60e
 Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>,
f2c60e
 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
f2c60e
 Russell King <linux@armlinux.org.uk>,
f2c60e
 Krzysztof Kozlowski <krzk@kernel.org>, 
f2c60e
 Kishon Vijay Abraham I <kishon@ti.com>,
f2c60e
 Rob Herring <robh+dt@kernel.org>, Kukjin Kim <kgene@kernel.org>,
f2c60e
 Andrzej Pietrasiewicz <andrzej.p@samsung.com>, 
f2c60e
 Marek Szyprowski <m.szyprowski@samsung.com>
f2c60e
Date: Mon, 09 Oct 2017 14:00:50 +0200
f2c60e
f2c60e
Some quirky UDCs (like dwc3 on Exynos) need to have their phys calibrated e.g.
f2c60e
for using super speed. This patch adds a new phy_calibrate() method.
f2c60e
When the calibration should be used is dependent on actual chip.
f2c60e
f2c60e
In case of dwc3 on Exynos the calibration must happen after usb_add_hcd()
f2c60e
(while in host mode), because certain phy parameters like Tx LOS levels
f2c60e
and boost levels need to be calibrated further post initialization of xHCI
f2c60e
controller, to get SuperSpeed operations working. But an hcd must be
f2c60e
prepared first in order to pass it to usb_add_hcd(), so, in particular, dwc3
f2c60e
registers must be available first, and in order for the latter to happen
f2c60e
the phys must be initialized. This poses a chicken and egg problem if
f2c60e
the calibration were to be performed in phy_init(). To break the circular
f2c60e
dependency a separate method is added which can be called at a desired
f2c60e
moment after phy intialization.
f2c60e
f2c60e
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
f2c60e
---
f2c60e
 drivers/phy/phy-core.c  | 15 +++++++++++++++
f2c60e
 include/linux/phy/phy.h | 10 ++++++++++
f2c60e
 2 files changed, 25 insertions(+)
f2c60e
f2c60e
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
f2c60e
index a268f4d..b4964b0 100644
f2c60e
--- a/drivers/phy/phy-core.c
f2c60e
+++ b/drivers/phy/phy-core.c
f2c60e
@@ -372,6 +372,21 @@ int phy_reset(struct phy *phy)
f2c60e
 }
f2c60e
 EXPORT_SYMBOL_GPL(phy_reset);
f2c60e
 
f2c60e
+int phy_calibrate(struct phy *phy)
f2c60e
+{
f2c60e
+	int ret;
f2c60e
+
f2c60e
+	if (!phy || !phy->ops->calibrate)
f2c60e
+		return 0;
f2c60e
+
f2c60e
+	mutex_lock(&phy->mutex);
f2c60e
+	ret = phy->ops->calibrate(phy);
f2c60e
+	mutex_unlock(&phy->mutex);
f2c60e
+
f2c60e
+	return ret;
f2c60e
+}
f2c60e
+EXPORT_SYMBOL_GPL(phy_calibrate);
f2c60e
+
f2c60e
 /**
f2c60e
  * _of_phy_get() - lookup and obtain a reference to a phy by phandle
f2c60e
  * @np: device_node for which to get the phy
f2c60e
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
f2c60e
index e694d40..87580c8 100644
f2c60e
--- a/include/linux/phy/phy.h
f2c60e
+++ b/include/linux/phy/phy.h
f2c60e
@@ -39,6 +39,7 @@ enum phy_mode {
f2c60e
  * @power_off: powering off the phy
f2c60e
  * @set_mode: set the mode of the phy
f2c60e
  * @reset: resetting the phy
f2c60e
+ * @calibrate: calibrate the phy
f2c60e
  * @owner: the module owner containing the ops
f2c60e
  */
f2c60e
 struct phy_ops {
f2c60e
@@ -48,6 +49,7 @@ struct phy_ops {
f2c60e
 	int	(*power_off)(struct phy *phy);
f2c60e
 	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
f2c60e
 	int	(*reset)(struct phy *phy);
f2c60e
+	int	(*calibrate)(struct phy *phy);
f2c60e
 	struct module *owner;
f2c60e
 };
f2c60e
 
f2c60e
@@ -141,6 +143,7 @@ static inline void *phy_get_drvdata(struct phy *phy)
f2c60e
 int phy_power_off(struct phy *phy);
f2c60e
 int phy_set_mode(struct phy *phy, enum phy_mode mode);
f2c60e
 int phy_reset(struct phy *phy);
f2c60e
+int phy_calibrate(struct phy *phy);
f2c60e
 static inline int phy_get_bus_width(struct phy *phy)
f2c60e
 {
f2c60e
 	return phy->attrs.bus_width;
f2c60e
@@ -262,6 +265,13 @@ static inline int phy_reset(struct phy *phy)
f2c60e
 	return -ENOSYS;
f2c60e
 }
f2c60e
 
f2c60e
+static inline int phy_calibrate(struct phy *phy)
f2c60e
+{
f2c60e
+	if (!phy)
f2c60e
+		return 0;
f2c60e
+	return -ENOSYS;
f2c60e
+}
f2c60e
+
f2c60e
 static inline int phy_get_bus_width(struct phy *phy)
f2c60e
 {
f2c60e
 	return -ENOSYS;
f2c60e
From patchwork Mon Oct  9 12:00:51 2017
f2c60e
Content-Type: text/plain; charset="utf-8"
f2c60e
MIME-Version: 1.0
f2c60e
Content-Transfer-Encoding: 7bit
f2c60e
Subject: [PATCHv4,
f2c60e
 2/2] phy: exynos5-usbdrd: Calibrate LOS levels for exynos5420/5800
f2c60e
From: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
f2c60e
X-Patchwork-Id: 9992809
f2c60e
Message-Id: <1507550451-21324-3-git-send-email-andrzej.p@samsung.com>
f2c60e
To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org,
f2c60e
 linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org
f2c60e
Cc: Mark Rutland <mark.rutland@arm.com>, Felipe Balbi <balbi@kernel.org>,
f2c60e
 Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>,
f2c60e
 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
f2c60e
 Russell King <linux@armlinux.org.uk>,
f2c60e
 Krzysztof Kozlowski <krzk@kernel.org>, 
f2c60e
 Kishon Vijay Abraham I <kishon@ti.com>,
f2c60e
 Rob Herring <robh+dt@kernel.org>, Kukjin Kim <kgene@kernel.org>,
f2c60e
 Andrzej Pietrasiewicz <andrzej.p@samsung.com>, 
f2c60e
 Marek Szyprowski <m.szyprowski@samsung.com>
f2c60e
Date: Mon, 09 Oct 2017 14:00:51 +0200
f2c60e
f2c60e
From: Vivek Gautam <gautam.vivek@samsung.com>
f2c60e
f2c60e
Adding phy calibration sequence for USB 3.0 DRD PHY present on
f2c60e
Exynos5420/5800 systems.
f2c60e
This calibration facilitates setting certain PHY parameters viz.
f2c60e
the Loss-of-Signal (LOS) Detector Threshold Level, as well as
f2c60e
Tx-Vboost-Level for Super-Speed operations.
f2c60e
Additionally we also set proper time to wait for RxDetect measurement,
f2c60e
for desired PHY reference clock, so as to solve issue with enumeration
f2c60e
of few USB 3.0 devices, like Samsung SUM-TSB16S 3.0 USB drive
f2c60e
on the controller.
f2c60e
f2c60e
We are using CR_port for this purpose to send required data
f2c60e
to override the LOS values.
f2c60e
f2c60e
On testing with USB 3.0 devices on USB 3.0 port present on
f2c60e
SMDK5420, and peach-pit boards should see following message:
f2c60e
usb 2-1: new SuperSpeed USB device number 2 using xhci-hcd
f2c60e
f2c60e
and without this patch, should see below shown message:
f2c60e
usb 1-1: new high-speed USB device number 2 using xhci-hcd
f2c60e
f2c60e
[Also removed unnecessary extra lines in the register macro definitions]
f2c60e
f2c60e
Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
f2c60e
[adapted to use phy_calibrate as entry point]
f2c60e
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
f2c60e
---
f2c60e
 drivers/phy/samsung/phy-exynos5-usbdrd.c | 183 +++++++++++++++++++++++++++++++
f2c60e
 drivers/usb/dwc3/core.c                  |   7 +-
f2c60e
 2 files changed, 188 insertions(+), 2 deletions(-)
f2c60e
f2c60e
diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c
f2c60e
index 22c68f5..9e83c15 100644
f2c60e
--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
f2c60e
+++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
f2c60e
@@ -90,7 +90,17 @@
f2c60e
 #define PHYCLKRST_COMMONONN			BIT(0)
f2c60e
 
f2c60e
 #define EXYNOS5_DRD_PHYREG0			0x14
f2c60e
+#define PHYREG0_SSC_REF_CLK_SEL			BIT(21)
f2c60e
+#define PHYREG0_SSC_RANGE			BIT(20)
f2c60e
+#define PHYREG0_CR_WRITE			BIT(19)
f2c60e
+#define PHYREG0_CR_READ				BIT(18)
f2c60e
+#define PHYREG0_CR_DATA_IN(_x)			((_x) << 2)
f2c60e
+#define PHYREG0_CR_CAP_DATA			BIT(1)
f2c60e
+#define PHYREG0_CR_CAP_ADDR			BIT(0)
f2c60e
+
f2c60e
 #define EXYNOS5_DRD_PHYREG1			0x18
f2c60e
+#define PHYREG1_CR_DATA_OUT(_x)			((_x) << 1)
f2c60e
+#define PHYREG1_CR_ACK				BIT(0)
f2c60e
 
f2c60e
 #define EXYNOS5_DRD_PHYPARAM0			0x1c
f2c60e
 
f2c60e
@@ -119,6 +129,25 @@
f2c60e
 #define EXYNOS5_DRD_PHYRESUME			0x34
f2c60e
 #define EXYNOS5_DRD_LINKPORT			0x44
f2c60e
 
f2c60e
+/* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */
f2c60e
+#define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN		(0x15)
f2c60e
+#define LOSLEVEL_OVRD_IN_LOS_BIAS_5420			(0x5 << 13)
f2c60e
+#define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT		(0x0 << 13)
f2c60e
+#define LOSLEVEL_OVRD_IN_EN				(0x1 << 10)
f2c60e
+#define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT		(0x9 << 0)
f2c60e
+
f2c60e
+#define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN	(0x12)
f2c60e
+#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420		(0x5 << 13)
f2c60e
+#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT		(0x4 << 13)
f2c60e
+
f2c60e
+#define EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG		(0x1010)
f2c60e
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M		(0x4 << 4)
f2c60e
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M		(0x8 << 4)
f2c60e
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_25M_26M		(0x8 << 4)
f2c60e
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M	(0x20 << 4)
f2c60e
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5		(0x20 << 4)
f2c60e
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M		(0x40 << 4)
f2c60e
+
f2c60e
 #define KHZ	1000
f2c60e
 #define MHZ	(KHZ * KHZ)
f2c60e
 
f2c60e
@@ -527,6 +556,151 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
f2c60e
 	return 0;
f2c60e
 }
f2c60e
 
f2c60e
+static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
f2c60e
+						u32 val, u32 cmd)
f2c60e
+{
f2c60e
+	u32 usec = 100;
f2c60e
+	unsigned int result;
f2c60e
+
f2c60e
+	writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
f2c60e
+
f2c60e
+	do {
f2c60e
+		result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
f2c60e
+		if (result & PHYREG1_CR_ACK)
f2c60e
+			break;
f2c60e
+
f2c60e
+		udelay(1);
f2c60e
+	} while (usec-- > 0);
f2c60e
+
f2c60e
+	if (!usec) {
f2c60e
+		dev_err(phy_drd->dev,
f2c60e
+			"CRPORT handshake timeout1 (0x%08x)\n", val);
f2c60e
+		return -ETIME;
f2c60e
+	}
f2c60e
+
f2c60e
+	usec = 100;
f2c60e
+
f2c60e
+	writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
f2c60e
+
f2c60e
+	do {
f2c60e
+		result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
f2c60e
+		if (!(result & PHYREG1_CR_ACK))
f2c60e
+			break;
f2c60e
+
f2c60e
+		udelay(1);
f2c60e
+	} while (usec-- > 0);
f2c60e
+
f2c60e
+	if (!usec) {
f2c60e
+		dev_err(phy_drd->dev,
f2c60e
+			"CRPORT handshake timeout2 (0x%08x)\n", val);
f2c60e
+		return -ETIME;
f2c60e
+	}
f2c60e
+
f2c60e
+	return 0;
f2c60e
+}
f2c60e
+
f2c60e
+static int crport_ctrl_write(struct exynos5_usbdrd_phy *phy_drd,
f2c60e
+						u32 addr, u32 data)
f2c60e
+{
f2c60e
+	int ret;
f2c60e
+
f2c60e
+	/* Write Address */
f2c60e
+	writel(PHYREG0_CR_DATA_IN(addr),
f2c60e
+		phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
f2c60e
+	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(addr),
f2c60e
+				PHYREG0_CR_CAP_ADDR);
f2c60e
+	if (ret)
f2c60e
+		return ret;
f2c60e
+
f2c60e
+	/* Write Data */
f2c60e
+	writel(PHYREG0_CR_DATA_IN(data),
f2c60e
+		phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
f2c60e
+	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
f2c60e
+				PHYREG0_CR_CAP_DATA);
f2c60e
+	if (ret)
f2c60e
+		return ret;
f2c60e
+
f2c60e
+	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
f2c60e
+				PHYREG0_CR_WRITE);
f2c60e
+
f2c60e
+	return ret;
f2c60e
+}
f2c60e
+
f2c60e
+/*
f2c60e
+ * Calibrate few PHY parameters using CR_PORT register to meet
f2c60e
+ * SuperSpeed requirements on Exynos5420 and Exynos5800 systems,
f2c60e
+ * which have 28nm USB 3.0 DRD PHY.
f2c60e
+ */
f2c60e
+static int exynos5420_usbdrd_phy_calibrate(struct exynos5_usbdrd_phy *phy_drd)
f2c60e
+{
f2c60e
+	unsigned int temp;
f2c60e
+	int ret = 0;
f2c60e
+
f2c60e
+	/*
f2c60e
+	 * Change los_bias to (0x5) for 28nm PHY from a
f2c60e
+	 * default value (0x0); los_level is set as default
f2c60e
+	 * (0x9) as also reflected in los_level[30:26] bits
f2c60e
+	 * of PHYPARAM0 register.
f2c60e
+	 */
f2c60e
+	temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 |
f2c60e
+		LOSLEVEL_OVRD_IN_EN |
f2c60e
+		LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT;
f2c60e
+	ret = crport_ctrl_write(phy_drd,
f2c60e
+				EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN,
f2c60e
+				temp);
f2c60e
+	if (ret) {
f2c60e
+		dev_err(phy_drd->dev,
f2c60e
+		 "Failed setting Loss-of-Signal level for SuperSpeed\n");
f2c60e
+		return ret;
f2c60e
+	}
f2c60e
+
f2c60e
+	/*
f2c60e
+	 * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning,
f2c60e
+	 * to raise Tx signal level from its default value of (0x4)
f2c60e
+	 */
f2c60e
+	temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420;
f2c60e
+	ret = crport_ctrl_write(phy_drd,
f2c60e
+				EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN,
f2c60e
+				temp);
f2c60e
+	if (ret) {
f2c60e
+		dev_err(phy_drd->dev,
f2c60e
+		 "Failed setting Tx-Vboost-Level for SuperSpeed\n");
f2c60e
+		return ret;
f2c60e
+	}
f2c60e
+
f2c60e
+	/*
f2c60e
+	 * Set proper time to wait for RxDetect measurement, for
f2c60e
+	 * desired reference clock of PHY, by tuning the CR_PORT
f2c60e
+	 * register LANE0.TX_DEBUG which is internal to PHY.
f2c60e
+	 * This fixes issue with few USB 3.0 devices, which are
f2c60e
+	 * not detected (not even generate interrupts on the bus
f2c60e
+	 * on insertion) without this change.
f2c60e
+	 * e.g. Samsung SUM-TSB16S 3.0 USB drive.
f2c60e
+	 */
f2c60e
+	switch (phy_drd->extrefclk) {
f2c60e
+	case EXYNOS5_FSEL_50MHZ:
f2c60e
+		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M;
f2c60e
+		break;
f2c60e
+	case EXYNOS5_FSEL_20MHZ:
f2c60e
+	case EXYNOS5_FSEL_19MHZ2:
f2c60e
+		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M;
f2c60e
+		break;
f2c60e
+	case EXYNOS5_FSEL_24MHZ:
f2c60e
+	default:
f2c60e
+		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M;
f2c60e
+		break;
f2c60e
+	}
f2c60e
+
f2c60e
+	ret = crport_ctrl_write(phy_drd,
f2c60e
+				EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG,
f2c60e
+				temp);
f2c60e
+	if (ret)
f2c60e
+		dev_err(phy_drd->dev,
f2c60e
+		 "Failed setting RxDetect measurement time for SuperSpeed\n");
f2c60e
+
f2c60e
+	return ret;
f2c60e
+}
f2c60e
+
f2c60e
 static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
f2c60e
 					struct of_phandle_args *args)
f2c60e
 {
f2c60e
@@ -538,11 +712,20 @@ static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
f2c60e
 	return phy_drd->phys[args->args[0]].phy;
f2c60e
 }
f2c60e
 
f2c60e
+static int exynos5_usbdrd_phy_calibrate(struct phy *phy)
f2c60e
+{
f2c60e
+	struct phy_usb_instance *inst = phy_get_drvdata(phy);
f2c60e
+	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
f2c60e
+
f2c60e
+	return exynos5420_usbdrd_phy_calibrate(phy_drd);
f2c60e
+}
f2c60e
+
f2c60e
 static const struct phy_ops exynos5_usbdrd_phy_ops = {
f2c60e
 	.init		= exynos5_usbdrd_phy_init,
f2c60e
 	.exit		= exynos5_usbdrd_phy_exit,
f2c60e
 	.power_on	= exynos5_usbdrd_phy_power_on,
f2c60e
 	.power_off	= exynos5_usbdrd_phy_power_off,
f2c60e
+	.calibrate	= exynos5_usbdrd_phy_calibrate,
f2c60e
 	.owner		= THIS_MODULE,
f2c60e
 };
f2c60e
 
f2c60e
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
f2c60e
index 03474d3..224e0dd 100644
f2c60e
--- a/drivers/usb/dwc3/core.c
f2c60e
+++ b/drivers/usb/dwc3/core.c
f2c60e
@@ -156,9 +156,10 @@ static void __dwc3_set_mode(struct work_struct *work)
f2c60e
 		} else {
f2c60e
 			if (dwc->usb2_phy)
f2c60e
 				otg_set_vbus(dwc->usb2_phy->otg, true);
f2c60e
-			if (dwc->usb2_generic_phy)
f2c60e
+			if (dwc->usb2_generic_phy) {
f2c60e
 				phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
f2c60e
-
f2c60e
+				phy_calibrate(dwc->usb2_generic_phy);
f2c60e
+			}
f2c60e
 		}
f2c60e
 		break;
f2c60e
 	case DWC3_GCTL_PRTCAP_DEVICE:
f2c60e
@@ -955,6 +956,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
f2c60e
 				dev_err(dev, "failed to initialize host\n");
f2c60e
 			return ret;
f2c60e
 		}
f2c60e
+		if (dwc->usb2_generic_phy)
f2c60e
+			phy_calibrate(dwc->usb2_generic_phy);
f2c60e
 		break;
f2c60e
 	case USB_DR_MODE_OTG:
f2c60e
 		INIT_WORK(&dwc->drd_work, __dwc3_set_mode);