[RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller

Manish Narani manish.narani at xilinx.com
Wed Jan 4 05:23:00 PST 2017


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 at 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 at 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 at ti.com>,
+ *	    Sebastian Andrzej Siewior <bigeasy at 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




More information about the linux-arm-kernel mailing list