linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree
@ 2017-01-04 13:22 Manish Narani
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: add support for OTG driver compilation Manish Narani
                   ` (6 more replies)
  0 siblings, 7 replies; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch adds OTG interrupt support in device tree. It will add
an extra interrupt line number dedicated to OTG events. This will
enable OTG interrupts to serve in DWC3 OTG driver.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 68a90833..ce9ad02 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -351,7 +351,7 @@
 			compatible = "snps,dwc3";
 			status = "disabled";
 			interrupt-parent = <&gic>;
-			interrupts = <0 65 4>;
+			interrupts = <0 65 4>, <0 69 4>;
 			reg = <0x0 0xfe200000 0x0 0x40000>;
 			clock-names = "clk_xin", "clk_ahb";
 		};
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH] usb: dwc3: add support for OTG driver compilation
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
@ 2017-01-04 13:22 ` Manish Narani
  2017-01-04 13:31   ` Felipe Balbi
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: core: add OTG support function calls and modifications Manish Narani
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch adds support for OTG driver compilation and object
file creation

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/Makefile | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index ffca340..2d269ad 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
 	dwc3-y				+= gadget.o ep0.o
 endif
 
+ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
+	dwc3-y				+= otg.o
+endif
+
 ifneq ($(CONFIG_USB_DWC3_ULPI),)
 	dwc3-y				+= ulpi.o
 endif
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH] usb: dwc3: core: add OTG support function calls and modifications
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: add support for OTG driver compilation Manish Narani
@ 2017-01-04 13:22 ` Manish Narani
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework Manish Narani
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch adds function call to initialize OTG driver. This patch
also adds support for OTG device structure in DWC3 device.

Modifications to event buffer related functions which are called
from OTG driver upon requirement.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/core.c | 17 ++++++++++++-----
 drivers/usb/dwc3/core.h | 14 ++++++++++++++
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 369bab1..9ab9c5b 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -240,7 +240,7 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
  * dwc3_free_event_buffers - frees all allocated event buffers
  * @dwc: Pointer to our controller context structure
  */
-static void dwc3_free_event_buffers(struct dwc3 *dwc)
+void dwc3_free_event_buffers(struct dwc3 *dwc)
 {
 	struct dwc3_event_buffer	*evt;
 
@@ -257,7 +257,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
  * Returns 0 on success otherwise negative errno. In the error case, dwc
  * may contain some buffers allocated but not all which were requested.
  */
-static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
+int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
 {
 	struct dwc3_event_buffer *evt;
 
@@ -277,7 +277,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
 {
 	struct dwc3_event_buffer	*evt;
 
@@ -862,10 +862,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 		}
 		break;
 	case USB_DR_MODE_OTG:
-		ret = dwc3_host_init(dwc);
+		ret = dwc3_otg_init(dwc);
 		if (ret) {
 			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize host\n");
+				dev_err(dev, "failed to initialize otg\n");
 			return ret;
 		}
 
@@ -875,6 +875,13 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 				dev_err(dev, "failed to initialize gadget\n");
 			return ret;
 		}
+
+		ret = dwc3_host_init(dwc);
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "failed to initialize host\n");
+			return ret;
+		}
 		break;
 	default:
 		dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index de5a857..6b92064 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -887,6 +887,8 @@ struct dwc3 {
 	struct usb_gadget	gadget;
 	struct usb_gadget_driver *gadget_driver;
 
+	struct dwc3_otg		*otg;
+
 	struct usb_phy		*usb2_phy;
 	struct usb_phy		*usb3_phy;
 
@@ -987,6 +989,7 @@ struct dwc3 {
 	unsigned		setup_packet_pending:1;
 	unsigned		three_stage_setup:1;
 	unsigned		usb3_lpm_capable:1;
+	unsigned		remote_wakeup:1;
 
 	unsigned		disable_scramble_quirk:1;
 	unsigned		u2exit_lfps_quirk:1;
@@ -1220,6 +1223,13 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
 { return 0; }
 #endif
 
+#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_otg_init(struct dwc3 *dwc);
+#else
+static inline int dwc3_otg_init(struct dwc3 *dwc)
+{ return 0; }
+#endif
+
 /* power management interface */
 #if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
 int dwc3_gadget_suspend(struct dwc3 *dwc);
@@ -1251,4 +1261,8 @@ static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
 { }
 #endif
 
+int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length);
+void dwc3_free_event_buffers(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+
 #endif /* __DRIVERS_USB_DWC3_CORE_H */
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: add support for OTG driver compilation Manish Narani
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: core: add OTG support function calls and modifications Manish Narani
@ 2017-01-04 13:22 ` Manish Narani
  2017-01-04 14:03   ` Felipe Balbi
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver Manish Narani
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch adds support for OTG in DWC3 gadget framework. This
also adds support for HNP polling by host while in OTG mode.

Modifications in couple of functions to make it in sync with
OTG driver while keeping its original functionality intact.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/ep0.c    | 49 +++++++++++++++++++++++---
 drivers/usb/dwc3/gadget.c | 87 +++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 116 insertions(+), 20 deletions(-)

diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 4878d18..aa78c1b 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -334,6 +334,8 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
 				usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
 		}
 
+		usb_status |= dwc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+
 		break;
 
 	case USB_RECIP_INTERFACE:
@@ -448,11 +450,45 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
 
 	switch (wValue) {
 	case USB_DEVICE_REMOTE_WAKEUP:
+		if (set)
+			dwc->remote_wakeup = 1;
+		else
+			dwc->remote_wakeup = 0;
 		break;
-	/*
-	 * 9.4.1 says only only for SS, in AddressState only for
-	 * default control pipe
-	 */
+	case USB_DEVICE_B_HNP_ENABLE:
+		dev_dbg(dwc->dev,
+			"SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+		if (set && dwc->gadget.is_otg) {
+			if (dwc->gadget.host_request_flag) {
+				struct usb_phy *phy =
+					usb_get_phy(USB_PHY_TYPE_USB3);
+
+				dwc->gadget.b_hnp_enable = 0;
+				dwc->gadget.host_request_flag = 0;
+				otg_start_hnp(phy->otg);
+				usb_put_phy(phy);
+			} else {
+				dwc->gadget.b_hnp_enable = 1;
+			}
+		} else
+			return -EINVAL;
+		break;
+
+	case USB_DEVICE_A_HNP_SUPPORT:
+		/* RH port supports HNP */
+		dev_dbg(dwc->dev,
+			"SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");
+		break;
+
+	case USB_DEVICE_A_ALT_HNP_SUPPORT:
+		/* other RH port does */
+		dev_dbg(dwc->dev,
+			"SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+		break;
+		/*
+		 * 9.4.1 says only only for SS, in AddressState only for
+		 * default control pipe
+		 */
 	case USB_DEVICE_U1_ENABLE:
 		ret = dwc3_ep0_handle_u1(dwc, state, set);
 		break;
@@ -759,7 +795,10 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 
 	switch (ctrl->bRequest) {
 	case USB_REQ_GET_STATUS:
-		ret = dwc3_ep0_handle_status(dwc, ctrl);
+		if (le16_to_cpu(ctrl->wIndex) == OTG_STS_SELECTOR)
+			ret = dwc3_ep0_delegate_req(dwc, ctrl);
+		else
+			ret = dwc3_ep0_handle_status(dwc, ctrl);
 		break;
 	case USB_REQ_CLEAR_FEATURE:
 		ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 6785595..3b0b771 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -34,6 +34,7 @@
 #include "core.h"
 #include "gadget.h"
 #include "io.h"
+#include "otg.h"
 
 /**
  * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
@@ -1792,25 +1793,51 @@ static int dwc3_gadget_start(struct usb_gadget *g,
 	int			ret = 0;
 	int			irq;
 
-	irq = dwc->irq_gadget;
-	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
-			IRQF_SHARED, "dwc3", dwc->ev_buf);
-	if (ret) {
-		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
-				irq, ret);
-		goto err0;
-	}
-
 	spin_lock_irqsave(&dwc->lock, flags);
 	if (dwc->gadget_driver) {
 		dev_err(dwc->dev, "%s is already bound to %s\n",
 				dwc->gadget.name,
 				dwc->gadget_driver->driver.name);
 		ret = -EBUSY;
-		goto err1;
+		goto err0;
 	}
 
-	dwc->gadget_driver	= driver;
+	if (g->is_otg) {
+		static struct usb_gadget_driver *prev_driver;
+		/* There are two instances where OTG functionality is enabled :
+		 * 1. This function will be called from OTG driver to start the
+		 * gadget
+		 * 2. This function will be called by the Linux Class Driver to
+		 * start the gadget
+		 * Below code will keep synchronization between these calls. The
+		 * "driver" argument will be NULL when it is called from the OTG
+		 * driver, so we are maintaning a global variable "prev_driver"
+		 * to assign value of argument "driver" (from class driver) to
+		 * dwc->gadget_driver when it is called from OTG.
+		 */
+		if (driver) {
+			prev_driver	= driver;
+			if (dwc->otg) {
+				struct dwc3_otg		*otg = dwc->otg;
+
+				if ((otg->host_started ||
+						(!otg->peripheral_started)))
+					goto err0;
+			}
+			dwc->gadget_driver	= driver;
+		} else
+			dwc->gadget_driver	= prev_driver;
+	} else
+		dwc->gadget_driver	= driver;
+
+	irq = dwc->irq_gadget;
+	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
+			IRQF_SHARED, "dwc3", dwc->ev_buf);
+	if (ret) {
+		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+				irq, ret);
+		goto err0;
+	}
 
 	if (pm_runtime_active(dwc->dev))
 		__dwc3_gadget_start(dwc);
@@ -1819,11 +1846,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
 
 	return 0;
 
-err1:
-	spin_unlock_irqrestore(&dwc->lock, flags);
-	free_irq(irq, dwc);
-
 err0:
+	dwc->gadget_driver = NULL;
+	spin_unlock_irqrestore(&dwc->lock, flags);
 	return ret;
 }
 
@@ -2977,6 +3002,18 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 
 	dwc->irq_gadget = irq;
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG) {
+		struct usb_phy *phy;
+		/* Switch otg to peripheral mode */
+		phy = usb_get_phy(USB_PHY_TYPE_USB3);
+		if (!IS_ERR(phy)) {
+			if (phy && phy->otg)
+				otg_set_peripheral(phy->otg,
+						(struct usb_gadget *)(long)1);
+			usb_put_phy(phy);
+		}
+	}
+
 	dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
 			&dwc->ctrl_req_addr, GFP_KERNEL);
 	if (!dwc->ctrl_req) {
@@ -3066,6 +3103,26 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 		goto err5;
 	}
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG) {
+		struct usb_phy *phy;
+
+		phy = usb_get_phy(USB_PHY_TYPE_USB3);
+		if (!IS_ERR(phy)) {
+			if (phy && phy->otg) {
+				ret = otg_set_peripheral(phy->otg,
+						&dwc->gadget);
+				if (ret) {
+					usb_put_phy(phy);
+					phy = NULL;
+					goto err5;
+				}
+			} else {
+				usb_put_phy(phy);
+				phy = NULL;
+			}
+		}
+	}
+
 	return 0;
 
 err5:
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
                   ` (2 preceding siblings ...)
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework Manish Narani
@ 2017-01-04 13:22 ` Manish Narani
  2017-01-04 13:32   ` Felipe Balbi
  2017-01-04 13:23 ` [RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller Manish Narani
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch adds support for OTG host mode initialization in DWC3
host driver. Before the host initialization sequence begins. The
driver has to make sure the no OTG peripheral mode is enabled.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/host.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 487f0ff..4caa3fe 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -16,6 +16,8 @@
  */
 
 #include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
 #include "core.h"
 
@@ -111,6 +113,18 @@ int dwc3_host_init(struct dwc3 *dwc)
 	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
 			  dev_name(dwc->dev));
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG) {
+		struct usb_phy *phy;
+		/* Switch otg to host mode */
+		phy = usb_get_phy(USB_PHY_TYPE_USB3);
+		if (!IS_ERR(phy)) {
+			if (phy && phy->otg)
+				otg_set_host(phy->otg,
+						(struct usb_bus *)(long)1);
+			usb_put_phy(phy);
+		}
+	}
+
 	ret = platform_device_add(xhci);
 	if (ret) {
 		dev_err(dwc->dev, "failed to register xHCI device\n");
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
                   ` (3 preceding siblings ...)
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver Manish Narani
@ 2017-01-04 13:23 ` Manish Narani
  2017-01-04 13:23 ` [RFC PATCH] usb: host: xhci: plat: add support for otg_set_host() call Manish Narani
  2017-01-04 13:30 ` [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Felipe Balbi
  6 siblings, 0 replies; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:23 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This driver will add support for USB 2.0 OTG and USB 3.0 DRD in
DWC3 framework.
This OTG driver supports host/peripheral modes on run time. This
driver will enable HNP and SRP support in High-Speed mode.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/otg.c | 2064 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/otg.h |  247 ++++++
 2 files changed, 2311 insertions(+)
 create mode 100644 drivers/usb/dwc3/otg.c
 create mode 100644 drivers/usb/dwc3/otg.h

diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c
new file mode 100644
index 0000000..4f2aa891
--- /dev/null
+++ b/drivers/usb/dwc3/otg.c
@@ -0,0 +1,2064 @@
+/**
+ * otg.c - DesignWare USB3 DRD Controller OTG file
+ *
+ * Copyright (C) 2016 Xilinx, Inc. All rights reserved.
+ *
+ * Author:  Manish Narani <mnarani@xilinx.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+
+#include <../drivers/usb/host/xhci.h>
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+#include "otg.h"
+
+#include <linux/ulpi/regs.h>
+#include <linux/ulpi/driver.h>
+#include "debug.h"
+
+/* Print the hardware registers' value for debugging purpose */
+static void print_debug_regs(struct dwc3_otg *otg)
+{
+	u32 gctl = otg_read(otg, DWC3_GCTL);
+	u32 gsts = otg_read(otg, DWC3_GSTS);
+	u32 gdbgltssm = otg_read(otg, DWC3_GDBGLTSSM);
+	u32 gusb2phycfg0 = otg_read(otg, DWC3_GUSB2PHYCFG(0));
+	u32 gusb3pipectl0 = otg_read(otg, DWC3_GUSB3PIPECTL(0));
+	u32 dcfg = otg_read(otg, DWC3_DCFG);
+	u32 dctl = otg_read(otg, DWC3_DCTL);
+	u32 dsts = otg_read(otg, DWC3_DSTS);
+	u32 ocfg = otg_read(otg, OCFG);
+	u32 octl = otg_read(otg, OCTL);
+	u32 oevt = otg_read(otg, OEVT);
+	u32 oevten = otg_read(otg, OEVTEN);
+	u32 osts = otg_read(otg, OSTS);
+
+	otg_info(otg, "gctl = %08x\n", gctl);
+	otg_info(otg, "gsts = %08x\n", gsts);
+	otg_info(otg, "gdbgltssm = %08x\n", gdbgltssm);
+	otg_info(otg, "gusb2phycfg0 = %08x\n", gusb2phycfg0);
+	otg_info(otg, "gusb3pipectl0 = %08x\n", gusb3pipectl0);
+	otg_info(otg, "dcfg = %08x\n", dcfg);
+	otg_info(otg, "dctl = %08x\n", dctl);
+	otg_info(otg, "dsts = %08x\n", dsts);
+	otg_info(otg, "ocfg = %08x\n", ocfg);
+	otg_info(otg, "octl = %08x\n", octl);
+	otg_info(otg, "oevt = %08x\n", oevt);
+	otg_info(otg, "oevten = %08x\n", oevten);
+	otg_info(otg, "osts = %08x\n", osts);
+}
+
+/* Check whether the hardware supports HNP or not */
+static int hnp_capable(struct dwc3_otg *otg)
+{
+	if (otg->hwparams6 & GHWPARAMS6_HNP_SUPPORT_ENABLED)
+		return 1;
+	return 0;
+}
+
+/* Check whether the hardware supports SRP or not */
+static int srp_capable(struct dwc3_otg *otg)
+{
+	if (otg->hwparams6 & GHWPARAMS6_SRP_SUPPORT_ENABLED)
+		return 1;
+	return 0;
+}
+
+/* Wakeup main thread to execute the OTG flow after an event */
+static void wakeup_main_thread(struct dwc3_otg *otg)
+{
+	if (!otg->main_thread)
+		return;
+
+	otg_vdbg(otg, "\n");
+	/* Tell the main thread that something has happened */
+	otg->main_wakeup_needed = 1;
+	wake_up_interruptible(&otg->main_wq);
+}
+
+/* Sleep main thread for 'msecs' to wait for an event to occur */
+static int sleep_main_thread_timeout(struct dwc3_otg *otg, int msecs)
+{
+	signed long jiffies;
+	int rc = msecs;
+
+	if (signal_pending(current)) {
+		otg_dbg(otg, "Main thread signal pending\n");
+		rc = -EINTR;
+		goto done;
+	}
+	if (otg->main_wakeup_needed) {
+		otg_dbg(otg, "Main thread wakeup needed\n");
+		rc = msecs;
+		goto done;
+	}
+
+	jiffies = msecs_to_jiffies(msecs);
+	rc = wait_event_freezable_timeout(otg->main_wq,
+					  otg->main_wakeup_needed,
+					  jiffies);
+
+	if (rc > 0)
+		rc = jiffies_to_msecs(rc);
+
+done:
+	otg->main_wakeup_needed = 0;
+	return rc;
+}
+
+/* Sleep main thread to wait for an event to occur */
+static int sleep_main_thread(struct dwc3_otg *otg)
+{
+	int rc;
+
+	do {
+		rc = sleep_main_thread_timeout(otg, 5000);
+	} while (rc == 0);
+
+	return rc;
+}
+
+static void get_events(struct dwc3_otg *otg, u32 *otg_events, u32 *user_events)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&otg->lock, flags);
+
+	if (otg_events)
+		*otg_events = otg->otg_events;
+
+	if (user_events)
+		*user_events = otg->user_events;
+
+	spin_unlock_irqrestore(&otg->lock, flags);
+}
+
+static void get_and_clear_events(struct dwc3_otg *otg, u32 *otg_events,
+		u32 *user_events)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&otg->lock, flags);
+
+	if (otg_events)
+		*otg_events = otg->otg_events;
+
+	if (user_events)
+		*user_events = otg->user_events;
+
+	otg->otg_events = 0;
+	otg->user_events = 0;
+
+	spin_unlock_irqrestore(&otg->lock, flags);
+}
+
+static int check_event(struct dwc3_otg *otg, u32 otg_mask, u32 user_mask)
+{
+	u32 otg_events;
+	u32 user_events;
+
+	get_events(otg, &otg_events, &user_events);
+	if ((otg_events & otg_mask) || (user_events & user_mask)) {
+		otg_dbg(otg, "Event occurred: otg_events=%x, otg_mask=%x, ",
+				otg_events, otg_mask);
+		otg_dbg(otg, "user_events=%x, user_mask=%x\n",
+				user_events, user_mask);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int sleep_until_event(struct dwc3_otg *otg, u32 otg_mask, u32 user_mask,
+		u32 *otg_events, u32 *user_events, int timeout)
+{
+	int rc;
+
+	/* Enable the events */
+	if (otg_mask)
+		otg_write(otg, OEVTEN, otg_mask);
+
+	/* Wait until it occurs, or timeout, or interrupt. */
+	if (timeout) {
+		otg_vdbg(otg, "Waiting for event (timeout=%d)...\n", timeout);
+		rc = sleep_main_thread_until_condition_timeout(otg,
+			check_event(otg, otg_mask, user_mask), timeout);
+	} else {
+		otg_vdbg(otg, "Waiting for event (no timeout)...\n");
+		rc = sleep_main_thread_until_condition(otg,
+			check_event(otg, otg_mask, user_mask));
+	}
+
+	/* Disable the events */
+	otg_write(otg, OEVTEN, 0);
+
+	otg_vdbg(otg, "Woke up rc=%d\n", rc);
+	if (rc >= 0)
+		get_and_clear_events(otg, otg_events, user_events);
+
+	return rc;
+}
+
+static void set_capabilities(struct dwc3_otg *otg)
+{
+	u32 ocfg = 0;
+
+	otg_dbg(otg, "\n");
+	if (srp_capable(otg))
+		ocfg |= OCFG_SRP_CAP;
+
+	if (hnp_capable(otg))
+		ocfg |= OCFG_HNP_CAP;
+
+	otg_write(otg, OCFG, ocfg);
+
+	otg_dbg(otg, "Enabled SRP and HNP capabilities in OCFG\n");
+}
+
+static int otg3_handshake(struct dwc3_otg *otg, u32 reg, u32 mask, u32 done,
+		u32 msec)
+{
+	u32 result;
+	u32 usec = msec * 1000;
+
+	otg_vdbg(otg, "reg=%08x, mask=%08x, value=%08x\n", reg, mask, done);
+	do {
+		result = otg_read(otg, reg);
+		if ((result & mask) == done)
+			return 1;
+		udelay(1);
+		usec -= 1;
+	} while (usec > 0);
+
+	return 0;
+}
+
+static int reset_port(struct dwc3_otg *otg)
+{
+	otg_dbg(otg, "\n");
+	if (!otg->otg.host)
+		return -ENODEV;
+	return usb_bus_start_enum(otg->otg.host, 1);
+}
+
+static int set_peri_mode(struct dwc3_otg *otg, int mode)
+{
+	u32 octl;
+
+	/* Set peri_mode */
+	octl = otg_read(otg, OCTL);
+	if (mode)
+		octl |= OCTL_PERI_MODE;
+	else
+		octl &= ~OCTL_PERI_MODE;
+
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set OCTL PERI_MODE = %d in OCTL\n", mode);
+
+	if (mode)
+		return otg3_handshake(otg, OSTS, OSTS_PERIP_MODE,
+				OSTS_PERIP_MODE, 100);
+	else
+		return otg3_handshake(otg, OSTS, OSTS_PERIP_MODE, 0, 100);
+
+	msleep(20);
+}
+
+static int start_host(struct dwc3_otg *otg)
+{
+	int ret = -ENODEV;
+	int flg;
+	u32 octl;
+	u32 osts;
+	u32 dctl;
+	u32 event_addr;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host)
+		return -ENODEV;
+
+	dctl = otg_read(otg, DCTL);
+	if (dctl & DWC3_DCTL_RUN_STOP) {
+		otg_dbg(otg, "Disabling the RUN/STOP bit\n");
+		dctl &= ~DWC3_DCTL_RUN_STOP;
+		otg_write(otg, DCTL, dctl);
+	}
+
+	event_addr = dwc3_readl(otg->dwc->regs, DWC3_GEVNTADRLO(0));
+	if (event_addr != 0x0) {
+		otg_dbg(otg, "Freeing the device event buffers\n");
+		dwc3_free_event_buffers(otg->dwc);
+	}
+
+	if (!set_peri_mode(otg, PERI_MODE_HOST)) {
+		otg_err(otg, "Failed to start host\n");
+		return -EINVAL;
+	}
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	otg_dbg(otg, "hcd=%p xhci=%p\n", hcd, xhci);
+
+	if (otg->host_started) {
+		otg_info(otg, "Host already started\n");
+		goto skip;
+	}
+
+	/* Start host driver */
+
+	*(struct xhci_hcd **)hcd->hcd_priv = xhci;
+	otg_dbg(otg, "1- calling usb_add_hcd() irq=%d\n", otg->hcd_irq);
+	ret = usb_add_hcd(hcd, otg->hcd_irq, IRQF_SHARED);
+	if (ret) {
+		otg_err(otg, "%s: failed to start primary hcd, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	*(struct xhci_hcd **)xhci->shared_hcd->hcd_priv = xhci;
+	if (xhci->shared_hcd) {
+		otg_dbg(otg, "2- calling usb_add_hcd() irq=%d\n", otg->hcd_irq);
+		ret = usb_add_hcd(xhci->shared_hcd, otg->hcd_irq, IRQF_SHARED);
+		if (ret) {
+			otg_err(otg,
+				"%s: failed to start secondary hcd, ret=%d\n",
+				__func__, ret);
+			usb_remove_hcd(hcd);
+			return ret;
+		}
+	}
+
+	otg->host_started = 1;
+skip:
+	hcd->self.otg_port = 1;
+	if (xhci->shared_hcd)
+		xhci->shared_hcd->self.otg_port = 1;
+
+	set_capabilities(otg);
+
+	/* Power the port only for A-host */
+	if (otg->otg.state == OTG_STATE_A_WAIT_VRISE) {
+		/* Spin on xhciPrtPwr bit until it becomes 1 */
+		osts = otg_read(otg, OSTS);
+		flg = otg3_handshake(otg, OSTS,
+				OSTS_XHCI_PRT_PWR,
+				OSTS_XHCI_PRT_PWR,
+				1000);
+		if (flg) {
+			otg_dbg(otg, "Port is powered by xhci-hcd\n");
+			/* Set port power control bit */
+			octl = otg_read(otg, OCTL);
+			octl |= OCTL_PRT_PWR_CTL;
+			otg_write(otg, OCTL, octl);
+		} else {
+			otg_dbg(otg, "Port is not powered by xhci-hcd\n");
+		}
+	}
+
+	return ret;
+}
+
+static int stop_host(struct dwc3_otg *otg)
+{
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "\n");
+
+	if (!otg->host_started) {
+		otg_info(otg, "Host already stopped\n");
+		return 1;
+	}
+
+	if (!otg->otg.host)
+		return -ENODEV;
+
+	otg_dbg(otg, "%s: turn off host %s\n",
+		__func__, otg->otg.host->bus_name);
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+
+	if (xhci->shared_hcd)
+		usb_remove_hcd(xhci->shared_hcd);
+	usb_remove_hcd(hcd);
+
+	otg->host_started = 0;
+	otg->dev_enum = 0;
+	return 0;
+}
+
+int dwc3_otg_host_release(struct usb_hcd *hcd)
+{
+	struct usb_bus *bus;
+	struct usb_device *rh;
+	struct usb_device *udev;
+
+	if (!hcd)
+		return -EINVAL;
+
+	bus = &hcd->self;
+	if (!bus->otg_port)
+		return 0;
+
+	rh = bus->root_hub;
+	udev = usb_hub_find_child(rh, bus->otg_port);
+	if (!udev)
+		return 0;
+
+	if (udev->config && udev->parent == udev->bus->root_hub) {
+		struct usb_otg20_descriptor *desc;
+
+		if (__usb_get_extra_descriptor(udev->rawdescriptors[0],
+				le16_to_cpu(udev->config[0].desc.wTotalLength),
+				USB_DT_OTG, (void **) &desc) == 0) {
+			int err;
+
+			dev_info(&udev->dev, "found OTG descriptor\n");
+			if ((desc->bcdOTG >= 0x0200) &&
+			    (udev->speed == USB_SPEED_HIGH)) {
+				err = usb_control_msg(udev,
+						usb_sndctrlpipe(udev, 0),
+						USB_REQ_SET_FEATURE, 0,
+						USB_DEVICE_TEST_MODE,
+						7 << 8,
+						NULL, 0, USB_CTRL_SET_TIMEOUT);
+				if (err < 0) {
+					dev_info(&udev->dev,
+						"can't initiate HNP from host: %d\n",
+						err);
+					return -1;
+				}
+			}
+		} else {
+			dev_info(&udev->dev, "didn't find OTG descriptor\n");
+		}
+	} else {
+		dev_info(&udev->dev,
+			 "udev->config NULL or udev->parent != udev->bus->root_hub\n");
+	}
+
+	return 0;
+}
+
+/* Sends the host release set feature request */
+static void host_release(struct dwc3_otg *otg)
+{
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "\n");
+	if (!otg->otg.host)
+		return;
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	dwc3_otg_host_release(hcd);
+	if (xhci->shared_hcd)
+		dwc3_otg_host_release(xhci->shared_hcd);
+}
+
+static void start_peripheral(struct dwc3_otg *otg)
+{
+	struct usb_gadget *gadget = otg->otg.gadget;
+	u32 event_addr;
+
+	otg_dbg(otg, "\n");
+	if (!gadget)
+		return;
+
+	if (!set_peri_mode(otg, PERI_MODE_PERIPHERAL))
+		otg_err(otg, "Failed to set peripheral mode\n");
+
+	if (otg->peripheral_started) {
+		otg_info(otg, "Peripheral already started\n");
+		return;
+	}
+
+	event_addr = dwc3_readl(otg->dwc->regs, DWC3_GEVNTADRLO(0));
+	if (event_addr == 0x0) {
+		int ret;
+
+		otg_dbg(otg, "allocating the event buffer\n");
+		ret = dwc3_alloc_event_buffers(otg->dwc,
+				DWC3_EVENT_BUFFERS_SIZE);
+		if (ret) {
+			dev_err(otg->dwc->dev,
+					"failed to allocate event buffers\n");
+		}
+		otg_dbg(otg, "setting up event buffers\n");
+		dwc3_event_buffers_setup(otg->dwc);
+	}
+
+	otg->peripheral_started = 1;
+
+	gadget->ops->udc_start(gadget, NULL);
+
+	gadget->b_hnp_enable = 0;
+	gadget->host_request_flag = 0;
+
+	otg_write(otg, DCTL, otg_read(otg, DCTL) | DCTL_RUN_STOP);
+	otg_dbg(otg, "Setting DCTL_RUN_STOP to 1 in DCTL\n");
+
+	msleep(20);
+}
+
+static void stop_peripheral(struct dwc3_otg *otg)
+{
+	struct usb_gadget *gadget = otg->otg.gadget;
+
+	otg_dbg(otg, "\n");
+
+	if (!otg->peripheral_started) {
+		otg_info(otg, "Peripheral already stopped\n");
+		return;
+	}
+
+	if (!gadget)
+		return;
+
+	gadget->ops->udc_stop(gadget);
+	otg->peripheral_started = 0;
+	msleep(20);
+}
+
+static void set_b_host(struct dwc3_otg *otg, int val)
+{
+	otg->otg.host->is_b_host = val;
+}
+
+static enum usb_otg_state do_b_idle(struct dwc3_otg *otg);
+
+static int init_b_device(struct dwc3_otg *otg)
+{
+	otg_dbg(otg, "\n");
+	set_capabilities(otg);
+
+	if (!set_peri_mode(otg, PERI_MODE_PERIPHERAL))
+		otg_err(otg, "Failed to start peripheral\n");
+
+	return do_b_idle(otg);
+}
+
+static int init_a_device(struct dwc3_otg *otg)
+{
+	otg_write(otg, OCFG, 0);
+	otg_write(otg, OCTL, 0);
+
+	otg_dbg(otg, "Write 0 to OCFG and OCTL\n");
+	return OTG_STATE_A_IDLE;
+}
+
+static enum usb_otg_state do_connector_id_status(struct dwc3_otg *otg)
+{
+	enum usb_otg_state state;
+	u32 osts;
+
+	otg_dbg(otg, "\n");
+
+	otg_write(otg, OCFG, 0);
+	otg_write(otg, OEVTEN, 0);
+	otg_write(otg, OEVT, 0xffffffff);
+	otg_write(otg, OEVTEN, OEVT_CONN_ID_STS_CHNG_EVNT);
+
+	msleep(60);
+
+	osts = otg_read(otg, OSTS);
+	if (!(osts & OSTS_CONN_ID_STS)) {
+		otg_dbg(otg, "Connector ID is A\n");
+		state = init_a_device(otg);
+	} else {
+		otg_dbg(otg, "Connector ID is B\n");
+		stop_host(otg);
+		state = init_b_device(otg);
+	}
+
+	/* TODO: This is a workaround for latest hibernation-enabled bitfiles
+	 * which have problems before initializing SRP.
+	 */
+	msleep(50);
+
+	return state;
+}
+
+static void reset_hw(struct dwc3_otg *otg)
+{
+	u32 temp;
+
+	otg_dbg(otg, "\n");
+
+	otg_write(otg, OEVTEN, 0);
+	temp = otg_read(otg, OCTL);
+	temp &= OCTL_PERI_MODE;
+	otg_write(otg, OCTL, temp);
+	temp = otg_read(otg, GCTL);
+	temp |= GCTL_PRT_CAP_DIR_OTG << GCTL_PRT_CAP_DIR_SHIFT;
+	otg_write(otg, GCTL, temp);
+}
+
+#define SRP_TIMEOUT			6000
+
+static void start_srp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl |= OCTL_SES_REQ;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set OCTL_SES_REQ in OCTL\n");
+}
+
+static void start_b_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl |= OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set (OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN) in OCTL\n");
+}
+
+static void stop_b_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl &= ~(OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN);
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "Clear ~(OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN) in OCTL\n");
+}
+
+static void start_a_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl |= OCTL_HST_SET_HNP_EN;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set OCTL_HST_SET_HNP_EN in OCTL\n");
+}
+
+static void stop_a_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl &= ~OCTL_HST_SET_HNP_EN;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "clear OCTL_HST_SET_HNP_EN in OCTL\n");
+}
+
+static enum usb_otg_state do_a_hnp_init(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_HNP_CHNG_EVNT;
+
+	start_a_hnp(otg);
+	rc = 3000;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	stop_a_hnp(otg);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_HNP_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_HNP_CHNG_EVNT\n");
+		if (otg_events & OEVT_HST_NEG_SCS) {
+			otg_dbg(otg, "A-HNP Success\n");
+			return OTG_STATE_A_PERIPHERAL;
+
+		} else {
+			otg_dbg(otg, "A-HNP Failed\n");
+			return OTG_STATE_A_WAIT_VFALL;
+		}
+
+	} else if (rc == 0) {
+		otg_dbg(otg, "A-HNP Failed (Timed out)\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else {
+		goto again;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_a_host(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT;
+	user_mask = USER_SRP_EVENT |
+		USER_HNP_EVENT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (user_events & USER_HNP_EVENT) {
+		otg_dbg(otg, "USER_HNP_EVENT\n");
+		return OTG_STATE_A_SUSPEND;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+#define A_WAIT_VFALL_TIMEOUT 1000
+
+static enum usb_otg_state do_a_wait_vfall(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_A_DEV_IDLE_EVNT;
+
+	rc = A_WAIT_VFALL_TIMEOUT;
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_A_DEV_IDLE_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_IDLE_EVNT\n");
+		return OTG_STATE_A_IDLE;
+
+	} else if (rc == 0) {
+		otg_dbg(otg, "A_WAIT_VFALL_TIMEOUT\n");
+		return OTG_STATE_A_IDLE;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+
+}
+
+#define A_WAIT_BCON_TIMEOUT 1000
+
+static enum usb_otg_state do_a_wait_bconn(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT |
+		OEVT_A_DEV_HOST_EVNT;
+
+	rc = A_WAIT_BCON_TIMEOUT;
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (otg_events & OEVT_A_DEV_HOST_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_HOST_EVNT\n");
+		return OTG_STATE_A_HOST;
+
+	} else if (rc == 0) {
+		if (otg_read(otg, OCTL) & OCTL_PRT_PWR_CTL)
+			return OTG_STATE_A_HOST;
+		else
+			return OTG_STATE_A_WAIT_VFALL;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+#define A_WAIT_VRISE_TIMEOUT 100
+
+static enum usb_otg_state do_a_wait_vrise(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "");
+	set_b_host(otg, 0);
+	start_host(otg);
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	usb_kick_hub_wq(hcd->self.root_hub);
+	if (xhci->shared_hcd)
+		usb_kick_hub_wq(xhci->shared_hcd->self.root_hub);
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT;
+
+	rc = A_WAIT_VRISE_TIMEOUT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (rc == 0) {
+		if (otg_read(otg, OCTL) & OCTL_PRT_PWR_CTL)
+			return OTG_STATE_A_WAIT_BCON;
+		else
+			return OTG_STATE_A_WAIT_VFALL;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_a_idle(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_A_DEV_SRP_DET_EVNT;
+	user_mask = USER_SRP_EVENT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events,
+			0);
+
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_A_DEV_SRP_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SRP_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VRISE;
+	} else if (user_events & USER_SRP_EVENT) {
+		otg_dbg(otg, "User initiated VBUS\n");
+		return OTG_STATE_A_WAIT_VRISE;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_a_peripheral(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT |
+		OEVT_A_DEV_B_DEV_HOST_END_EVNT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (otg_events & OEVT_A_DEV_B_DEV_HOST_END_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_B_DEV_HOST_END_EVNT\n");
+		return OTG_STATE_A_WAIT_VRISE;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+#define HNP_TIMEOUT	4000
+
+static enum usb_otg_state do_b_hnp_init(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_HNP_CHNG_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT;
+
+	start_b_hnp(otg);
+	rc = HNP_TIMEOUT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&events, NULL, rc);
+	stop_b_hnp(otg);
+
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		return OTG_STATE_B_IDLE;
+	} else if (events & OEVT_B_DEV_HNP_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_HNP_CHNG_EVNT\n");
+		if (events & OEVT_HST_NEG_SCS) {
+			otg_dbg(otg, "B-HNP Success\n");
+			return OTG_STATE_B_WAIT_ACON;
+
+		} else {
+			otg_err(otg, "B-HNP Failed\n");
+			return OTG_STATE_B_PERIPHERAL;
+		}
+	} else if (rc == 0) {
+		/* Timeout */
+		otg_err(otg, "HNP timed out!\n");
+		return OTG_STATE_B_PERIPHERAL;
+
+	} else {
+		goto again;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_peripheral(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_B_DEV_VBUS_CHNG_EVNT;
+	user_mask = USER_HNP_EVENT | USER_END_SESSION |
+		USER_SRP_EVENT | INITIAL_SRP;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			goto again;
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			return OTG_STATE_B_IDLE;
+		}
+
+	} else if (user_events & USER_HNP_EVENT) {
+		otg_dbg(otg, "USER_HNP_EVENT\n");
+		return do_b_hnp_init(otg);
+	} else if (user_events & USER_END_SESSION) {
+		otg_dbg(otg, "USER_END_SESSION\n");
+		return OTG_STATE_B_IDLE;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_wait_acon(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask = 0;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "");
+	set_b_host(otg, 1);
+	start_host(otg);
+	otg_mask = OEVT_B_DEV_B_HOST_END_EVNT;
+	otg_write(otg, OEVTEN, otg_mask);
+	reset_port(otg);
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	usb_kick_hub_wq(hcd->self.root_hub);
+	if (xhci->shared_hcd)
+		usb_kick_hub_wq(xhci->shared_hcd->self.root_hub);
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_B_HOST_END_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT |
+		OEVT_HOST_ROLE_REQ_INIT_EVNT;
+	user_mask = USER_A_CONN_EVENT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_B_DEV_B_HOST_END_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_B_HOST_END_EVNT\n");
+		return OTG_STATE_B_PERIPHERAL;
+	} else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			goto again;
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			return OTG_STATE_B_IDLE;
+		}
+	} else if (user_events & USER_A_CONN_EVENT) {
+		otg_dbg(otg, "A-device connected\n");
+		return OTG_STATE_B_HOST;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_host(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask = 0;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_B_HOST_END_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT |
+		OEVT_HOST_ROLE_REQ_INIT_EVNT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_B_DEV_B_HOST_END_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_B_HOST_END_EVNT\n");
+		return OTG_STATE_B_PERIPHERAL;
+	} else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			goto again;
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			return OTG_STATE_B_IDLE;
+		}
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_idle(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_SES_VLD_DET_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT;
+	user_mask = USER_SRP_EVENT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if ((otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) ||
+		(otg_events & OEVT_B_DEV_SES_VLD_DET_EVNT)) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			return OTG_STATE_B_PERIPHERAL;
+
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			goto again;
+		}
+	} else if (user_events & USER_SRP_EVENT) {
+		otg_dbg(otg, "USER_SRP_EVENT\n");
+		return OTG_STATE_B_SRP_INIT;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_srp_init(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_SES_VLD_DET_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT;
+
+	otg_write(otg, OEVTEN, otg_mask);
+	start_srp(otg);
+
+	rc = SRP_TIMEOUT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (events & OEVT_B_DEV_SES_VLD_DET_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_SES_VLD_DET_EVNT\n");
+		return OTG_STATE_B_PERIPHERAL;
+	} else if (rc == 0) {
+		otg_dbg(otg, "SRP Timeout (rc=%d)\n", rc);
+		otg_info(otg, "DEVICE NO RESPONSE FOR SRP\n");
+		return OTG_STATE_B_IDLE;
+
+	} else {
+		goto again;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+int otg_main_thread(void *data)
+{
+	struct dwc3_otg *otg = (struct dwc3_otg *)data;
+	enum usb_otg_state prev = OTG_STATE_UNDEFINED;
+
+#ifdef VERBOSE_DEBUG
+	u32 snpsid = otg_read(otg, 0xc120);
+
+	otg_vdbg(otg, "io_priv=%p\n", otg->regs);
+	otg_vdbg(otg, "c120: %x\n", snpsid);
+#endif
+
+	/* Allow the thread to be killed by a signal, but set the signal mask
+	 * to block everything but INT, TERM, KILL, and USR1.
+	 */
+	allow_signal(SIGINT);
+	allow_signal(SIGTERM);
+	allow_signal(SIGKILL);
+	allow_signal(SIGUSR1);
+
+	/* Allow the thread to be frozen */
+	set_freezable();
+
+	/* Allow host/peripheral driver load to finish */
+	msleep(100);
+
+	reset_hw(otg);
+
+	stop_host(otg);
+	stop_peripheral(otg);
+
+	otg_dbg(otg, "Thread running\n");
+	while (1) {
+		enum usb_otg_state next = OTG_STATE_UNDEFINED;
+
+		otg_vdbg(otg, "\n\n\nMain thread entering state\n");
+
+		switch (otg->otg.state) {
+		case OTG_STATE_UNDEFINED:
+			otg_dbg(otg, "OTG_STATE_UNDEFINED\n");
+			next = do_connector_id_status(otg);
+			break;
+
+		case OTG_STATE_A_IDLE:
+			otg_dbg(otg, "OTG_STATE_A_IDLE\n");
+			stop_peripheral(otg);
+
+			if (prev == OTG_STATE_UNDEFINED)
+				next = OTG_STATE_A_WAIT_VRISE;
+			else
+				next = do_a_idle(otg);
+			break;
+
+		case OTG_STATE_A_WAIT_VRISE:
+			otg_dbg(otg, "OTG_STATE_A_WAIT_VRISE\n");
+			next = do_a_wait_vrise(otg);
+			break;
+
+		case OTG_STATE_A_WAIT_BCON:
+			otg_dbg(otg, "OTG_STATE_A_WAIT_BCON\n");
+			next = do_a_wait_bconn(otg);
+			break;
+
+		case OTG_STATE_A_HOST:
+			otg_dbg(otg, "OTG_STATE_A_HOST\n");
+			stop_peripheral(otg);
+			next = do_a_host(otg);
+			/* Don't stop the host here if we are going into
+			 * A_SUSPEND. We need to delay that until later. It
+			 * will be stopped when coming out of A_SUSPEND
+			 * state.
+			 */
+			if (next != OTG_STATE_A_SUSPEND)
+				stop_host(otg);
+			break;
+
+		case OTG_STATE_A_SUSPEND:
+			otg_dbg(otg, "OTG_STATE_A_SUSPEND\n");
+			next = do_a_hnp_init(otg);
+
+			/* Stop the host. */
+			stop_host(otg);
+			break;
+
+		case OTG_STATE_A_WAIT_VFALL:
+			otg_dbg(otg, "OTG_STATE_A_WAIT_VFALL\n");
+			next = do_a_wait_vfall(otg);
+			stop_host(otg);
+			break;
+
+		case OTG_STATE_A_PERIPHERAL:
+			otg_dbg(otg, "OTG_STATE_A_PERIPHERAL\n");
+			stop_host(otg);
+			start_peripheral(otg);
+			next = do_a_peripheral(otg);
+			stop_peripheral(otg);
+			break;
+
+		case OTG_STATE_B_IDLE:
+			otg_dbg(otg, "OTG_STATE_B_IDLE\n");
+			next = do_b_idle(otg);
+			break;
+
+		case OTG_STATE_B_PERIPHERAL:
+			otg_dbg(otg, "OTG_STATE_B_PERIPHERAL\n");
+			stop_host(otg);
+			start_peripheral(otg);
+			next = do_b_peripheral(otg);
+			stop_peripheral(otg);
+			break;
+
+		case OTG_STATE_B_SRP_INIT:
+			otg_dbg(otg, "OTG_STATE_B_SRP_INIT\n");
+			otg_read(otg, OSTS);
+			next = do_b_srp_init(otg);
+			break;
+
+		case OTG_STATE_B_WAIT_ACON:
+			otg_dbg(otg, "OTG_STATE_B_WAIT_ACON\n");
+			next = do_b_wait_acon(otg);
+			break;
+
+		case OTG_STATE_B_HOST:
+			otg_dbg(otg, "OTG_STATE_B_HOST\n");
+			next = do_b_host(otg);
+			stop_host(otg);
+			break;
+
+		default:
+			otg_err(otg, "Unknown state %d, sleeping...\n",
+					otg->state);
+			sleep_main_thread(otg);
+			break;
+		}
+
+		prev = otg->otg.state;
+		otg->otg.state = next;
+		if (kthread_should_stop())
+			break;
+	}
+
+	otg->main_thread = NULL;
+	otg_dbg(otg, "OTG main thread exiting....\n");
+
+	return 0;
+}
+
+static void start_main_thread(struct dwc3_otg *otg)
+{
+	if (!otg->main_thread && otg->otg.gadget && otg->otg.host) {
+		otg_dbg(otg, "Starting OTG main thread\n");
+		otg->main_thread = kthread_create(otg_main_thread, otg, "otg");
+		wake_up_process(otg->main_thread);
+	}
+}
+
+static inline struct dwc3_otg *otg_to_dwc3_otg(struct usb_otg *x)
+{
+	return container_of(x, struct dwc3_otg, otg);
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_otg)
+{
+	struct dwc3_otg *otg;
+	u32 oevt;
+	u32 osts;
+	u32 octl;
+	u32 ocfg;
+	u32 oevten;
+	u32 otg_mask = OEVT_ALL;
+
+	if (!_otg)
+		return 0;
+
+	otg = (struct dwc3_otg *)_otg;
+
+	oevt = otg_read(otg, OEVT);
+	osts = otg_read(otg, OSTS);
+	octl = otg_read(otg, OCTL);
+	ocfg = otg_read(otg, OCFG);
+	oevten = otg_read(otg, OEVTEN);
+
+	/* Clear handled events */
+	otg_write(otg, OEVT, oevt);
+
+	otg_vdbg(otg, "\n");
+	otg_vdbg(otg, "    oevt = %08x\n", oevt);
+	otg_vdbg(otg, "    osts = %08x\n", osts);
+	otg_vdbg(otg, "    octl = %08x\n", octl);
+	otg_vdbg(otg, "    ocfg = %08x\n", ocfg);
+	otg_vdbg(otg, "  oevten = %08x\n", oevten);
+
+	otg_vdbg(otg, "oevt[DeviceMode] = %s\n",
+			oevt & OEVT_DEV_MOD_EVNT ? "Device" : "Host");
+
+	if (oevt & OEVT_CONN_ID_STS_CHNG_EVNT)
+		otg_dbg(otg, "Connector ID Status Change Event\n");
+	if (oevt & OEVT_HOST_ROLE_REQ_INIT_EVNT)
+		otg_dbg(otg, "Host Role Request Init Notification Event\n");
+	if (oevt & OEVT_HOST_ROLE_REQ_CONFIRM_EVNT)
+		otg_dbg(otg, "Host Role Request Confirm Notification Event\n");
+	if (oevt & OEVT_A_DEV_B_DEV_HOST_END_EVNT)
+		otg_dbg(otg, "A-Device B-Host End Event\n");
+	if (oevt & OEVT_A_DEV_HOST_EVNT)
+		otg_dbg(otg, "A-Device Host Event\n");
+	if (oevt & OEVT_A_DEV_HNP_CHNG_EVNT)
+		otg_dbg(otg, "A-Device HNP Change Event\n");
+	if (oevt & OEVT_A_DEV_SRP_DET_EVNT)
+		otg_dbg(otg, "A-Device SRP Detect Event\n");
+	if (oevt & OEVT_A_DEV_SESS_END_DET_EVNT)
+		otg_dbg(otg, "A-Device Session End Detected Event\n");
+	if (oevt & OEVT_B_DEV_B_HOST_END_EVNT)
+		otg_dbg(otg, "B-Device B-Host End Event\n");
+	if (oevt & OEVT_B_DEV_HNP_CHNG_EVNT)
+		otg_dbg(otg, "B-Device HNP Change Event\n");
+	if (oevt & OEVT_B_DEV_SES_VLD_DET_EVNT)
+		otg_dbg(otg, "B-Device Session Valid Detect Event\n");
+	if (oevt & OEVT_B_DEV_VBUS_CHNG_EVNT)
+		otg_dbg(otg, "B-Device VBUS Change Event\n");
+
+	if (oevt & otg_mask) {
+		/* Pass event to main thread */
+		spin_lock(&otg->lock);
+		otg->otg_events |= oevt;
+		wakeup_main_thread(otg);
+		spin_unlock(&otg->lock);
+		return 1;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void hnp_polling_work(struct work_struct *w)
+{
+	struct dwc3_otg *otg = container_of(w, struct dwc3_otg,
+			hp_work.work);
+	struct usb_bus *bus;
+	struct usb_device *udev;
+	struct usb_hcd *hcd;
+	u8 *otgstatus;
+	int ret;
+	int err;
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	if (!hcd)
+		return;
+
+	bus = &hcd->self;
+	if (!bus->otg_port)
+		return;
+
+	udev = usb_hub_find_child(bus->root_hub, bus->otg_port);
+	if (!udev)
+		return;
+
+	otgstatus = kmalloc(sizeof(*otgstatus), GFP_NOIO);
+	if (!otgstatus)
+		return;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+			0, 0xf000, otgstatus, sizeof(*otgstatus),
+			USB_CTRL_GET_TIMEOUT);
+
+	if (ret == sizeof(*otgstatus) && (*otgstatus & 0x1)) {
+		/* enable HNP before suspend, it's simpler */
+
+		udev->bus->b_hnp_enable = 1;
+		err = usb_control_msg(udev,
+				usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, 0,
+				udev->bus->b_hnp_enable
+				? USB_DEVICE_B_HNP_ENABLE
+				: USB_DEVICE_A_ALT_HNP_SUPPORT,
+				0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+		if (err < 0) {
+			/* OTG MESSAGE: report errors here,
+			 * customize to match your product.
+			 */
+			otg_info(otg, "ERROR : Device no response\n");
+			dev_info(&udev->dev, "can't set HNP mode: %d\n",
+					err);
+			udev->bus->b_hnp_enable = 0;
+			if (le16_to_cpu(udev->descriptor.idVendor) == 0x1a0a) {
+				if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND)
+						< 0)
+					dev_dbg(&udev->dev, "HNP fail, %d\n",
+							err);
+			}
+		} else {
+			/* Device wants role-switch, suspend the bus. */
+			static struct usb_phy *phy;
+
+			phy = usb_get_phy(USB_PHY_TYPE_USB3);
+			otg_start_hnp(phy->otg);
+			usb_put_phy(phy);
+
+			if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) < 0)
+				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+		}
+	} else if (ret < 0) {
+		udev->bus->b_hnp_enable = 1;
+		err = usb_control_msg(udev,
+				usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, 0,
+				USB_DEVICE_B_HNP_ENABLE,
+				0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+		if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) < 0)
+			dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+	} else {
+		schedule_delayed_work(&otg->hp_work, 1 * HZ);
+	}
+
+	kfree(otgstatus);
+}
+
+static int dwc3_otg_notify_connect(struct usb_phy *phy,
+		enum usb_device_speed speed)
+{
+	struct usb_bus *bus;
+	struct usb_device *udev;
+	struct usb_hcd *hcd;
+	struct dwc3_otg *otg;
+	int err = 0;
+
+	otg = otg_to_dwc3_otg(phy->otg);
+
+	hcd = container_of(phy->otg->host, struct usb_hcd, self);
+	if (!hcd)
+		return -EINVAL;
+
+	bus = &hcd->self;
+	if (!bus->otg_port)
+		return 0;
+
+	udev = usb_hub_find_child(bus->root_hub, bus->otg_port);
+	if (!udev)
+		return 0;
+
+	/*
+	 * OTG-aware devices on OTG-capable root hubs may be able to use SRP,
+	 * to wake us after we've powered off VBUS; and HNP, switching roles
+	 * "host" to "peripheral".  The OTG descriptor helps figure this out.
+	 */
+	if (udev->config && udev->parent == udev->bus->root_hub) {
+		struct usb_otg20_descriptor	*desc = NULL;
+
+		/* descriptor may appear anywhere in config */
+		err = __usb_get_extra_descriptor(udev->rawdescriptors[0],
+				le16_to_cpu(udev->config[0].desc.wTotalLength),
+				USB_DT_OTG, (void **) &desc);
+		if (err || !(desc->bmAttributes & USB_OTG_HNP))
+			return 0;
+
+		if (udev->portnum == udev->bus->otg_port) {
+			INIT_DELAYED_WORK(&otg->hp_work,
+					hnp_polling_work);
+			schedule_delayed_work(&otg->hp_work, HZ);
+		}
+
+	}
+
+	return err;
+}
+
+static int dwc3_otg_notify_disconnect(struct usb_phy *phy,
+		enum usb_device_speed speed)
+{
+	struct dwc3_otg *otg;
+
+	otg = otg_to_dwc3_otg(phy->otg);
+
+	if (work_pending(&otg->hp_work.work)) {
+		while (!cancel_delayed_work(&otg->hp_work))
+			msleep(20);
+	}
+	return 0;
+}
+
+static void dwc3_otg_set_peripheral(struct usb_otg *_otg, int yes)
+{
+	struct dwc3_otg *otg;
+
+	if (!_otg)
+		return;
+
+	otg = otg_to_dwc3_otg(_otg);
+	otg_dbg(otg, "\n");
+
+	if (yes) {
+		if (otg->hwparams6 == 0xdeadbeef)
+			otg->hwparams6 = otg_read(otg, GHWPARAMS6);
+		stop_host(otg);
+	} else {
+		stop_peripheral(otg);
+	}
+
+	set_peri_mode(otg, yes);
+}
+EXPORT_SYMBOL(dwc3_otg_set_peripheral);
+
+static int dwc3_otg_set_periph(struct usb_otg *_otg, struct usb_gadget *gadget)
+{
+	struct dwc3_otg *otg;
+
+	if (!_otg)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(_otg);
+	otg_dbg(otg, "\n");
+
+	if ((long)gadget == 1) {
+		dwc3_otg_set_peripheral(_otg, 1);
+		return 0;
+	}
+
+	if (!gadget) {
+		otg->otg.gadget = NULL;
+		return -ENODEV;
+	}
+
+	otg->otg.gadget = gadget;
+	otg->otg.gadget->hnp_polling_support = 1;
+	otg->otg.state = OTG_STATE_B_IDLE;
+
+	start_main_thread(otg);
+	return 0;
+}
+
+static int dwc3_otg_set_host(struct usb_otg *_otg, struct usb_bus *host)
+{
+	struct dwc3_otg *otg;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	if (!_otg)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(_otg);
+	otg_dbg(otg, "\n");
+
+	if ((long)host == 1) {
+		dwc3_otg_set_peripheral(_otg, 0);
+		return 0;
+	}
+
+	if (!host) {
+		otg->otg.host = NULL;
+		otg->hcd_irq = 0;
+		return -ENODEV;
+	}
+
+	hcd = container_of(host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	otg_dbg(otg, "hcd=%p xhci=%p\n", hcd, xhci);
+
+	hcd->self.otg_port = 1;
+	if (xhci->shared_hcd) {
+		xhci->shared_hcd->self.otg_port = 1;
+		otg_dbg(otg, "shared_hcd=%p\n", xhci->shared_hcd);
+	}
+
+	otg->otg.host = host;
+	otg->hcd_irq = hcd->irq;
+	otg_dbg(otg, "host=%p irq=%d\n", otg->otg.host, otg->hcd_irq);
+
+
+	otg->host_started = 1;
+	otg->dev_enum = 0;
+	start_main_thread(otg);
+	return 0;
+}
+
+static int dwc3_otg_start_srp(struct usb_otg *x)
+{
+	unsigned long flags;
+	struct dwc3_otg *otg;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= USER_SRP_EVENT;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+static int dwc3_otg_start_hnp(struct usb_otg *x)
+{
+	unsigned long flags;
+	struct dwc3_otg *otg;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= USER_HNP_EVENT;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+static int dwc3_otg_end_session(struct usb_otg *x)
+{
+	unsigned long flags;
+	struct dwc3_otg *otg;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= USER_END_SESSION;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+static int otg_end_session(struct usb_otg *otg)
+{
+	return dwc3_otg_end_session(otg);
+}
+EXPORT_SYMBOL(otg_end_session);
+
+static int dwc3_otg_received_host_release(struct usb_otg *x)
+{
+	struct dwc3_otg *otg;
+	unsigned long flags;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= PCD_RECEIVED_HOST_RELEASE_EVENT;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+int otg_host_release(struct usb_otg *otg)
+{
+	return dwc3_otg_received_host_release(otg);
+}
+EXPORT_SYMBOL(otg_host_release);
+
+static void dwc3_otg_enable_irq(struct dwc3_otg *otg)
+{
+	u32			reg;
+
+	/* Enable OTG IRQs */
+	reg = OEVT_ALL;
+
+	otg_write(otg, OEVTEN, reg);
+}
+
+static ssize_t store_srp(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+
+	phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	if (IS_ERR(phy) || !phy) {
+		if (!IS_ERR(phy))
+			usb_put_phy(phy);
+		return count;
+	}
+
+	otg = phy->otg;
+	if (!otg) {
+		usb_put_phy(phy);
+		return count;
+	}
+
+	otg_start_srp(otg);
+	usb_put_phy(phy);
+	return count;
+}
+static DEVICE_ATTR(srp, 0220, NULL, store_srp);
+
+static ssize_t store_end(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+
+	phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	if (IS_ERR(phy) || !phy) {
+		if (!IS_ERR(phy))
+			usb_put_phy(phy);
+		return count;
+	}
+
+	otg = phy->otg;
+	if (!otg) {
+		usb_put_phy(phy);
+		return count;
+	}
+
+	otg_end_session(otg);
+	usb_put_phy(phy);
+	return count;
+}
+static DEVICE_ATTR(end, 0220, NULL, store_end);
+
+static ssize_t store_hnp(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct dwc3 *dwc = dev_get_drvdata(dev);
+	struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	struct usb_otg *otg;
+
+	dev_dbg(dwc->dev, "%s()\n", __func__);
+
+	if (IS_ERR(phy) || !phy) {
+		dev_info(dwc->dev, "NO PHY!!\n");
+		if (!IS_ERR(phy))
+			usb_put_phy(phy);
+		return count;
+	}
+
+	otg = phy->otg;
+	if (!otg) {
+		dev_info(dwc->dev, "NO OTG!!\n");
+		usb_put_phy(phy);
+		return count;
+	}
+
+	dev_info(dev, "b_hnp_enable is FALSE\n");
+	dwc->gadget.host_request_flag = 1;
+
+	usb_put_phy(phy);
+	return count;
+}
+static DEVICE_ATTR(hnp, 0220, NULL, store_hnp);
+
+static ssize_t store_a_hnp_reqd(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct dwc3 *dwc = dev_get_drvdata(dev);
+	struct dwc3_otg *otg;
+
+	otg = dwc->otg;
+	host_release(otg);
+	return count;
+}
+static DEVICE_ATTR(a_hnp_reqd, 0220, NULL, store_a_hnp_reqd);
+
+static ssize_t store_print_dbg(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct dwc3 *dwc = dev_get_drvdata(dev);
+	struct dwc3_otg *otg;
+
+	otg = dwc->otg;
+	print_debug_regs(otg);
+
+	return count;
+}
+static DEVICE_ATTR(print_dbg, 0220, NULL, store_print_dbg);
+
+void dwc_usb3_remove_dev_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_print_dbg);
+	device_remove_file(dev, &dev_attr_a_hnp_reqd);
+	device_remove_file(dev, &dev_attr_end);
+	device_remove_file(dev, &dev_attr_srp);
+	device_remove_file(dev, &dev_attr_hnp);
+}
+
+int dwc3_otg_create_dev_files(struct device *dev)
+{
+	int retval;
+
+	retval = device_create_file(dev, &dev_attr_hnp);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_srp);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_end);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_a_hnp_reqd);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_print_dbg);
+	if (retval)
+		goto fail;
+
+	return 0;
+
+fail:
+	dev_err(dev, "Failed to create one or more sysfs files!!\n");
+	return retval;
+}
+
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+	struct dwc3_otg *otg;
+	int err;
+	u32 reg;
+
+	dev_dbg(dwc->dev, "dwc3_otg_init\n");
+
+	/*
+	 * GHWPARAMS6[10] bit is SRPSupport.
+	 * This bit also reflects DWC_USB3_EN_OTG
+	 */
+	reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+	if (!(reg & GHWPARAMS6_SRP_SUPPORT_ENABLED)) {
+		/*
+		 * No OTG support in the HW core.
+		 * We return 0 to indicate no error, since this is acceptable
+		 * situation, just continue probe the dwc3 driver without otg.
+		 */
+		dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n");
+		return 0;
+	}
+
+	otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+	if (!otg)
+		return -ENOMEM;
+
+	dwc->otg = otg;
+	otg->dev = dwc->dev;
+	otg->dwc = dwc;
+
+	otg->regs = dwc->regs - DWC3_GLOBALS_REGS_START;
+	otg->otg.usb_phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL);
+	otg->otg.usb_phy->dev = otg->dev;
+	otg->otg.usb_phy->label = "dwc3_otg";
+	otg->otg.state = OTG_STATE_UNDEFINED;
+	otg->otg.usb_phy->otg = &otg->otg;
+	otg->otg.usb_phy->notify_connect = dwc3_otg_notify_connect;
+	otg->otg.usb_phy->notify_disconnect = dwc3_otg_notify_disconnect;
+
+	otg->otg.start_srp = dwc3_otg_start_srp;
+	otg->otg.start_hnp = dwc3_otg_start_hnp;
+	otg->otg.set_host = dwc3_otg_set_host;
+	otg->otg.set_peripheral = dwc3_otg_set_periph;
+
+	otg->hwparams6 = reg;
+	otg->state = OTG_STATE_UNDEFINED;
+
+	spin_lock_init(&otg->lock);
+	init_waitqueue_head(&otg->main_wq);
+
+	err = usb_add_phy(otg->otg.usb_phy, USB_PHY_TYPE_USB3);
+	if (err) {
+		dev_err(otg->dev, "can't register transceiver, err: %d\n",
+			err);
+		goto exit;
+	}
+
+	otg->irq = platform_get_irq(to_platform_device(otg->dev), 1);
+
+	dwc3_otg_create_dev_files(otg->dev);
+
+	/* Set irq handler */
+	err = request_irq(otg->irq, dwc3_otg_irq, IRQF_SHARED, "dwc3_otg", otg);
+	if (err) {
+		dev_err(otg->otg.usb_phy->dev, "failed to request irq #%d --> %d\n",
+				otg->irq, err);
+		goto exit;
+	}
+
+	dwc3_otg_enable_irq(otg);
+
+	return 0;
+exit:
+	kfree(otg->otg.usb_phy);
+	kfree(otg);
+	return err;
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+	struct dwc3_otg *otg = dwc->otg;
+
+	otg_dbg(otg, "\n");
+	usb_remove_phy(otg->otg.usb_phy);
+	kfree(otg->otg.usb_phy);
+	kfree(otg);
+}
diff --git a/drivers/usb/dwc3/otg.h b/drivers/usb/dwc3/otg.h
new file mode 100644
index 0000000..e2eb4ca
--- /dev/null
+++ b/drivers/usb/dwc3/otg.h
@@ -0,0 +1,247 @@
+/**
+ * otg.h - DesignWare USB3 DRD OTG Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define otg_dbg(d, fmt, args...)  dev_dbg((d)->dev, "%s(): " fmt,\
+		__func__, ## args)
+#define otg_vdbg(d, fmt, args...) dev_vdbg((d)->dev, "%s(): " fmt,\
+		__func__, ## args)
+#define otg_err(d, fmt, args...)  dev_err((d)->dev, "%s(): ERROR: " fmt,\
+		__func__, ## args)
+#define otg_warn(d, fmt, args...) dev_warn((d)->dev, "%s(): WARN: " fmt,\
+		__func__, ## args)
+#define otg_info(d, fmt, args...) dev_info((d)->dev, "%s(): INFO: " fmt,\
+		__func__, ## args)
+
+#ifdef VERBOSE_DEBUG
+#define otg_write(o, reg, val)	do {					\
+		otg_vdbg(o, "OTG_WRITE: reg=0x%05x, val=0x%08x\n", reg, val); \
+		writel(val, ((void *)((o)->regs)) + reg);	\
+	} while (0)
+
+#define otg_read(o, reg) ({						\
+		u32 __r = readl(((void *)((o)->regs)) + reg);	\
+		otg_vdbg(o, "OTG_READ: reg=0x%05x, val=0x%08x\n", reg, __r); \
+		__r;							\
+	})
+#else
+#define otg_write(o, reg, val)	writel(val, ((void *)((o)->regs)) + reg)
+#define otg_read(o, reg)	readl(((void *)((o)->regs)) + reg)
+#endif
+
+#define sleep_main_thread_until_condition_timeout(otg, condition, msecs) ({ \
+		int __timeout = msecs;				\
+		while (!(condition)) {				\
+			otg_dbg(otg, "  ... sleeping for %d\n", __timeout); \
+			__timeout = sleep_main_thread_timeout(otg, __timeout); \
+			if (__timeout <= 0) {			\
+				break;				\
+			}					\
+		}						\
+		__timeout;					\
+	})
+
+#define sleep_main_thread_until_condition(otg, condition) ({	\
+		int __rc;					\
+		do {						\
+			__rc = sleep_main_thread_until_condition_timeout(otg, \
+					condition, 50000);	\
+		} while (__rc == 0);				\
+		__rc;						\
+	})
+
+#define GHWPARAMS6				0xc158
+#define GHWPARAMS6_SRP_SUPPORT_ENABLED		0x0400
+#define GHWPARAMS6_HNP_SUPPORT_ENABLED		0x0800
+
+#define GCTL					0xc110
+#define GCTL_PRT_CAP_DIR			0x3000
+#define GCTL_PRT_CAP_DIR_SHIFT			12
+#define GCTL_PRT_CAP_DIR_HOST			1
+#define GCTL_PRT_CAP_DIR_DEV			2
+#define GCTL_PRT_CAP_DIR_OTG			3
+#define GCTL_GBL_HIBERNATION_EN			0x2
+
+#define OCFG					0xcc00
+#define OCFG_SRP_CAP				0x01
+#define OCFG_SRP_CAP_SHIFT			0
+#define OCFG_HNP_CAP				0x02
+#define OCFG_HNP_CAP_SHIFT			1
+#define OCFG_OTG_VERSION			0x04
+#define OCFG_OTG_VERSION_SHIFT			2
+
+#define OCTL					0xcc04
+#define OCTL_HST_SET_HNP_EN			0x01
+#define OCTL_HST_SET_HNP_EN_SHIFT		0
+#define OCTL_DEV_SET_HNP_EN			0x02
+#define OCTL_DEV_SET_HNP_EN_SHIFT		1
+#define OCTL_TERM_SEL_DL_PULSE			0x04
+#define OCTL_TERM_SEL_DL_PULSE_SHIFT		2
+#define OCTL_SES_REQ				0x08
+#define OCTL_SES_REQ_SHIFT			3
+#define OCTL_HNP_REQ				0x10
+#define OCTL_HNP_REQ_SHIFT			4
+#define OCTL_PRT_PWR_CTL			0x20
+#define OCTL_PRT_PWR_CTL_SHIFT			5
+#define OCTL_PERI_MODE				0x40
+#define OCTL_PERI_MODE_SHIFT			6
+
+#define OEVT					0xcc08
+#define OEVT_ERR				0x00000001
+#define OEVT_ERR_SHIFT				0
+#define OEVT_SES_REQ_SCS			0x00000002
+#define OEVT_SES_REQ_SCS_SHIFT			1
+#define OEVT_HST_NEG_SCS			0x00000004
+#define OEVT_HST_NEG_SCS_SHIFT			2
+#define OEVT_B_SES_VLD_EVT			0x00000008
+#define OEVT_B_SES_VLD_EVT_SHIFT		3
+#define OEVT_B_DEV_VBUS_CHNG_EVNT		0x00000100
+#define OEVT_B_DEV_VBUS_CHNG_EVNT_SHIFT		8
+#define OEVT_B_DEV_SES_VLD_DET_EVNT		0x00000200
+#define OEVT_B_DEV_SES_VLD_DET_EVNT_SHIFT	9
+#define OEVT_B_DEV_HNP_CHNG_EVNT		0x00000400
+#define OEVT_B_DEV_HNP_CHNG_EVNT_SHIFT		10
+#define OEVT_B_DEV_B_HOST_END_EVNT		0x00000800
+#define OEVT_B_DEV_B_HOST_END_EVNT_SHIFT	11
+#define OEVT_A_DEV_SESS_END_DET_EVNT		0x00010000
+#define OEVT_A_DEV_SESS_END_DET_EVNT_SHIFT	16
+#define OEVT_A_DEV_SRP_DET_EVNT			0x00020000
+#define OEVT_A_DEV_SRP_DET_EVNT_SHIFT		17
+#define OEVT_A_DEV_HNP_CHNG_EVNT		0x00040000
+#define OEVT_A_DEV_HNP_CHNG_EVNT_SHIFT		18
+#define OEVT_A_DEV_HOST_EVNT			0x00080000
+#define OEVT_A_DEV_HOST_EVNT_SHIFT		19
+#define OEVT_A_DEV_B_DEV_HOST_END_EVNT		0x00100000
+#define OEVT_A_DEV_B_DEV_HOST_END_EVNT_SHIFT	20
+#define OEVT_A_DEV_IDLE_EVNT			0x00200000
+#define OEVT_A_DEV_IDLE_EVNT_SHIFT		21
+#define OEVT_HOST_ROLE_REQ_INIT_EVNT		0x00400000
+#define OEVT_HOST_ROLE_REQ_INIT_EVNT_SHIFT	22
+#define OEVT_HOST_ROLE_REQ_CONFIRM_EVNT		0x00800000
+#define OEVT_HOST_ROLE_REQ_CONFIRM_EVNT_SHIFT	23
+#define OEVT_CONN_ID_STS_CHNG_EVNT		0x01000000
+#define OEVT_CONN_ID_STS_CHNG_EVNT_SHIFT	24
+#define OEVT_DEV_MOD_EVNT			0x80000000
+#define OEVT_DEV_MOD_EVNT_SHIFT			31
+
+#define OEVTEN					0xcc0c
+
+#define OEVT_ALL (OEVT_CONN_ID_STS_CHNG_EVNT | \
+		OEVT_HOST_ROLE_REQ_INIT_EVNT | \
+		OEVT_HOST_ROLE_REQ_CONFIRM_EVNT | \
+		OEVT_A_DEV_B_DEV_HOST_END_EVNT | \
+		OEVT_A_DEV_HOST_EVNT | \
+		OEVT_A_DEV_HNP_CHNG_EVNT | \
+		OEVT_A_DEV_SRP_DET_EVNT | \
+		OEVT_A_DEV_SESS_END_DET_EVNT | \
+		OEVT_B_DEV_B_HOST_END_EVNT | \
+		OEVT_B_DEV_HNP_CHNG_EVNT | \
+		OEVT_B_DEV_SES_VLD_DET_EVNT | \
+		OEVT_B_DEV_VBUS_CHNG_EVNT)
+
+#define OSTS					0xcc10
+#define OSTS_CONN_ID_STS			0x0001
+#define OSTS_CONN_ID_STS_SHIFT			0
+#define OSTS_A_SES_VLD				0x0002
+#define OSTS_A_SES_VLD_SHIFT			1
+#define OSTS_B_SES_VLD				0x0004
+#define OSTS_B_SES_VLD_SHIFT			2
+#define OSTS_XHCI_PRT_PWR			0x0008
+#define OSTS_XHCI_PRT_PWR_SHIFT			3
+#define OSTS_PERIP_MODE				0x0010
+#define OSTS_PERIP_MODE_SHIFT			4
+#define OSTS_OTG_STATES				0x0f00
+#define OSTS_OTG_STATE_SHIFT			8
+
+#define DCTL					0xc704
+#define DCTL_RUN_STOP				0x80000000
+
+#define OTG_STATE_INVALID			-1
+#define OTG_STATE_EXIT				14
+#define OTG_STATE_TERMINATED			15
+
+#define PERI_MODE_HOST		0
+#define PERI_MODE_PERIPHERAL	1
+
+/** The main structure to keep track of OTG driver state. */
+struct dwc3_otg {
+
+	/** OTG PHY */
+	struct usb_otg otg;
+	struct device *dev;
+	struct dwc3 *dwc;
+
+	void __iomem *regs;
+
+	int main_wakeup_needed;
+	struct task_struct *main_thread;
+	wait_queue_head_t main_wq;
+
+	spinlock_t lock;
+
+	int otg_srp_reqd;
+
+	/* Events */
+	u32 otg_events;
+
+	u32 user_events;
+
+	/** User initiated SRP.
+	 *
+	 * Valid in B-device during sensing/probing. Initiates SRP signalling
+	 * across the bus.
+	 *
+	 * Also valid as an A-device during probing. This causes the A-device to
+	 * apply V-bus manually and check for a device. Can be used if the
+	 * device does not support SRP and the host does not support ADP.
+	 */
+#define USER_SRP_EVENT			0x1
+	/** User initiated HNP (only valid in B-peripheral) */
+#define USER_HNP_EVENT			0x2
+	/** User has ended the session (only valid in B-peripheral) */
+#define USER_END_SESSION		0x4
+	/** User initiated VBUS. This will cause the A-device to turn on the
+	 * VBUS and see if a device will connect (only valid in A-device during
+	 * sensing/probing)
+	 */
+#define USER_VBUS_ON			0x8
+	/** User has initiated RSP */
+#define USER_RSP_EVENT			0x10
+	/** Host release event */
+#define PCD_RECEIVED_HOST_RELEASE_EVENT	0x20
+	/** Initial SRP */
+#define INITIAL_SRP			0x40
+	/** A-device connected event*/
+#define USER_A_CONN_EVENT		0x80
+
+	/* States */
+	enum usb_otg_state prev;
+	enum usb_otg_state state;
+
+	u32 hwparams6;
+	int hcd_irq;
+	int irq;
+	int host_started;
+	int peripheral_started;
+	int dev_enum;
+
+	struct delayed_work hp_work;	/* drives HNP polling */
+
+};
+
+extern int usb_port_suspend(struct usb_device *udev, pm_message_t msg);
+extern void usb_kick_hub_wq(struct usb_device *dev);
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH] usb: host: xhci: plat: add support for otg_set_host() call
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
                   ` (4 preceding siblings ...)
  2017-01-04 13:23 ` [RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller Manish Narani
@ 2017-01-04 13:23 ` Manish Narani
  2017-01-04 13:30 ` [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Felipe Balbi
  6 siblings, 0 replies; 14+ messages in thread
From: Manish Narani @ 2017-01-04 13:23 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon,
	michal.simek, soren.brinkmann, balbi, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch will add support for OTG host initialization. This will
help OTG drivers to populate their host subsystem.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/host/xhci-plat.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index ddfab30..aa08bdd 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -19,6 +19,7 @@
 #include <linux/usb/phy.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
+#include <linux/usb/otg.h>
 
 #include "xhci.h"
 #include "xhci-plat.h"
@@ -144,6 +145,37 @@ static const struct of_device_id usb_xhci_of_match[] = {
 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
 #endif
 
+static int usb_otg_set_host(struct device *dev, struct usb_hcd *hcd, bool yes)
+{
+	int ret = 0;
+
+	hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	if (!IS_ERR_OR_NULL(hcd->usb_phy) && hcd->usb_phy->otg) {
+		dev_dbg(dev, "%s otg support available\n", __func__);
+		if (yes) {
+			if (otg_set_host(hcd->usb_phy->otg, &hcd->self)) {
+				dev_err(dev, "%s otg_set_host failed\n",
+						__func__);
+				usb_put_phy(hcd->usb_phy);
+				goto disable_phy;
+			}
+		} else {
+			ret = otg_set_host(hcd->usb_phy->otg, NULL);
+			usb_put_phy(hcd->usb_phy);
+			goto disable_phy;
+		}
+
+	} else
+		goto disable_phy;
+
+	return 0;
+
+disable_phy:
+	hcd->usb_phy = NULL;
+
+	return ret;
+}
+
 static int xhci_plat_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
@@ -255,6 +287,11 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	if (ret)
 		goto dealloc_usb2_hcd;
 
+	ret = usb_otg_set_host(&pdev->dev, hcd, 1);
+	if (ret)
+		goto dealloc_usb2_hcd;
+
+
 	return 0;
 
 
@@ -283,6 +320,8 @@ static int xhci_plat_remove(struct platform_device *dev)
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	struct clk *clk = xhci->clk;
 
+	usb_otg_set_host(&dev->dev, hcd, 0);
+
 	usb_remove_hcd(xhci->shared_hcd);
 	usb_phy_shutdown(hcd->usb_phy);
 
-- 
2.1.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree
  2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
                   ` (5 preceding siblings ...)
  2017-01-04 13:23 ` [RFC PATCH] usb: host: xhci: plat: add support for otg_set_host() call Manish Narani
@ 2017-01-04 13:30 ` Felipe Balbi
  6 siblings, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2017-01-04 13:30 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, soren.brinkmann, gregkh,
	mathias.nyman, agraf, bharatku, punnaiah.choudary.kalluri,
	dhdang, marc.zyngier, mnarani, devicetree, linux-arm-kernel,
	linux-kernel, linux-usb
  Cc: anirudh, anuragku

[-- Attachment #1: Type: text/plain, Size: 901 bytes --]


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> This patch adds OTG interrupt support in device tree. It will add
> an extra interrupt line number dedicated to OTG events. This will
> enable OTG interrupts to serve in DWC3 OTG driver.
>
> Signed-off-by: Manish Narani <mnarani@xilinx.com>
> ---
>  arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
> index 68a90833..ce9ad02 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
> @@ -351,7 +351,7 @@
>  			compatible = "snps,dwc3";
>  			status = "disabled";
>  			interrupt-parent = <&gic>;
> -			interrupts = <0 65 4>;
> +			interrupts = <0 65 4>, <0 69 4>;

help the driver by passing names to these IRQ lines.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH] usb: dwc3: add support for OTG driver compilation
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: add support for OTG driver compilation Manish Narani
@ 2017-01-04 13:31   ` Felipe Balbi
  2017-01-05  5:27     ` Manish Narani
  0 siblings, 1 reply; 14+ messages in thread
From: Felipe Balbi @ 2017-01-04 13:31 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, soren.brinkmann, gregkh,
	mathias.nyman, agraf, bharatku, punnaiah.choudary.kalluri,
	dhdang, marc.zyngier, mnarani, devicetree, linux-arm-kernel,
	linux-kernel, linux-usb
  Cc: anirudh, anuragku

[-- Attachment #1: Type: text/plain, Size: 721 bytes --]


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> This patch adds support for OTG driver compilation and object
> file creation
>
> Signed-off-by: Manish Narani <mnarani@xilinx.com>
> ---
>  drivers/usb/dwc3/Makefile | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> index ffca340..2d269ad 100644
> --- a/drivers/usb/dwc3/Makefile
> +++ b/drivers/usb/dwc3/Makefile
> @@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
>  	dwc3-y				+= gadget.o ep0.o
>  endif
>  
> +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
> +	dwc3-y				+= otg.o
> +endif

you just broke compilation

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver Manish Narani
@ 2017-01-04 13:32   ` Felipe Balbi
  2017-01-05 12:24     ` Manish Narani
  0 siblings, 1 reply; 14+ messages in thread
From: Felipe Balbi @ 2017-01-04 13:32 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, soren.brinkmann, gregkh,
	mathias.nyman, agraf, bharatku, punnaiah.choudary.kalluri,
	dhdang, marc.zyngier, mnarani, devicetree, linux-arm-kernel,
	linux-kernel, linux-usb
  Cc: anirudh, anuragku

[-- Attachment #1: Type: text/plain, Size: 1385 bytes --]


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> This patch adds support for OTG host mode initialization in DWC3
> host driver. Before the host initialization sequence begins. The
> driver has to make sure the no OTG peripheral mode is enabled.
>
> Signed-off-by: Manish Narani <mnarani@xilinx.com>
> ---
>  drivers/usb/dwc3/host.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
> index 487f0ff..4caa3fe 100644
> --- a/drivers/usb/dwc3/host.c
> +++ b/drivers/usb/dwc3/host.c
> @@ -16,6 +16,8 @@
>   */
>  
>  #include <linux/platform_device.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
>  
>  #include "core.h"
>  
> @@ -111,6 +113,18 @@ int dwc3_host_init(struct dwc3 *dwc)
>  	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
>  			  dev_name(dwc->dev));
>  
> +	if (dwc->dr_mode == USB_DR_MODE_OTG) {
> +		struct usb_phy *phy;
> +		/* Switch otg to host mode */
> +		phy = usb_get_phy(USB_PHY_TYPE_USB3);
> +		if (!IS_ERR(phy)) {
> +			if (phy && phy->otg)
> +				otg_set_host(phy->otg,
> +						(struct usb_bus *)(long)1);
> +			usb_put_phy(phy);
> +		}
> +	}

NAK. Don't change default mode for everybody. Default mode should
actually be peripheral, but let's not touch whatever HW designer has
set; at least for now.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework
  2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework Manish Narani
@ 2017-01-04 14:03   ` Felipe Balbi
  0 siblings, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2017-01-04 14:03 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, soren.brinkmann, gregkh,
	mathias.nyman, agraf, bharatku, punnaiah.choudary.kalluri,
	dhdang, marc.zyngier, mnarani, devicetree, linux-arm-kernel,
	linux-kernel, linux-usb
  Cc: anirudh, anuragku

[-- Attachment #1: Type: text/plain, Size: 6584 bytes --]


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> This patch adds support for OTG in DWC3 gadget framework. This
> also adds support for HNP polling by host while in OTG mode.
>
> Modifications in couple of functions to make it in sync with
> OTG driver while keeping its original functionality intact.
>
> Signed-off-by: Manish Narani <mnarani@xilinx.com>
> ---
>  drivers/usb/dwc3/ep0.c    | 49 +++++++++++++++++++++++---
>  drivers/usb/dwc3/gadget.c | 87 +++++++++++++++++++++++++++++++++++++++--------
>  2 files changed, 116 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
> index 4878d18..aa78c1b 100644
> --- a/drivers/usb/dwc3/ep0.c
> +++ b/drivers/usb/dwc3/ep0.c
> @@ -334,6 +334,8 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
>  				usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
>  		}
>  
> +		usb_status |= dwc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
> +
>  		break;
>  
>  	case USB_RECIP_INTERFACE:
> @@ -448,11 +450,45 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
>  
>  	switch (wValue) {
>  	case USB_DEVICE_REMOTE_WAKEUP:
> +		if (set)
> +			dwc->remote_wakeup = 1;
> +		else
> +			dwc->remote_wakeup = 0;
>  		break;
> -	/*
> -	 * 9.4.1 says only only for SS, in AddressState only for
> -	 * default control pipe
> -	 */
> +	case USB_DEVICE_B_HNP_ENABLE:
> +		dev_dbg(dwc->dev,
> +			"SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");

sorry, but no thanks. It has taken me a lot of work to come up with
proper tracers so I could get rid of all dev_dbg() calls here. I'm not
accepting any new one :-)

> +		if (set && dwc->gadget.is_otg) {
> +			if (dwc->gadget.host_request_flag) {
> +				struct usb_phy *phy =
> +					usb_get_phy(USB_PHY_TYPE_USB3);
> +
> +				dwc->gadget.b_hnp_enable = 0;
> +				dwc->gadget.host_request_flag = 0;
> +				otg_start_hnp(phy->otg);
> +				usb_put_phy(phy);
> +			} else {
> +				dwc->gadget.b_hnp_enable = 1;
> +			}
> +		} else
> +			return -EINVAL;
> +		break;
> +
> +	case USB_DEVICE_A_HNP_SUPPORT:
> +		/* RH port supports HNP */
> +		dev_dbg(dwc->dev,
> +			"SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");

ditto

> +		break;
> +
> +	case USB_DEVICE_A_ALT_HNP_SUPPORT:
> +		/* other RH port does */
> +		dev_dbg(dwc->dev,
> +			"SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");

ditto

> +		break;
> +		/*
> +		 * 9.4.1 says only only for SS, in AddressState only for
> +		 * default control pipe
> +		 */
>  	case USB_DEVICE_U1_ENABLE:
>  		ret = dwc3_ep0_handle_u1(dwc, state, set);
>  		break;
> @@ -759,7 +795,10 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
>  
>  	switch (ctrl->bRequest) {
>  	case USB_REQ_GET_STATUS:
> -		ret = dwc3_ep0_handle_status(dwc, ctrl);
> +		if (le16_to_cpu(ctrl->wIndex) == OTG_STS_SELECTOR)
> +			ret = dwc3_ep0_delegate_req(dwc, ctrl);
> +		else
> +			ret = dwc3_ep0_handle_status(dwc, ctrl);
>  		break;
>  	case USB_REQ_CLEAR_FEATURE:
>  		ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index 6785595..3b0b771 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -34,6 +34,7 @@
>  #include "core.h"
>  #include "gadget.h"
>  #include "io.h"
> +#include "otg.h"
>  
>  /**
>   * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
> @@ -1792,25 +1793,51 @@ static int dwc3_gadget_start(struct usb_gadget *g,
>  	int			ret = 0;
>  	int			irq;
>  
> -	irq = dwc->irq_gadget;
> -	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
> -			IRQF_SHARED, "dwc3", dwc->ev_buf);
> -	if (ret) {
> -		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
> -				irq, ret);
> -		goto err0;
> -	}
> -
>  	spin_lock_irqsave(&dwc->lock, flags);
>  	if (dwc->gadget_driver) {
>  		dev_err(dwc->dev, "%s is already bound to %s\n",
>  				dwc->gadget.name,
>  				dwc->gadget_driver->driver.name);
>  		ret = -EBUSY;
> -		goto err1;
> +		goto err0;
>  	}
>  
> -	dwc->gadget_driver	= driver;
> +	if (g->is_otg) {
> +		static struct usb_gadget_driver *prev_driver;

hehe, there's no way I'm taking this. There are platforms with more than
one dwc3 instance. Please go back to drawing board.

> +		/* There are two instances where OTG functionality is enabled :
> +		 * 1. This function will be called from OTG driver to start the
> +		 * gadget
> +		 * 2. This function will be called by the Linux Class Driver to
> +		 * start the gadget
> +		 * Below code will keep synchronization between these calls. The
> +		 * "driver" argument will be NULL when it is called from the OTG
> +		 * driver, so we are maintaning a global variable "prev_driver"
> +		 * to assign value of argument "driver" (from class driver) to
> +		 * dwc->gadget_driver when it is called from OTG.
> +		 */
> +		if (driver) {
> +			prev_driver	= driver;
> +			if (dwc->otg) {
> +				struct dwc3_otg		*otg = dwc->otg;
> +
> +				if ((otg->host_started ||
> +						(!otg->peripheral_started)))
> +					goto err0;
> +			}
> +			dwc->gadget_driver	= driver;
> +		} else
> +			dwc->gadget_driver	= prev_driver;
> +	} else
> +		dwc->gadget_driver	= driver;
> +
> +	irq = dwc->irq_gadget;
> +	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
> +			IRQF_SHARED, "dwc3", dwc->ev_buf);
> +	if (ret) {
> +		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
> +				irq, ret);
> +		goto err0;
> +	}
>  
>  	if (pm_runtime_active(dwc->dev))
>  		__dwc3_gadget_start(dwc);
> @@ -1819,11 +1846,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
>  
>  	return 0;
>  
> -err1:
> -	spin_unlock_irqrestore(&dwc->lock, flags);
> -	free_irq(irq, dwc);
> -
>  err0:
> +	dwc->gadget_driver = NULL;
> +	spin_unlock_irqrestore(&dwc->lock, flags);
>  	return ret;
>  }

you shouldn't have to change anything in this file to add OTG. If you
are changing then it's likely something's wrong with your approach. I
need further clarification as to why you think this change is necessary.

> @@ -2977,6 +3002,18 @@ int dwc3_gadget_init(struct dwc3 *dwc)
>  
>  	dwc->irq_gadget = irq;
>  
> +	if (dwc->dr_mode == USB_DR_MODE_OTG) {
> +		struct usb_phy *phy;
> +		/* Switch otg to peripheral mode */
> +		phy = usb_get_phy(USB_PHY_TYPE_USB3);

serioulsy? Did you not notice we already *HAVE* a handle to the PHY
which we get during probe?? You don't need to contantly get the PHY like this.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFC PATCH] usb: dwc3: add support for OTG driver compilation
  2017-01-04 13:31   ` Felipe Balbi
@ 2017-01-05  5:27     ` Manish Narani
  2017-01-05  9:20       ` Felipe Balbi
  0 siblings, 1 reply; 14+ messages in thread
From: Manish Narani @ 2017-01-05  5:27 UTC (permalink / raw)
  To: Felipe Balbi, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, Soren Brinkmann, gregkh,
	mathias.nyman, agraf, Bharat Kumar Gogada,
	Punnaiah Choudary Kalluri, dhdang, marc.zyngier, devicetree,
	linux-arm-kernel, linux-kernel, linux-usb
  Cc: Anirudha Sarangi, Anurag Kumar Vulisha

Hi Felipe,


> From: Felipe Balbi [mailto:balbi@kernel.org]
> Sent: Wednesday, January 04, 2017 7:01 PM
> Hi,
>
> Manish Narani <manish.narani@xilinx.com> writes:
> > This patch adds support for OTG driver compilation and object file
> > creation
> >
> > Signed-off-by: Manish Narani <mnarani@xilinx.com>
> > ---
> >  drivers/usb/dwc3/Makefile | 4 ++++
> >  1 file changed, 4 insertions(+)
> >
> > diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> > index ffca340..2d269ad 100644
> > --- a/drivers/usb/dwc3/Makefile
> > +++ b/drivers/usb/dwc3/Makefile
> > @@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET)
> $(CONFIG_USB_DWC3_DUAL_ROLE)),)
> >     dwc3-y                          += gadget.o ep0.o
> >  endif
> >
> > +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
> > +   dwc3-y                          += otg.o
> > +endif
>
> you just broke compilation

Should I add new USB_DWC3_OTG macro in Kconfig?


Thanks,
Manish


This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFC PATCH] usb: dwc3: add support for OTG driver compilation
  2017-01-05  5:27     ` Manish Narani
@ 2017-01-05  9:20       ` Felipe Balbi
  0 siblings, 0 replies; 14+ messages in thread
From: Felipe Balbi @ 2017-01-05  9:20 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, Soren Brinkmann, gregkh,
	mathias.nyman, agraf, Bharat Kumar Gogada,
	Punnaiah Choudary Kalluri, dhdang, marc.zyngier, devicetree,
	linux-arm-kernel, linux-kernel, linux-usb
  Cc: Anirudha Sarangi, Anurag Kumar Vulisha

[-- Attachment #1: Type: text/plain, Size: 1700 bytes --]


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> Hi Felipe,
>
>
>> From: Felipe Balbi [mailto:balbi@kernel.org]
>> Sent: Wednesday, January 04, 2017 7:01 PM
>> Hi,
>>
>> Manish Narani <manish.narani@xilinx.com> writes:
>> > This patch adds support for OTG driver compilation and object file
>> > creation
>> >
>> > Signed-off-by: Manish Narani <mnarani@xilinx.com>
>> > ---
>> >  drivers/usb/dwc3/Makefile | 4 ++++
>> >  1 file changed, 4 insertions(+)
>> >
>> > diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
>> > index ffca340..2d269ad 100644
>> > --- a/drivers/usb/dwc3/Makefile
>> > +++ b/drivers/usb/dwc3/Makefile
>> > @@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET)
>> $(CONFIG_USB_DWC3_DUAL_ROLE)),)
>> >     dwc3-y                          += gadget.o ep0.o
>> >  endif
>> >
>> > +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
>> > +   dwc3-y                          += otg.o
>> > +endif
>>
>> you just broke compilation
>
> Should I add new USB_DWC3_OTG macro in Kconfig?

No, no. Reset your tree to this commit and try to compile it. Problem
should be clear.

> This email and any attachments are intended for the sole use of the
> named recipient(s) and contain(s) confidential information that may be
> proprietary, privileged or copyrighted under applicable law. If you
> are not the intended recipient, do not read, copy, or forward this
> email message or any attachments. Delete this email message and any
> attachments immediately.

you should make sure these notes aren't sent to public mailing
lists. I'll delete this email of yours and stop reading them until this
is sorted out.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver
  2017-01-04 13:32   ` Felipe Balbi
@ 2017-01-05 12:24     ` Manish Narani
  0 siblings, 0 replies; 14+ messages in thread
From: Manish Narani @ 2017-01-05 12:24 UTC (permalink / raw)
  To: Felipe Balbi, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, Soren Brinkmann, gregkh,
	mathias.nyman, agraf, Bharat Kumar Gogada,
	Punnaiah Choudary Kalluri, dhdang, marc.zyngier, devicetree,
	linux-arm-kernel, linux-kernel, linux-usb
  Cc: Anirudha Sarangi, Anurag Kumar Vulisha

Hi Felipe,

> -----Original Message-----
> From: Felipe Balbi [mailto:balbi@kernel.org]
> Sent: Wednesday, January 04, 2017 7:03 PM
> 
> Hi,
> 
> Manish Narani <manish.narani@xilinx.com> writes:
> > This patch adds support for OTG host mode initialization in DWC3 host
> > driver. Before the host initialization sequence begins. The driver has
> > to make sure the no OTG peripheral mode is enabled.
> >
> > Signed-off-by: Manish Narani <mnarani@xilinx.com>
> > ---
> >  drivers/usb/dwc3/host.c | 14 ++++++++++++++
> >  1 file changed, 14 insertions(+)
> >
> > diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index
> > 487f0ff..4caa3fe 100644
> > --- a/drivers/usb/dwc3/host.c
> > +++ b/drivers/usb/dwc3/host.c
> > @@ -16,6 +16,8 @@
> >   */
> >
> >  #include <linux/platform_device.h>
> > +#include <linux/usb.h>
> > +#include <linux/usb/hcd.h>
> >
> >  #include "core.h"
> >
> > @@ -111,6 +113,18 @@ int dwc3_host_init(struct dwc3 *dwc)
> >  	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
> >  			  dev_name(dwc->dev));
> >
> > +	if (dwc->dr_mode == USB_DR_MODE_OTG) {
> > +		struct usb_phy *phy;
> > +		/* Switch otg to host mode */
> > +		phy = usb_get_phy(USB_PHY_TYPE_USB3);
> > +		if (!IS_ERR(phy)) {
> > +			if (phy && phy->otg)
> > +				otg_set_host(phy->otg,
> > +						(struct usb_bus *)(long)1);
> > +			usb_put_phy(phy);
> > +		}
> > +	}
> 
> NAK. Don't change default mode for everybody. Default mode should actually
> be peripheral, but let's not touch whatever HW designer has set; at least for
> now.

Yes, The default mode is Peripheral. The above is to initialize the host related stuff in OTG driver before sensing the OTG ID and get to the respective mode.

- Manish

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2017-01-05 12:25 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-04 13:22 [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Manish Narani
2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: add support for OTG driver compilation Manish Narani
2017-01-04 13:31   ` Felipe Balbi
2017-01-05  5:27     ` Manish Narani
2017-01-05  9:20       ` Felipe Balbi
2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: core: add OTG support function calls and modifications Manish Narani
2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework Manish Narani
2017-01-04 14:03   ` Felipe Balbi
2017-01-04 13:22 ` [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver Manish Narani
2017-01-04 13:32   ` Felipe Balbi
2017-01-05 12:24     ` Manish Narani
2017-01-04 13:23 ` [RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller Manish Narani
2017-01-04 13:23 ` [RFC PATCH] usb: host: xhci: plat: add support for otg_set_host() call Manish Narani
2017-01-04 13:30 ` [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree Felipe Balbi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).