Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 76316a3..ceefb23 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -133,8 +133,6 @@
 
 source "drivers/usb/musb/Kconfig"
 
-source "drivers/usb/renesas_usbhs/Kconfig"
-
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 53a7bc0..b1ec3fc 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -27,6 +27,7 @@
 obj-$(CONFIG_USB_ISP1760_HCD)	+= host/
 obj-$(CONFIG_USB_IMX21_HCD)	+= host/
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= host/
+obj-$(CONFIG_USB_PEHCI_HCD)	+= host/
 
 obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/
 
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9a56635..19e1244 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1274,6 +1274,57 @@
 	return status;
 }
 
+#ifdef CONFIG_USB_OTG
+void usb_hnp_polling_work(struct work_struct *work)
+{
+	int ret;
+	struct usb_bus *bus =
+		container_of(work, struct usb_bus, hnp_polling.work);
+	struct usb_device *udev = bus->root_hub->children[bus->otg_port - 1];
+	u8 *status = NULL;
+
+	/*
+	 * The OTG-B device must hand back the host role to OTG PET
+	 * within 100 msec irrespective of host_request flag.
+	 */
+	if (bus->quick_hnp) {
+		bus->quick_hnp = 0;
+		goto start_hnp;
+	}
+
+	status = kmalloc(sizeof(*status), GFP_KERNEL);
+	if (!status)
+		goto reschedule;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+		0, OTG_STATUS_SELECTOR, status, sizeof(*status),
+		USB_CTRL_GET_TIMEOUT);
+	if (ret < 0) {
+		/* Peripheral may not be supporting HNP polling */
+		dev_info(&udev->dev, "HNP polling failed. status %d\n", ret);
+		goto out;
+	}
+
+	if (!(*status & (1 << HOST_REQUEST_FLAG)))
+		goto reschedule;
+
+start_hnp:
+	do_unbind_rebind(udev, DO_UNBIND);
+	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+	ret = usb_suspend_both(udev, PMSG_USER_SUSPEND);
+	if (ret)
+		dev_info(&udev->dev, "suspend failed\n");
+	goto out;
+
+reschedule:
+	schedule_delayed_work(&bus->hnp_polling,
+		msecs_to_jiffies(THOST_REQ_POLL));
+out:
+	kfree(status);
+}
+#endif
+
 static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
 {
 	int	w;
@@ -1342,6 +1393,7 @@
 	 * (This can't be done in usb_resume_interface()
 	 * above because it doesn't own the right set of locks.)
 	 */
+	pm_runtime_get_sync(dev->parent);
 	status = usb_resume_both(udev, msg);
 	if (status == 0) {
 		pm_runtime_disable(dev);
@@ -1349,6 +1401,7 @@
 		pm_runtime_enable(dev);
 		unbind_no_reset_resume_drivers_interfaces(udev);
 	}
+	pm_runtime_put_sync(dev->parent);
 
 	/* Avoid PM error messages for devices disconnected while suspended
 	 * as we'll display regular disconnect messages just a bit later.
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 140d3e1..7c37aff 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -897,6 +897,9 @@
 	bus->bandwidth_isoc_reqs = 0;
 
 	INIT_LIST_HEAD (&bus->bus_list);
+#ifdef CONFIG_USB_OTG
+	INIT_DELAYED_WORK(&bus->hnp_polling, usb_hnp_polling_work);
+#endif
 }
 
 /*-------------------------------------------------------------------------*/
@@ -926,6 +929,11 @@
 	/* Add it to the local list of buses */
 	list_add (&bus->bus_list, &usb_bus_list);
 	mutex_unlock(&usb_bus_list_lock);
+#ifdef CONFIG_USB_OTG
+	/* Obvioulsy HNP is supported on B-host */
+	if (bus->is_b_host)
+		bus->hnp_support = 1;
+#endif
 
 	usb_notify_add_bus(bus);
 
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ec6c97d..5dceb41 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -24,12 +24,38 @@
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/usb/otg.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
 #include "usb.h"
 
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+#include <linux/usb/hcd.h>
+#include <linux/usb/ch11.h>
+
+int portno;
+int No_Data_Phase;
+EXPORT_SYMBOL(No_Data_Phase);
+int No_Status_Phase;
+EXPORT_SYMBOL(No_Status_Phase);
+unsigned char hub_tier;
+
+#define PDC_HOST_NOTIFY		0x8001	/*completion from core */
+#define UNSUPPORTED_DEVICE	0x8099
+#define UNWANTED_SUSPEND	0x8098
+#define PDC_POWERMANAGEMENT	0x8097
+
+int Unwanted_SecondReset;
+EXPORT_SYMBOL(Unwanted_SecondReset);
+int HostComplianceTest;
+EXPORT_SYMBOL(HostComplianceTest);
+int HostTest;
+EXPORT_SYMBOL(HostTest);
+#endif
+
+
 /* if we are in debug mode, always announce new devices */
 #ifdef DEBUG
 #ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -358,8 +384,11 @@
 {
 	int i, status = -ETIMEDOUT;
 
+	/* ISP1763A HUB sometimes returns 2 bytes instead of 4 bytes, retry
+	 * if this happens
+	 */
 	for (i = 0; i < USB_STS_RETRIES &&
-			(status == -ETIMEDOUT || status == -EPIPE); i++) {
+			(status == -ETIMEDOUT || status == -EPIPE || status == 2); i++) {
 		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
 			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
 			data, sizeof(*data), USB_STS_TIMEOUT);
@@ -765,6 +794,10 @@
 		 */
 		if (type == HUB_INIT) {
 			delay = hub_power_on(hub, false);
+#ifdef CONFIG_USB_OTG
+			if (hdev->bus->is_b_host)
+				goto init2;
+#endif
 			PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
 			schedule_delayed_work(&hub->init_work,
 					msecs_to_jiffies(delay));
@@ -906,6 +939,11 @@
 	 * will see them later and handle them normally.
 	 */
 	if (need_debounce_delay) {
+#ifdef CONFIG_USB_OTG
+		if (hdev->bus->is_b_host && type == HUB_INIT)
+			goto init3;
+#endif
+
 		delay = HUB_DEBOUNCE_STABLE;
 
 		/* Don't do a long sleep inside a workqueue routine */
@@ -1335,6 +1373,7 @@
 #ifdef	CONFIG_USB_OTG_BLACKLIST_HUB
 	if (hdev->parent) {
 		dev_warn(&intf->dev, "ignoring external hub\n");
+		otg_send_event(OTG_EVENT_HUB_NOT_SUPPORTED);
 		return -ENODEV;
 	}
 #endif
@@ -1676,6 +1715,13 @@
 	dev_info(&udev->dev, "USB disconnect, device number %d\n",
 			udev->devnum);
 
+#ifdef CONFIG_USB_OTG
+	if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+		cancel_delayed_work_sync(&udev->bus->hnp_polling);
+		udev->bus->hnp_support = 0;
+	}
+#endif
+
 	usb_lock_device(udev);
 
 	/* Free up all the children before we remove this device */
@@ -1757,6 +1803,7 @@
 	int err = 0;
 
 #ifdef	CONFIG_USB_OTG
+	bool old_otg = false;
 	/*
 	 * 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
@@ -1780,15 +1827,34 @@
 					(port1 == bus->otg_port)
 						? "" : "non-");
 
+				/* a_alt_hnp_support is obsoleted */
+				if (port1 != bus->otg_port)
+					goto out;
+
+				bus->hnp_support = 1;
+
+				/* a_hnp_support is not required for devices
+				 * compliant to revision 2.0 or subsequent
+				 * versions.
+				 */
+
+				if ((le16_to_cpu(desc->bLength) ==
+						USB_DT_OTG_SIZE) &&
+					le16_to_cpu(desc->bcdOTG) >= 0x0200)
+					goto out;
+
+				/* Legacy B-device i.e compliant to spec
+				 * revision 1.3 expect A-device to set
+				 * a_hnp_support or b_hnp_enable before
+				 * selecting configuration.
+				 */
+				old_otg = true;
+
 				/* enable HNP before suspend, it's simpler */
-				if (port1 == bus->otg_port)
-					bus->b_hnp_enable = 1;
 				err = usb_control_msg(udev,
 					usb_sndctrlpipe(udev, 0),
 					USB_REQ_SET_FEATURE, 0,
-					bus->b_hnp_enable
-						? USB_DEVICE_B_HNP_ENABLE
-						: USB_DEVICE_A_ALT_HNP_SUPPORT,
+					USB_DEVICE_A_HNP_SUPPORT,
 					0, NULL, 0, USB_CTRL_SET_TIMEOUT);
 				if (err < 0) {
 					/* OTG MESSAGE: report errors here,
@@ -1797,26 +1863,47 @@
 					dev_info(&udev->dev,
 						"can't set HNP mode: %d\n",
 						err);
-					bus->b_hnp_enable = 0;
+					bus->hnp_support = 0;
 				}
 			}
 		}
 	}
+out:
+	if ((udev->quirks & USB_QUIRK_OTG_PET)) {
+		if (le16_to_cpu(udev->descriptor.bcdDevice) &
+			OTG_TTST_VBUS_OFF)
+			udev->bus->otg_vbus_off = 1;
+		if (udev->bus->is_b_host || old_otg)
+			udev->bus->quick_hnp = 1;
+	}
 
 	if (!is_targeted(udev)) {
 
+		otg_send_event(OTG_EVENT_DEV_NOT_SUPPORTED);
+
 		/* Maybe it can talk to us, though we can't talk to it.
 		 * (Includes HNP test device.)
 		 */
-		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
+		if (udev->bus->hnp_support) {
 			err = usb_port_suspend(udev, PMSG_SUSPEND);
 			if (err < 0)
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 		}
 		err = -ENOTSUPP;
-		goto fail;
+	} else if (udev->bus->hnp_support &&
+		udev->portnum == udev->bus->otg_port) {
+		/* HNP polling is introduced in OTG supplement Rev 2.0
+		 * and older devices may not support. Work is not
+		 * re-armed if device returns STALL. B-Host also perform
+		 * HNP polling.
+		 */
+		if (udev->bus->quick_hnp)
+			schedule_delayed_work(&udev->bus->hnp_polling,
+				msecs_to_jiffies(OTG_TTST_SUSP));
+		else
+			schedule_delayed_work(&udev->bus->hnp_polling,
+				msecs_to_jiffies(THOST_REQ_POLL));
 	}
-fail:
 #endif
 	return err;
 }
@@ -2475,6 +2562,22 @@
 				return status;
 		}
 	}
+#ifdef CONFIG_USB_OTG
+	if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+		udev->portnum == udev->bus->otg_port) {
+		status = 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 (status < 0) {
+			otg_send_event(OTG_EVENT_NO_RESP_FOR_HNP_ENABLE);
+			dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+					"status %d\n", port1, status);
+		} else {
+			udev->bus->b_hnp_enable = 1;
+		}
+	}
+#endif
 
 	/* disable USB2 hardware LPM */
 	if (udev->usb2_hw_lpm_enabled == 1)
@@ -3091,14 +3194,22 @@
 					buf->bMaxPacketSize0;
 			kfree(buf);
 
-			retval = hub_port_reset(hub, port1, udev, delay, false);
-			if (retval < 0)		/* error or disconnect */
-				goto fail;
-			if (oldspeed != udev->speed) {
-				dev_dbg(&udev->dev,
-					"device reset changed speed!\n");
-				retval = -ENODEV;
-				goto fail;
+			/*
+			 * If it is a HSET Test device, we don't issue a
+			 * second reset which results in failure due to
+			 * speed change.
+			 */
+			if (le16_to_cpu(buf->idVendor) != 0x1a0a) {
+				retval = hub_port_reset(hub, port1, udev,
+							 delay, false);
+				if (retval < 0)	/* error or disconnect */
+					goto fail;
+				if (oldspeed != udev->speed) {
+					dev_dbg(&udev->dev,
+					       "device reset changed speed!\n");
+					retval = -ENODEV;
+					goto fail;
+				}
 			}
 			if (r) {
 				dev_err(&udev->dev,
@@ -3366,6 +3477,9 @@
 			(portchange & USB_PORT_STAT_C_CONNECTION))
 		clear_bit(port1, hub->removed_bits);
 
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+	if (Unwanted_SecondReset == 0)   /*stericsson*/
+#endif
 	if (portchange & (USB_PORT_STAT_C_CONNECTION |
 				USB_PORT_STAT_C_ENABLE)) {
 		status = hub_port_debounce(hub, port1);
@@ -3504,7 +3618,32 @@
 		status = hub_power_remaining(hub);
 		if (status)
 			dev_dbg(hub_dev, "%dmA power budget left\n", status);
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+		if (HostComplianceTest == 1 && udev->devnum > 1) {
+			if (HostTest == 7) {	/*SINGLE_STEP_GET_DEV_DESC */
+				dev_info(hub_dev, "Testing "
+						"SINGLE_STEP_GET_DEV_DESC\n");
+				/* Test the Single Step Get Device Descriptor ,
+				 * take care it should not get status phase
+				 */
+				No_Data_Phase = 1;
+				No_Status_Phase = 1;
 
+				usb_get_device_descriptor(udev, 8);
+				No_Data_Phase = 0;
+				No_Status_Phase = 0;
+			}
+
+			if (HostTest == 8) {
+				dev_info(hub_dev, "Testing "
+						"SINGLE_STEP_SET_FEATURE\n");
+				/* Test Single Step Set Feature */
+				No_Status_Phase = 1;
+				usb_get_device_descriptor(udev, 8);
+				No_Status_Phase = 0;
+			}
+		}
+#endif
 		return;
 
 loop_disable:
@@ -3581,6 +3720,11 @@
 	u16 portstatus;
 	u16 portchange;
 	int i, ret;
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+	int j;
+	int otgport = 0;
+	struct usb_port_status port_status;
+#endif
 	int connect_change, wakeup_change;
 
 	/*
@@ -3657,6 +3801,171 @@
 
 		/* deal with port status changes */
 		for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+			struct usb_port_status portsts;
+
+			/*if we have something to do on
+			 * otg port
+			 * */
+			if ((hdev->otgstate & USB_OTG_SUSPEND) ||
+			    (hdev->otgstate & USB_OTG_ENUMERATE) ||
+			    (hdev->otgstate & USB_OTG_DISCONNECT) ||
+			    (hdev->otgstate & USB_OTG_RESUME)) {
+				otgport = 1;
+			}
+
+
+			if (hdev->otgstate & USB_OTG_RESUME) {
+				ret = clear_port_feature(hdev, i,
+							 USB_PORT_FEAT_SUSPEND);
+				if (ret < 0) {
+					dev_err(hub_dev, "usb otg port Resume"
+						" fails, %d\n", ret);
+				}
+				hdev->otgstate &= ~USB_OTG_RESUME;
+			}
+			if ((hdev->otgstate & USB_OTG_SUSPEND)
+			    && (hdev->children[0])) {
+				hdev->otgstate &= ~USB_OTG_SUSPEND;
+
+				ret = set_port_feature(hdev, 1,
+						       USB_PORT_FEAT_SUSPEND);
+				if (ret < 0) {
+					dev_err(hub_dev, "usb otg port suspend"
+						" fails, %d\n", ret);
+					break;
+				}
+				msleep(1);
+				ret = get_port_status(hdev, i, &portsts);
+				if (ret < 0) {
+					dev_err(hub_dev, "usb otg get port"
+						" status fails, %d\n", ret);
+					break;
+				}
+				portchange = le16_to_cpu(portsts.wPortChange);
+				if (portchange & USB_PORT_STAT_C_SUSPEND) {
+					clear_port_feature(hdev, i,
+						USB_PORT_FEAT_C_SUSPEND);
+				}
+				break;
+			}
+
+			if (hdev->otgstate & USB_OTG_REMOTEWAKEUP) {
+
+				for (j = 1; j <= hub->descriptor->bNbrPorts;
+				     j++) {
+					if (hdev->children[j - 1]) {
+						dev_dbg(hub_dev, "child"
+						     " found at port %d\n", j);
+						ret = usb_control_msg(hdev->
+						      children[j - 1],
+						      usb_sndctrlpipe(hdev->
+								children[j - 1],
+								0),
+						      USB_REQ_SET_FEATURE,
+						      USB_RECIP_DEVICE,
+						      USB_DEVICE_REMOTE_WAKEUP,
+						      0, NULL,
+						      0,
+						      USB_CTRL_SET_TIMEOUT);
+						if (ret < 0) {
+							dev_err(hub_dev, "Port"
+							  " %d doesn't support"
+							  "remote wakeup\n", j);
+						} else {
+							dev_dbg(hub_dev, "Port"
+							  " %d supports"
+							  "remote wakeup\n", j);
+						}
+						ret = set_port_feature(hdev, j,
+							USB_PORT_FEAT_SUSPEND);
+						if (ret < 0) {
+							dev_err(hub_dev, "Port"
+							  " %d NOT ABLE TO"
+							  " SUSPEND\n", j);
+						} else {
+							dev_dbg(hub_dev, "Port"
+							  " %d is ABLE TO"
+							  " SUSPEND\n", j);
+						}
+					}
+				}
+				ret = usb_control_msg(hdev,
+						      usb_sndctrlpipe(hdev, 0),
+						      USB_REQ_SET_FEATURE,
+						      USB_RECIP_DEVICE,
+						      USB_DEVICE_REMOTE_WAKEUP,
+						      0, NULL, 0,
+						      USB_CTRL_SET_TIMEOUT);
+				if (ret < 0) {
+					dev_err(hub_dev, "HUB doesn't support"
+							" REMOTE WAKEUP\n");
+				} else {
+					dev_dbg(hub_dev, "HUB supports"
+							" REMOTE WAKEUP\n");
+				}
+				ret = 0;
+				msleep(10);
+				if (hdev->parent == hdev->bus->root_hub) {
+					if (hdev->hcd_suspend &&
+					    hdev->hcd_priv) {
+						dev_dbg(hub_dev, "calling"
+						  " suspend after remote wakeup"
+						  " command is issued\n");
+						hdev->hcd_suspend(hdev->
+								   hcd_priv);
+					}
+					if (hdev->otg_notif)
+						hdev->otg_notif(hdev->otgpriv,
+						       PDC_POWERMANAGEMENT, 10);
+				}
+			}
+
+			if (hdev->otgstate & USB_OTG_WAKEUP_ALL) {
+				(void) usb_control_msg(hdev,
+						       usb_sndctrlpipe(hdev, 0),
+						       USB_REQ_CLEAR_FEATURE,
+						       USB_RECIP_DEVICE,
+						       USB_DEVICE_REMOTE_WAKEUP,
+						       0, NULL, 0,
+						       USB_CTRL_SET_TIMEOUT);
+				dev_dbg(hub_dev, "Hub CLEARED REMOTE WAKEUP\n");
+				for (j = 1; j <= hub->descriptor->bNbrPorts;
+				     j++) {
+					if (hdev->children[j - 1]) {
+						dev_dbg(hub_dev, "PORT %d"
+						   " SUSPEND IS CLEARD\n", j);
+						clear_port_feature(hdev, j,
+						   USB_PORT_FEAT_C_SUSPEND);
+						msleep(50);
+						(void) usb_control_msg(hdev->
+						       children[j - 1],
+						       usb_sndctrlpipe(
+							  hdev->children[j - 1],
+							  0),
+						       USB_REQ_CLEAR_FEATURE,
+						       USB_RECIP_DEVICE,
+						       USB_DEVICE_REMOTE_WAKEUP,
+						       0, NULL,
+						       0,
+						       USB_CTRL_SET_TIMEOUT);
+						dev_dbg(hub_dev, "PORT %d "
+							"REMOTE WAKEUP IS "
+							"CLEARD\n", j);
+						msleep(10);
+					}
+				}
+
+
+			}
+
+
+			/*
+			 * reset the state of otg device,
+			 * regardless of otg device
+			 */
+			hdev->otgstate = 0;
+#endif
 			if (test_bit(i, hub->busy_bits))
 				continue;
 			connect_change = test_bit(i, hub->change_bits);
@@ -3761,9 +4070,19 @@
 						HUB_BH_RESET_TIME, true);
 			}
 
-			if (connect_change)
+			if (connect_change) {
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+				if (hdev->parent == hdev->bus->root_hub)
+					if (hdev->otg_notif
+					    && (HostComplianceTest == 0))
+						hdev->otg_notif(hdev->otgpriv,
+								PDC_HOST_NOTIFY,
+								5);
+				portno = i;
+#endif
 				hub_port_connect_change(hub, i,
 						portstatus, portchange);
+				}
 		} /* end for i */
 
 		/* deal with hub status changes */
@@ -3795,7 +4114,105 @@
 						"condition\n");
 			}
 		}
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+		/* if we have something on otg */
+		if (otgport) {
+			otgport = 0;
+			/* notify otg controller about it */
+			if (hdev->parent == hdev->bus->root_hub)
+				if (hdev->otg_notif)
+					hdev->otg_notif(hdev->otgpriv,
+							PDC_HOST_NOTIFY, 0);
+		}
 
+		if (HostComplianceTest && hdev->devnum > 1) {
+			/* TEST_SE0_NAK */
+			if (HostTest == 1) {
+				dev_info(hub_dev, "Testing for TEST_SE0_NAK\n");
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						       USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x300,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			/*TEST_J*/
+			if (HostTest == 2) {
+				dev_info(hub_dev, "Testing TEST_J\n");
+				ret = clear_port_feature(hdev, portno,
+						USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x100,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			if (HostTest == 3) {
+				dev_info(hub_dev, "Testing TEST_K\n");
+				ret = clear_port_feature(hdev, portno,
+						USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						       USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x200,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			if (HostTest == 4) {
+				dev_info(hub_dev, "Testing TEST_PACKET at Port"
+						  " %d\n", portno);
+				ret = clear_port_feature(hdev, portno,
+						USB_PORT_FEAT_C_CONNECTION);
+				if (ret < 0)
+					dev_err(hub_dev, "Clear port feature"
+						" C_CONNECTION failed\n");
+
+				ret = set_port_feature(hdev, portno,
+						       USB_PORT_FEAT_SUSPEND);
+				if (ret < 0)
+					dev_err(hub_dev, "Clear port feature"
+						" SUSPEND failed\n");
+
+				ret = set_port_feature(hdev, portno | 0x400,
+						       USB_PORT_FEAT_TEST);
+				if (ret < 0)
+					dev_err(hub_dev, "Clear port feature"
+						" TEST failed\n");
+
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+				if (ret < 0)
+					dev_err(hub_dev, "Get port status"
+						" failed\n");
+			}
+			if (HostTest == 5) {
+				dev_info(hub_dev, "Testing TEST_FORCE_ENBLE\n");
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						 USB_PORT_FEAT_SUSPEND);
+				ret = set_port_feature(hdev, portno | 0x500,
+						       USB_PORT_FEAT_TEST);
+				ret = get_port_status(hdev, portno,
+						      &port_status);
+			}
+			if (HostTest == 6) {
+				dev_info(hub_dev, "Testing "
+					 "HS_HOST_PORT_SUSPEND_RESUME\n");
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_C_CONNECTION);
+				ret = set_port_feature(hdev, portno,
+						     USB_PORT_FEAT_SUSPEND);
+				msleep(3000);
+				ret = clear_port_feature(hdev, portno,
+						 USB_PORT_FEAT_SUSPEND);
+				HostTest = 0;
+			}
+		}
+#endif
  loop_autopm:
 		/* Balance the usb_autopm_get_interface() above */
 		usb_autopm_put_interface_no_suspend(intf);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index ca717da..b2da64c 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1770,6 +1770,9 @@
 		goto free_interfaces;
 	}
 
+	dev->actconfig = cp;
+	if (cp)
+		usb_notify_config_device(dev);
 	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 			      USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
 			      NULL, 0, USB_CTRL_SET_TIMEOUT);
@@ -1777,11 +1780,11 @@
 		/* All the old state is gone, so what else can we do?
 		 * The device is probably useless now anyway.
 		 */
-		cp = NULL;
+		dev->actconfig = cp = NULL;
 	}
 
-	dev->actconfig = cp;
 	if (!cp) {
+		usb_notify_config_device(dev);
 		usb_set_device_state(dev, USB_STATE_ADDRESS);
 		usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
 		mutex_unlock(hcd->bandwidth_mutex);
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index 7728c91..617a364 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -58,6 +58,12 @@
 	mutex_unlock(&usbfs_mutex);
 }
 
+void usb_notify_config_device(struct usb_device *udev)
+{
+	blocking_notifier_call_chain(&usb_notifier_list,
+			USB_DEVICE_CONFIG, udev);
+}
+
 void usb_notify_add_bus(struct usb_bus *ubus)
 {
 	blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus);
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index e8cdce5..7bb6747 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -59,6 +59,11 @@
 	     le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
 		return 0;
 
+	/* OTG PET device is always targeted (see OTG 2.0 ECN 6.4.2) */
+	if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
+	     le16_to_cpu(dev->descriptor.idProduct) == 0x0200))
+		return 1;
+
 	/* NOTE: can't use usb_match_id() since interface caches
 	 * aren't set up yet. this is cut/paste from that code.
 	 */
@@ -92,7 +97,30 @@
 		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
 		    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
 			continue;
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+		/*Hub is targeted device,so code execution should reach here */
+		if (USB_CLASS_HUB == dev->descriptor.bDeviceClass) {
+			/* count the tiers and if it is more than 6, return 0 */
+			unsigned char tier = 0;
+			struct usb_device *root_hub;
 
+			root_hub = dev->bus->root_hub;
+			while ((dev->parent != NULL) && /* root hub not count */
+				(dev->parent != root_hub) &&
+				(tier != 6))  {/* interal hub not count */
+				tier++;
+				dev = dev->parent;
+			}
+
+			if (tier == 6) {
+				dev_err(&dev->dev, "5 tier of hubs reached,"
+					" newly added hub will not be"
+					" supported!\n");
+				hub_tier = 1;
+				return 0;
+			}
+		}
+#endif
 		return 1;
 	}
 
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 4c65eb6..200dc9b 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -150,6 +150,9 @@
 	/* INTEL VALUE SSD */
 	{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
 
+	/* Protocol and OTG Electrical Test Device */
+	{ USB_DEVICE(0x1a0a, 0x0200), .driver_info = USB_QUIRK_OTG_PET },
+
 	{ }  /* terminating entry must be last */
 };
 
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 71648dc..4f40547 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -104,6 +104,10 @@
 }
 #endif
 
+#ifdef CONFIG_USB_OTG
+extern void usb_hnp_polling_work(struct work_struct *work);
+#endif
+
 extern struct bus_type usb_bus_type;
 extern struct device_type usb_device_type;
 extern struct device_type usb_if_device_type;
@@ -153,6 +157,7 @@
 /* internal notify stuff */
 extern void usb_notify_add_device(struct usb_device *udev);
 extern void usb_notify_remove_device(struct usb_device *udev);
+extern void usb_notify_config_device(struct usb_device *udev);
 extern void usb_notify_add_bus(struct usb_bus *ubus);
 extern void usb_notify_remove_bus(struct usb_bus *ubus);
 
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index d441fe4..3227508 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -26,7 +26,9 @@
 # PCI doesn't provide nops if CONFIG_PCI isn't enabled.
 ##
 
-obj-$(CONFIG_USB_DWC3)		+= dwc3-omap.o
+
+obj-$(CONFIG_USB_DWC3_OMAP)	+= dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_MSM)	+= dwc3-msm.o
 
 ##
 # REVISIT Samsung Exynos platform needs the clk API which isn't
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
new file mode 100644
index 0000000..4e6091e
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -0,0 +1,879 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/list.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/msm_hsusb.h>
+
+#include "core.h"
+#include "gadget.h"
+
+/**
+ *  USB DBM Hardware registers.
+ *
+ */
+#define DBM_EP_CFG(n)		(0x00 + 4 * (n))
+#define DBM_DATA_FIFO(n)	(0x10 + 4 * (n))
+#define DBM_DATA_FIFO_SIZE(n)	(0x20 + 4 * (n))
+#define DBM_DATA_FIFO_EN	(0x30)
+#define DBM_GEVNTADR		(0x34)
+#define DBM_GEVNTSIZ		(0x38)
+#define DBM_DBG_CNFG		(0x3C)
+#define DBM_HW_TRB0_EP(n)	(0x40 + 4 * (n))
+#define DBM_HW_TRB1_EP(n)	(0x50 + 4 * (n))
+#define DBM_HW_TRB2_EP(n)	(0x60 + 4 * (n))
+#define DBM_HW_TRB3_EP(n)	(0x70 + 4 * (n))
+#define DBM_PIPE_CFG		(0x80)
+#define DBM_SOFT_RESET		(0x84)
+
+/**
+ *  USB DBM  Hardware registers bitmask.
+ *
+ */
+/* DBM_EP_CFG */
+#define DBM_EN_EP		0x00000000
+#define DBM_USB3_EP_NUM		0x0000003E
+#define DBM_BAM_PIPE_NUM	0x000000C0
+#define DBM_PRODUCER		0x00000100
+#define DBM_DISABLE_WB		0x00000200
+#define DBM_INT_RAM_ACC		0x00000400
+
+/* DBM_DATA_FIFO_SIZE */
+#define DBM_DATA_FIFO_SIZE_MASK	0x0000ffff
+
+/* DBM_GEVNTSIZ */
+#define DBM_GEVNTSIZ_MASK	0x0000ffff
+
+/* DBM_DBG_CNFG */
+#define DBM_ENABLE_IOC_MASK	0x0000000f
+
+/* DBM_SOFT_RESET */
+#define DBM_SFT_RST_EP0		0x00000001
+#define DBM_SFT_RST_EP1		0x00000002
+#define DBM_SFT_RST_EP2		0x00000004
+#define DBM_SFT_RST_EP3		0x00000008
+#define DBM_SFT_RST_EPS		0x0000000F
+#define DBM_SFT_RST		0x80000000
+
+#define DBM_MAX_EPS		4
+
+struct dwc3_msm_req_complete {
+	struct list_head list_item;
+	struct usb_request *req;
+	void (*orig_complete)(struct usb_ep *ep,
+			      struct usb_request *req);
+};
+
+struct dwc3_msm {
+	struct platform_device *dwc3;
+	struct device *dev;
+	void __iomem *base;
+	u32 resource_size;
+	int dbm_num_eps;
+	u8 ep_num_mapping[DBM_MAX_EPS];
+	const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
+	struct list_head req_complete_list;
+};
+
+static struct dwc3_msm *context;
+
+/**
+ *
+ * Read register with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ *
+ * @return u32
+ */
+static inline u32 dwc3_msm_read_reg(void *base, u32 offset)
+{
+	u32 val = ioread32(base + offset);
+	return val;
+}
+
+/**
+ * Read register masked field with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ *
+ * @return u32
+ */
+static inline u32 dwc3_msm_read_reg_field(void *base,
+					  u32 offset,
+					  const u32 mask)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 val = ioread32(base + offset);
+	val &= mask;		/* clear other bits */
+	val >>= shift;
+	return val;
+}
+
+/**
+ *
+ * Write register with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @val - value to write.
+ *
+ */
+static inline void dwc3_msm_write_reg(void *base, u32 offset, u32 val)
+{
+	iowrite32(val, base + offset);
+}
+
+/**
+ * Write register masked field with debug info.
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ * @val - value to write.
+ *
+ */
+static inline void dwc3_msm_write_reg_field(void *base, u32 offset,
+					    const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = ioread32(base + offset);
+
+	tmp &= ~mask;		/* clear written bits */
+	val = tmp | (val << shift);
+	iowrite32(val, base + offset);
+}
+
+/**
+ * Return DBM EP number which is not already configured.
+ *
+ */
+static int dwc3_msm_find_avail_dbm_ep(void)
+{
+	int i;
+
+	for (i = 0; i < context->dbm_num_eps; i++)
+		if (!context->ep_num_mapping[i])
+			return i;
+
+	return -ENODEV; /* Not found */
+}
+
+/**
+ * Return DBM EP number according to usb endpoint number.
+ *
+ */
+static int dwc3_msm_find_matching_dbm_ep(u8 usb_ep)
+{
+	int i;
+
+	for (i = 0; i < context->dbm_num_eps; i++)
+		if (context->ep_num_mapping[i] == usb_ep)
+			return i;
+
+	return -ENODEV; /* Not found */
+}
+
+/**
+ * Return number of configured DBM endpoints.
+ *
+ */
+static int dwc3_msm_configured_dbm_ep_num(void)
+{
+	int i;
+	int count = 0;
+
+	for (i = 0; i < context->dbm_num_eps; i++)
+		if (context->ep_num_mapping[i])
+			count++;
+
+	return count;
+}
+
+/**
+ * Configure the DBM with the USB3 core event buffer.
+ * This function is called by the SNPS UDC upon initialization.
+ *
+ * @addr - address of the event buffer.
+ * @size - size of the event buffer.
+ *
+ */
+static int dwc3_msm_event_buffer_config(u32 addr, u16 size)
+{
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dwc3_msm_write_reg(context->base, DBM_GEVNTADR, addr);
+	dwc3_msm_write_reg_field(context->base, DBM_GEVNTSIZ,
+		DBM_GEVNTSIZ_MASK, size);
+
+	return 0;
+}
+
+/**
+ * Reset the DBM registers upon initialization.
+ *
+ */
+static int dwc3_msm_dbm_soft_reset(void)
+{
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+		DBM_SFT_RST, 1);
+
+	return 0;
+}
+
+/**
+ * Soft reset specific DBM ep.
+ * This function is called by the function driver upon events
+ * such as transfer aborting, USB re-enumeration and USB
+ * disconnection.
+ *
+ * @dbm_ep - DBM ep number.
+ * @enter_reset - should we enter a reset state or get out of it.
+ *
+ */
+static int dwc3_msm_dbm_ep_soft_reset(u8 dbm_ep, bool enter_reset)
+{
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	if (dbm_ep >= context->dbm_num_eps) {
+		dev_err(context->dev,
+				"%s: Invalid DBM ep index\n", __func__);
+		return -ENODEV;
+	}
+
+	if (enter_reset) {
+		dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+			DBM_SFT_RST_EPS, 1 << dbm_ep);
+	} else {
+		dwc3_msm_write_reg_field(context->base, DBM_SOFT_RESET,
+			DBM_SFT_RST_EPS, 0);
+	}
+
+	return 0;
+}
+
+/**
+ * Configure a USB DBM ep to work in BAM mode.
+ *
+ *
+ * @usb_ep - USB physical EP number.
+ * @producer - producer/consumer.
+ * @disable_wb - disable write back to system memory.
+ * @internal_mem - use internal USB memory for data fifo.
+ * @ioc - enable interrupt on completion.
+ *
+ * @return int - DBM ep number.
+ */
+static int dwc3_msm_dbm_ep_config(u8 usb_ep, u8 bam_pipe,
+				  bool producer, bool disable_wb,
+				  bool internal_mem, bool ioc)
+{
+	u8 dbm_ep;
+	u8 ioc_mask;
+
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dbm_ep = dwc3_msm_find_avail_dbm_ep();
+	if (dbm_ep < 0) {
+		dev_err(context->dev, "%s: No more DBM eps\n", __func__);
+		return -ENODEV;
+	}
+
+	context->ep_num_mapping[dbm_ep] = usb_ep;
+
+	/* First, reset the dbm endpoint */
+	dwc3_msm_dbm_ep_soft_reset(dbm_ep, false);
+
+	ioc_mask = dwc3_msm_read_reg_field(context->base, DBM_DBG_CNFG,
+		DBM_ENABLE_IOC_MASK);
+	ioc_mask &= ~(ioc << dbm_ep); /* Clear ioc bit for dbm_ep */
+	/* Set ioc bit for dbm_ep if needed */
+	dwc3_msm_write_reg_field(context->base, DBM_DBG_CNFG,
+		DBM_ENABLE_IOC_MASK, ioc_mask | (ioc << dbm_ep));
+
+	dwc3_msm_write_reg(context->base, DBM_EP_CFG(dbm_ep),
+		producer | disable_wb | internal_mem);
+	dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+		DBM_USB3_EP_NUM, usb_ep);
+	dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+		DBM_BAM_PIPE_NUM, bam_pipe);
+	dwc3_msm_write_reg_field(context->base, DBM_EP_CFG(dbm_ep),
+		DBM_EN_EP, 1);
+
+	return dbm_ep;
+}
+
+/**
+ * Configure a USB DBM ep to work in normal mode.
+ *
+ * @usb_ep - USB ep number.
+ *
+ */
+static int dwc3_msm_dbm_ep_unconfig(u8 usb_ep)
+{
+	u8 dbm_ep;
+
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dbm_ep = dwc3_msm_find_matching_dbm_ep(usb_ep);
+
+	if (dbm_ep < 0) {
+		dev_err(context->dev,
+				"%s: Invalid usb ep index\n", __func__);
+		return -ENODEV;
+	}
+
+	context->ep_num_mapping[dbm_ep] = 0;
+
+	dwc3_msm_write_reg(context->base, DBM_EP_CFG(dbm_ep), 0);
+
+	/* Reset the dbm endpoint */
+	dwc3_msm_dbm_ep_soft_reset(dbm_ep, true);
+
+	return 0;
+}
+
+/**
+ * Configure the DBM with the BAM's data fifo.
+ * This function is called by the USB BAM Driver
+ * upon initialization.
+ *
+ * @ep - pointer to usb endpoint.
+ * @addr - address of data fifo.
+ * @size - size of data fifo.
+ *
+ */
+int msm_data_fifo_config(struct usb_ep *ep, u32 addr, u32 size)
+{
+	u8 dbm_ep;
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+
+	dev_dbg(context->dev, "%s\n", __func__);
+
+	dbm_ep = dwc3_msm_find_matching_dbm_ep(dep->number);
+
+	if (dbm_ep >= context->dbm_num_eps) {
+		dev_err(context->dev,
+				"%s: Invalid DBM ep index\n", __func__);
+		return -ENODEV;
+	}
+
+	dwc3_msm_write_reg(context->base, DBM_DATA_FIFO(dbm_ep), addr);
+	dwc3_msm_write_reg_field(context->base, DBM_DATA_FIFO_SIZE(dbm_ep),
+		DBM_DATA_FIFO_SIZE_MASK, size);
+
+	return 0;
+}
+
+/**
+* Cleanups for msm endpoint on request complete.
+*
+* Also call original request complete.
+*
+* @usb_ep - pointer to usb_ep instance.
+* @request - pointer to usb_request instance.
+*
+* @return int - 0 on success, negetive on error.
+*/
+static void dwc3_msm_req_complete_func(struct usb_ep *ep,
+				       struct usb_request *request)
+{
+	struct dwc3_request *req = to_dwc3_request(request);
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct dwc3_msm_req_complete *req_complete = NULL;
+
+	/* Find original request complete function and remove it from list */
+	list_for_each_entry(req_complete,
+				&context->req_complete_list,
+				list_item) {
+		if (req_complete->req == request)
+			break;
+	}
+	if (!req_complete || req_complete->req != request) {
+		dev_err(dep->dwc->dev, "%s: could not find the request\n",
+					__func__);
+		return;
+	}
+	list_del(&req_complete->list_item);
+
+	/*
+	 * Release another one TRB to the pool since DBM queue took 2 TRBs
+	 * (normal and link), and the dwc3/gadget.c :: dwc3_gadget_giveback
+	 * released only one.
+	 */
+	if (req->queued)
+		dep->busy_slot++;
+
+	/* Unconfigure dbm ep */
+	dwc3_msm_dbm_ep_unconfig(dep->number);
+
+	/*
+	 * If this is the last endpoint we unconfigured, than reset also
+	 * the event buffers.
+	 */
+	if (0 == dwc3_msm_configured_dbm_ep_num())
+		dwc3_msm_event_buffer_config(0, 0);
+
+	/*
+	 * Call original complete function, notice that dwc->lock is already
+	 * taken by the caller of this function (dwc3_gadget_giveback()).
+	 */
+	request->complete = req_complete->orig_complete;
+	request->complete(ep, request);
+
+	kfree(req_complete);
+}
+
+/**
+* Helper function.
+* See the header of the dwc3_msm_ep_queue function.
+*
+* @dwc3_ep - pointer to dwc3_ep instance.
+* @req - pointer to dwc3_request instance.
+*
+* @return int - 0 on success, negetive on error.
+*/
+static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+	struct dwc3_trb_hw *trb_hw;
+	struct dwc3_trb_hw *trb_link_hw;
+	struct dwc3_trb trb;
+	struct dwc3_gadget_ep_cmd_params params;
+	u32 cmd;
+	int ret = 0;
+
+	if ((req->request.udc_priv & MSM_IS_FINITE_TRANSFER) &&
+	    (req->request.length > 0)) {
+		/* Map the request to a DMA. */
+		dwc3_map_buffer_to_dma(req);
+	}
+
+	/* We push the request to the dep->req_queued list to indicate that
+	 * this request is issued with start transfer. The request will be out
+	 * from this list in 2 cases. The first is that the transfer will be
+	 * completed (not if the transfer is endless using a circular TRBs with
+	 * with link TRB). The second case is an option to do stop stransfer,
+	 * this can be initiated by the function driver when calling dequeue.
+	 */
+	req->queued = true;
+	list_add_tail(&req->list, &dep->req_queued);
+
+	/* First, prepare a normal TRB, point to the fake buffer */
+	trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+	dep->free_slot++;
+	memset(&trb, 0, sizeof(trb));
+
+	req->trb = trb_hw;
+
+	trb.bplh = req->request.dma;
+	trb.lst = 0;
+	trb.trbctl = DWC3_TRBCTL_NORMAL;
+	trb.length = req->request.length;
+	trb.hwo = true;
+
+	dwc3_trb_to_hw(&trb, trb_hw);
+	req->trb_dma = dep->trb_pool_dma;
+
+	/* Second, prepare a Link TRB that points to the first TRB*/
+	trb_link_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+	dep->free_slot++;
+	memset(&trb, 0, sizeof(trb));
+
+	trb.bplh = dep->trb_pool_dma;
+	trb.trbctl = DWC3_TRBCTL_LINK_TRB;
+	trb.hwo = true;
+
+	dwc3_trb_to_hw(&trb, trb_link_hw);
+
+	/*
+	 * Now start the transfer
+	 */
+	memset(&params, 0, sizeof(params));
+	params.param0 = upper_32_bits(req->trb_dma);
+	params.param1 = lower_32_bits(req->trb_dma);
+	cmd = DWC3_DEPCMD_STARTTRANSFER;
+	ret = dwc3_send_gadget_ep_cmd(dep->dwc, dep->number, cmd, &params);
+	if (ret < 0) {
+		dev_dbg(dep->dwc->dev,
+			"%s: failed to send STARTTRANSFER command\n",
+			__func__);
+
+		dwc3_unmap_buffer_from_dma(req);
+		list_del(&req->list);
+		return ret;
+	}
+
+	return ret;
+}
+
+/**
+* Queue a usb request to the DBM endpoint.
+* This function should be called after the endpoint
+* was enabled by the ep_enable.
+*
+* This function prepares special structure of TRBs which
+* is familier with the DBM HW, so it will possible to use
+* this endpoint in DBM mode.
+*
+* The TRBs prepared by this function, is one normal TRB
+* which point to a fake buffer, followed by a link TRB
+* that points to the first TRB.
+*
+* The API of this function follow the regular API of
+* usb_ep_queue (see usb_ep_ops in include/linuk/usb/gadget.h).
+*
+* @usb_ep - pointer to usb_ep instance.
+* @request - pointer to usb_request instance.
+* @gfp_flags - possible flags.
+*
+* @return int - 0 on success, negetive on error.
+*/
+static int dwc3_msm_ep_queue(struct usb_ep *ep,
+			     struct usb_request *request, gfp_t gfp_flags)
+{
+	struct dwc3_request *req = to_dwc3_request(request);
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct dwc3 *dwc = dep->dwc;
+	struct dwc3_msm_req_complete *req_complete;
+	unsigned long flags;
+	int ret = 0;
+	u8 bam_pipe;
+	bool producer;
+	bool disable_wb;
+	bool internal_mem;
+	bool ioc;
+
+	if (!(request->udc_priv & MSM_SPS_MODE)) {
+		/* Not SPS mode, call original queue */
+		dev_vdbg(dwc->dev, "%s: not sps mode, use regular queue\n",
+					__func__);
+
+		return (context->original_ep_ops[dep->number])->queue(ep,
+								request,
+								gfp_flags);
+	}
+
+	if (!dep->endpoint.desc) {
+		dev_err(dwc->dev,
+			"%s: trying to queue request %p to disabled ep %s\n",
+			__func__, request, ep->name);
+		return -EPERM;
+	}
+
+	if (dep->number == 0 || dep->number == 1) {
+		dev_err(dwc->dev,
+			"%s: trying to queue dbm request %p to control ep %s\n",
+			__func__, request, ep->name);
+		return -EPERM;
+	}
+
+	if (dep->free_slot > 0 || dep->busy_slot > 0 ||
+		!list_empty(&dep->request_list) ||
+		!list_empty(&dep->req_queued)) {
+
+		dev_err(dwc->dev,
+			"%s: trying to queue dbm request %p tp ep %s\n",
+			__func__, request, ep->name);
+		return -EPERM;
+	}
+
+	/*
+	 * Override req->complete function, but before doing that,
+	 * store it's original pointer in the req_complete_list.
+	 */
+	req_complete = kzalloc(sizeof(*req_complete), GFP_KERNEL);
+	if (!req_complete) {
+		dev_err(dep->dwc->dev, "%s: not enough memory\n", __func__);
+		return -ENOMEM;
+	}
+	req_complete->req = request;
+	req_complete->orig_complete = request->complete;
+	list_add_tail(&req_complete->list_item, &context->req_complete_list);
+	request->complete = dwc3_msm_req_complete_func;
+
+	/*
+	 * Configure dbm event buffers if this is the first
+	 * dbm endpoint we about to configure.
+	 */
+	if (0 == dwc3_msm_configured_dbm_ep_num())
+		dwc3_msm_event_buffer_config(dwc->ev_buffs[0]->dma,
+					     dwc->ev_buffs[0]->length);
+
+	/*
+	 * Configure the DBM endpoint
+	 */
+	bam_pipe = (request->udc_priv & MSM_PIPE_ID_MASK);
+	producer = ((request->udc_priv & MSM_PRODUCER) ? true : false);
+	disable_wb = ((request->udc_priv & MSM_DISABLE_WB) ? true : false);
+	internal_mem = ((request->udc_priv & MSM_INTERNAL_MEM) ? true : false);
+	ioc = ((request->udc_priv & MSM_ETD_IOC) ? true : false);
+
+	ret = dwc3_msm_dbm_ep_config(dep->number,
+					bam_pipe, producer,
+					disable_wb, internal_mem, ioc);
+	if (ret < 0) {
+		dev_err(context->dev,
+			"error %d after calling dwc3_msm_dbm_ep_config\n",
+			ret);
+		return ret;
+	}
+
+	dev_vdbg(dwc->dev, "%s: queing request %p to ep %s length %d\n",
+			__func__, request, ep->name, request->length);
+
+	/*
+	 * We must obtain the lock of the dwc3 core driver,
+	 * including disabling interrupts, so we will be sure
+	 * that we are the only ones that configure the HW device
+	 * core and ensure that we queuing the request will finish
+	 * as soon as possible so we will release back the lock.
+	 */
+	spin_lock_irqsave(&dwc->lock, flags);
+	ret = __dwc3_msm_ep_queue(dep, req);
+	spin_unlock_irqrestore(&dwc->lock, flags);
+	if (ret < 0) {
+		dev_err(context->dev,
+			"error %d after calling __dwc3_msm_ep_queue\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Configure MSM endpoint.
+ * This function do specific configurations
+ * to an endpoint which need specific implementaion
+ * in the MSM architecture.
+ *
+ * This function should be called by usb function/class
+ * layer which need a support from the specific MSM HW
+ * which wrap the USB3 core. (like DBM specific endpoints)
+ *
+ * @ep - a pointer to some usb_ep instance
+ *
+ * @return int - 0 on success, negetive on error.
+ */
+int msm_ep_config(struct usb_ep *ep)
+{
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct usb_ep_ops *new_ep_ops;
+
+	/* Save original ep ops for future restore*/
+	if (context->original_ep_ops[dep->number]) {
+		dev_err(context->dev,
+			"ep [%s,%d] already configured as msm endpoint\n",
+			ep->name, dep->number);
+		return -EPERM;
+	}
+	context->original_ep_ops[dep->number] = ep->ops;
+
+	/* Set new usb ops as we like */
+	new_ep_ops = kzalloc(sizeof(struct usb_ep_ops), GFP_KERNEL);
+	if (!new_ep_ops) {
+		dev_err(context->dev,
+			"%s: unable to allocate mem for new usb ep ops\n",
+			__func__);
+		return -ENOMEM;
+	}
+	(*new_ep_ops) = (*ep->ops);
+	new_ep_ops->queue = dwc3_msm_ep_queue;
+	ep->ops = new_ep_ops;
+
+	/*
+	 * Do HERE more usb endpoint configurations
+	 * which are specific to MSM.
+	 */
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_ep_config);
+
+/**
+ * Un-configure MSM endpoint.
+ * Tear down configurations done in the
+ * dwc3_msm_ep_config function.
+ *
+ * @ep - a pointer to some usb_ep instance
+ *
+ * @return int - 0 on success, negetive on error.
+ */
+int msm_ep_unconfig(struct usb_ep *ep)
+{
+	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct usb_ep_ops *old_ep_ops;
+
+	/* Restore original ep ops */
+	if (!context->original_ep_ops[dep->number]) {
+		dev_err(context->dev,
+			"ep [%s,%d] was not configured as msm endpoint\n",
+			ep->name, dep->number);
+		return -EINVAL;
+	}
+	old_ep_ops = (struct usb_ep_ops	*)ep->ops;
+	ep->ops = context->original_ep_ops[dep->number];
+	context->original_ep_ops[dep->number] = NULL;
+	kfree(old_ep_ops);
+
+	/*
+	 * Do HERE more usb endpoint un-configurations
+	 * which are specific to MSM.
+	 */
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_ep_unconfig);
+
+static int __devinit dwc3_msm_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct platform_device *dwc3;
+	struct dwc3_msm *msm;
+	struct resource *res;
+	int ret = 0;
+
+	msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL);
+	if (!msm) {
+		dev_err(&pdev->dev, "not enough memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, msm);
+	context = msm;
+
+	INIT_LIST_HEAD(&msm->req_complete_list);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "missing memory base resource\n");
+		return -ENODEV;
+	}
+
+	msm->base = devm_ioremap_nocache(&pdev->dev, res->start,
+		resource_size(res));
+	if (!msm->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		return -ENODEV;
+	}
+
+	dwc3 = platform_device_alloc("dwc3-msm", -1);
+	if (!dwc3) {
+		dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
+		return -ENOMEM;
+	}
+
+	dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
+
+	dwc3->dev.parent = &pdev->dev;
+	dwc3->dev.dma_mask = pdev->dev.dma_mask;
+	dwc3->dev.dma_parms = pdev->dev.dma_parms;
+	msm->resource_size = resource_size(res);
+	msm->dev = &pdev->dev;
+	msm->dwc3 = dwc3;
+
+	if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
+				 &msm->dbm_num_eps)) {
+		dev_err(&pdev->dev,
+			"unable to read platform data num of dbm eps\n");
+		msm->dbm_num_eps = DBM_MAX_EPS;
+	}
+
+	if (msm->dbm_num_eps > DBM_MAX_EPS) {
+		dev_err(&pdev->dev,
+			"Driver doesn't support number of DBM EPs. "
+			"max: %d, dbm_num_eps: %d\n",
+			DBM_MAX_EPS, msm->dbm_num_eps);
+		ret = -ENODEV;
+		goto err1;
+	}
+
+	ret = platform_device_add_resources(dwc3, pdev->resource,
+		pdev->num_resources);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
+		goto err1;
+	}
+
+	ret = platform_device_add(dwc3);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register dwc3 device\n");
+		goto err1;
+	}
+
+	/* Reset the DBM */
+	dwc3_msm_dbm_soft_reset();
+
+	return 0;
+
+err1:
+	platform_device_put(dwc3);
+
+	return ret;
+}
+
+static int __devexit dwc3_msm_remove(struct platform_device *pdev)
+{
+	struct dwc3_msm	*msm = platform_get_drvdata(pdev);
+
+	platform_device_unregister(msm->dwc3);
+
+	return 0;
+}
+
+static const struct of_device_id of_dwc3_matach[] = {
+	{
+		.compatible = "qcom,dwc-usb3-msm",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_matach);
+
+static struct platform_driver dwc3_msm_driver = {
+	.probe		= dwc3_msm_probe,
+	.remove		= __devexit_p(dwc3_msm_remove),
+	.driver		= {
+		.name	= "msm-dwc3",
+		.of_match_table	= of_dwc3_matach,
+	},
+};
+
+MODULE_LICENSE("GPLV2");
+MODULE_DESCRIPTION("DesignWare USB3 MSM Glue Layer");
+
+static int __devinit dwc3_msm_init(void)
+{
+	return platform_driver_register(&dwc3_msm_driver);
+}
+module_init(dwc3_msm_init);
+
+static void __exit dwc3_msm_exit(void)
+{
+	platform_driver_unregister(&dwc3_msm_driver);
+}
+module_exit(dwc3_msm_exit);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index a04b3f5..14b07e5 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -83,7 +83,7 @@
 config USB_GADGET_VBUS_DRAW
 	int "Maximum VBUS Power usage (2-500 mA)"
 	range 2 500
-	default 2
+	default 500
 	help
 	   Some devices need to draw power from USB when they are
 	   configured, perhaps to operate circuitry or to recharge
@@ -494,10 +494,33 @@
 	  dynamically linked module called "ci13xxx_msm" and force all
 	  gadget drivers to also be dynamically linked.
 
+config USB_CI13XXX_MSM_HSIC
+	tristate "MIPS HSIC CI13xxx for MSM"
+	depends on ARCH_MSM
+	select USB_GADGET_DUALSPEED
+	help
+	  MSM SoC has chipidea USB controller.  This driver uses
+	  ci13xxx_udc core. Support USB-HSIC core.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "ci13xxx_msm_hsic" and force all
+	  gadget drivers to also be dynamically linked.
+
 #
 # LAST -- dummy/emulated controller
 #
 
+config USB_MSM_72K
+	tristate "MSM 72K Device Controller"
+	depends on ARCH_MSM
+	select USB_GADGET_DUALSPEED
+	help
+	   USB gadget driver for Qualcomm MSM 72K architecture.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "msm72k" and force all
+	   gadget drivers to also be dynamically linked.
+
 config USB_DUMMY_HCD
 	tristate "Dummy HCD (DEVELOPMENT)"
 	depends on USB=y || (USB=m && USB_GADGET=m)
@@ -986,4 +1009,90 @@
 
 endchoice
 
+config USB_CSW_HACK
+	boolean "USB Mass storage csw hack Feature"
+	default y
+	help
+	 This csw hack feature is for increasing the performance of the mass
+	 storage
+
+config USB_MSC_PROFILING
+	bool "USB MSC performance profiling"
+	help
+	  If you say Y here, support will be added for collecting
+	  Mass-storage performance numbers at the VFS level.
+
+config MODEM_SUPPORT
+	boolean "modem support in generic serial function driver"
+	depends on USB_G_ANDROID
+	default y
+	help
+          This feature enables the modem functionality in the
+	  generic serial.
+	  adds interrupt endpoint support to send modem notifications
+	  to host.
+	  adds CDC descriptors to enumerate the generic serial as MODEM.
+	  adds CDC class requests to configure MODEM line settings.
+	  Say "y" to enable MODEM support in the generic serial driver.
+
+config RMNET_SMD_CTL_CHANNEL
+	string "RMNET control SMD channel name"
+	depends on USB_G_ANDROID && MSM_SMD
+	default ""
+	help
+	  Control SMD channel for transferring QMI messages
+
+config RMNET_SMD_DATA_CHANNEL
+	string "RMNET Data SMD channel name"
+	depends on USB_G_ANDROID && MSM_SMD
+	default ""
+	help
+	  Data SMD channel for transferring network data
+
+config RMNET_SDIO_CTL_CHANNEL
+       int "RMNET control SDIO channel id"
+       default 8
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       help
+         Control SDIO channel for transferring RMNET QMI messages
+
+config RMNET_SDIO_DATA_CHANNEL
+       int "RMNET Data SDIO channel id"
+       default 8
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       help
+         Data SDIO channel for transferring network data
+
+config RMNET_SMD_SDIO_CTL_CHANNEL
+       int "RMNET(sdio_smd) Control SDIO channel id"
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       default 8
+       help
+         Control SDIO channel for transferring QMI messages
+
+config RMNET_SMD_SDIO_DATA_CHANNEL
+       int "RMNET(sdio_smd) Data SDIO channel id"
+       default 8
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       help
+         Data SDIO channel for transferring network data
+
+config RMNET_SDIO_SMD_DATA_CHANNEL
+       string "RMNET(sdio_smd) Data SMD channel name"
+       depends on MSM_SDIO_CMUX && MSM_SDIO_DMUX
+       default "DATA40"
+       help
+	  Data SMD channel for transferring network data
+
+config USB_ANDROID_RMNET_CTRL_SMD
+       boolean "RmNet(BAM) control over SMD driver"
+       depends on MSM_SMD
+       help
+         Enabling this option adds rmnet control over SMD
+	 support to the android gadget. Rmnet is an
+	 alternative to CDC-ECM and Windows RNDIS.
+	 It uses QUALCOMM MSM Interface for control
+	 transfers. This option enables only control interface.
+	 Data interface used is BAM.
+
 endif # USB_GADGET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index b91866a6..c646c9f 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -29,8 +29,10 @@
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_MV_UDC)	+= mv_udc.o
 mv_udc-y			:= mv_udc_core.o
+obj-$(CONFIG_USB_CI13XXX_MSM_HSIC)	+= ci13xxx_msm_hsic.o
 obj-$(CONFIG_USB_CI13XXX_MSM)	+= ci13xxx_msm.o
 obj-$(CONFIG_USB_FUSB300)	+= fusb300_udc.o
+obj-$(CONFIG_USB_MSM_72K)	+= msm72k_udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 7a2af58..eefe95f 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -23,10 +23,12 @@
 #include <linux/kernel.h>
 #include <linux/utsname.h>
 #include <linux/platform_device.h>
+#include <linux/pm_qos.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/composite.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/android.h>
 
 #include "gadget_chips.h"
 
@@ -42,16 +44,33 @@
 #include "epautoconf.c"
 #include "composite.c"
 
+#include "f_diag.c"
+#include "f_rmnet_smd.c"
+#include "f_rmnet_sdio.c"
+#include "f_rmnet_smd_sdio.c"
+#include "f_rmnet.c"
 #include "f_mass_storage.c"
 #include "u_serial.c"
+#include "u_sdio.c"
+#include "u_smd.c"
+#include "u_bam.c"
+#include "u_rmnet_ctrl_smd.c"
+#include "u_ctrl_hsic.c"
+#include "u_data_hsic.c"
+#include "u_ctrl_hsuart.c"
+#include "u_data_hsuart.c"
+#include "f_serial.c"
 #include "f_acm.c"
 #include "f_adb.c"
+#include "f_ccid.c"
 #include "f_mtp.c"
 #include "f_accessory.c"
 #define USB_ETH_RNDIS y
 #include "f_rndis.c"
 #include "rndis.c"
 #include "u_ether.c"
+#include "u_bam_data.c"
+#include "f_mbim.c"
 
 MODULE_AUTHOR("Mike Lockwood");
 MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -106,8 +125,12 @@
 	bool enabled;
 	int disable_depth;
 	struct mutex mutex;
+	struct android_usb_platform_data *pdata;
+
 	bool connected;
 	bool sw_connected;
+	char pm_qos[5];
+	struct pm_qos_request pm_qos_req_dma;
 	struct work_struct work;
 };
 
@@ -154,14 +177,50 @@
 	.bNumConfigurations   = 1,
 };
 
+static struct usb_otg_descriptor otg_descriptor = {
+	.bLength =		sizeof otg_descriptor,
+	.bDescriptorType =	USB_DT_OTG,
+	.bmAttributes =		USB_OTG_SRP | USB_OTG_HNP,
+	.bcdOTG               = __constant_cpu_to_le16(0x0200),
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+	(struct usb_descriptor_header *) &otg_descriptor,
+	NULL,
+};
+
 static struct usb_configuration android_config_driver = {
 	.label		= "android",
 	.unbind		= android_unbind_config,
 	.bConfigurationValue = 1,
-	.bmAttributes	= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
-	.bMaxPower	= 0xFA, /* 500ma */
 };
 
+enum android_device_state {
+	USB_DISCONNECTED,
+	USB_CONNECTED,
+	USB_CONFIGURED,
+};
+
+static void android_pm_qos_update_latency(struct android_dev *dev, int vote)
+{
+	struct android_usb_platform_data *pdata = dev->pdata;
+	u32 swfi_latency = 0;
+	static int last_vote = -1;
+
+	if (!pdata || vote == last_vote
+		|| !pdata->swfi_latency)
+		return;
+
+	swfi_latency = pdata->swfi_latency + 1;
+	if (vote)
+		pm_qos_update_request(&dev->pm_qos_req_dma,
+				swfi_latency);
+	else
+		pm_qos_update_request(&dev->pm_qos_req_dma,
+				PM_QOS_DEFAULT_VALUE);
+	last_vote = vote;
+}
+
 static void android_work(struct work_struct *data)
 {
 	struct android_dev *dev = container_of(data, struct android_dev, work);
@@ -170,18 +229,53 @@
 	char *connected[2]    = { "USB_STATE=CONNECTED", NULL };
 	char *configured[2]   = { "USB_STATE=CONFIGURED", NULL };
 	char **uevent_envp = NULL;
+	static enum android_device_state last_uevent, next_state;
 	unsigned long flags;
+	int pm_qos_vote = -1;
 
 	spin_lock_irqsave(&cdev->lock, flags);
-	if (cdev->config)
+	if (cdev->config) {
 		uevent_envp = configured;
-	else if (dev->connected != dev->sw_connected)
+		next_state = USB_CONFIGURED;
+	} else if (dev->connected != dev->sw_connected) {
 		uevent_envp = dev->connected ? connected : disconnected;
+		next_state = dev->connected ? USB_CONNECTED : USB_DISCONNECTED;
+		if (dev->connected && strncmp(dev->pm_qos, "low", 3))
+			pm_qos_vote = 1;
+		else if (!dev->connected || !strncmp(dev->pm_qos, "low", 3))
+			pm_qos_vote = 0;
+	}
 	dev->sw_connected = dev->connected;
 	spin_unlock_irqrestore(&cdev->lock, flags);
 
+	if (pm_qos_vote != -1)
+		android_pm_qos_update_latency(dev, pm_qos_vote);
+
 	if (uevent_envp) {
+		/*
+		 * Some userspace modules, e.g. MTP, work correctly only if
+		 * CONFIGURED uevent is preceded by DISCONNECT uevent.
+		 * Check if we missed sending out a DISCONNECT uevent. This can
+		 * happen if host PC resets and configures device really quick.
+		 */
+		if (((uevent_envp == connected) &&
+		      (last_uevent != USB_DISCONNECTED)) ||
+		    ((uevent_envp == configured) &&
+		      (last_uevent == USB_CONFIGURED))) {
+			pr_info("%s: sent missed DISCONNECT event\n", __func__);
+			kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE,
+								disconnected);
+			msleep(20);
+		}
+		/*
+		 * Before sending out CONFIGURED uevent give function drivers
+		 * a chance to wakeup userspace threads and notify disconnect
+		 */
+		if (uevent_envp == configured)
+			msleep(50);
+
 		kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp);
+		last_uevent = next_state;
 		pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]);
 	} else {
 		pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
@@ -311,88 +405,410 @@
 }
 
 
-#define MAX_ACM_INSTANCES 4
-struct acm_function_config {
-	int instances;
+/*-------------------------------------------------------------------------*/
+/* Supported functions initialization */
+
+/* RMNET_SMD */
+static int rmnet_smd_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return rmnet_smd_bind_config(c);
+}
+
+static struct android_usb_function rmnet_smd_function = {
+	.name		= "rmnet_smd",
+	.bind_config	= rmnet_smd_function_bind_config,
 };
 
-static int
-acm_function_init(struct android_usb_function *f,
-		struct usb_composite_dev *cdev)
+/* RMNET_SDIO */
+static int rmnet_sdio_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
 {
-	f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL);
-	if (!f->config)
-		return -ENOMEM;
-
-	return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES);
+	return rmnet_sdio_function_add(c);
 }
 
+static struct android_usb_function rmnet_sdio_function = {
+	.name		= "rmnet_sdio",
+	.bind_config	= rmnet_sdio_function_bind_config,
+};
+
+/* RMNET_SMD_SDIO */
+static int rmnet_smd_sdio_function_init(struct android_usb_function *f,
+				 struct usb_composite_dev *cdev)
+{
+	return rmnet_smd_sdio_init();
+}
+
+static void rmnet_smd_sdio_function_cleanup(struct android_usb_function *f)
+{
+	rmnet_smd_sdio_cleanup();
+}
+
+static int rmnet_smd_sdio_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return rmnet_smd_sdio_function_add(c);
+}
+
+static struct device_attribute *rmnet_smd_sdio_attributes[] = {
+					&dev_attr_transport, NULL };
+
+static struct android_usb_function rmnet_smd_sdio_function = {
+	.name		= "rmnet_smd_sdio",
+	.init		= rmnet_smd_sdio_function_init,
+	.cleanup	= rmnet_smd_sdio_function_cleanup,
+	.bind_config	= rmnet_smd_sdio_bind_config,
+	.attributes	= rmnet_smd_sdio_attributes,
+};
+
+/*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */
+#define MAX_XPORT_STR_LEN 50
+static char rmnet_transports[MAX_XPORT_STR_LEN];
+
+static void rmnet_function_cleanup(struct android_usb_function *f)
+{
+	frmnet_cleanup();
+}
+
+static int rmnet_function_bind_config(struct android_usb_function *f,
+					 struct usb_configuration *c)
+{
+	int i;
+	int err = 0;
+	char *ctrl_name;
+	char *data_name;
+	char buf[MAX_XPORT_STR_LEN], *b;
+	static int rmnet_initialized, ports;
+
+	if (!rmnet_initialized) {
+		rmnet_initialized = 1;
+		strlcpy(buf, rmnet_transports, sizeof(buf));
+		b = strim(buf);
+		while (b) {
+			ctrl_name = strsep(&b, ",");
+			data_name = strsep(&b, ",");
+			if (ctrl_name && data_name) {
+				err = frmnet_init_port(ctrl_name, data_name);
+				if (err) {
+					pr_err("rmnet: Cannot open ctrl port:"
+						"'%s' data port:'%s'\n",
+						ctrl_name, data_name);
+					goto out;
+				}
+				ports++;
+			}
+		}
+
+		err = rmnet_gport_setup();
+		if (err) {
+			pr_err("rmnet: Cannot setup transports");
+			goto out;
+		}
+	}
+
+	for (i = 0; i < ports; i++) {
+		err = frmnet_bind_config(c, i);
+		if (err) {
+			pr_err("Could not bind rmnet%u config\n", i);
+			break;
+		}
+	}
+out:
+	return err;
+}
+
+static ssize_t rmnet_transports_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports);
+}
+
+static ssize_t rmnet_transports_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(rmnet_transports, buff, sizeof(rmnet_transports));
+
+	return size;
+}
+
+static struct device_attribute dev_attr_rmnet_transports =
+					__ATTR(transports, S_IRUGO | S_IWUSR,
+						rmnet_transports_show,
+						rmnet_transports_store);
+static struct device_attribute *rmnet_function_attributes[] = {
+					&dev_attr_rmnet_transports,
+					NULL };
+
+static struct android_usb_function rmnet_function = {
+	.name		= "rmnet",
+	.cleanup	= rmnet_function_cleanup,
+	.bind_config	= rmnet_function_bind_config,
+	.attributes	= rmnet_function_attributes,
+};
+
+
+/* MBIM - used with BAM */
+#define MAX_MBIM_INSTANCES 1
+
+static int mbim_function_init(struct android_usb_function *f,
+					 struct usb_composite_dev *cdev)
+{
+	return mbim_init(MAX_MBIM_INSTANCES);
+}
+
+static void mbim_function_cleanup(struct android_usb_function *f)
+{
+	fmbim_cleanup();
+}
+
+static int mbim_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return mbim_bind_config(c, 0);
+}
+
+static struct android_usb_function mbim_function = {
+	.name		= "usb_mbim",
+	.cleanup	= mbim_function_cleanup,
+	.bind_config	= mbim_function_bind_config,
+	.init		= mbim_function_init,
+};
+
+
+/* DIAG */
+static char diag_clients[32];	    /*enabled DIAG clients- "diag[,diag_mdm]" */
+static ssize_t clients_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(diag_clients, buff, sizeof(diag_clients));
+
+	return size;
+}
+
+static DEVICE_ATTR(clients, S_IWUSR, NULL, clients_store);
+static struct device_attribute *diag_function_attributes[] =
+					 { &dev_attr_clients, NULL };
+
+static int diag_function_init(struct android_usb_function *f,
+				 struct usb_composite_dev *cdev)
+{
+	return diag_setup();
+}
+
+static void diag_function_cleanup(struct android_usb_function *f)
+{
+	diag_cleanup();
+}
+
+static int diag_function_bind_config(struct android_usb_function *f,
+					struct usb_configuration *c)
+{
+	char *name;
+	char buf[32], *b;
+	int once = 0, err = -1;
+	int (*notify)(uint32_t, const char *);
+
+	strlcpy(buf, diag_clients, sizeof(buf));
+	b = strim(buf);
+
+	while (b) {
+		notify = NULL;
+		name = strsep(&b, ",");
+		/* Allow only first diag channel to update pid and serial no */
+		if (_android_dev->pdata && !once++)
+			notify = _android_dev->pdata->update_pid_and_serial_num;
+
+		if (name) {
+			err = diag_function_add(c, name, notify);
+			if (err)
+				pr_err("diag: Cannot open channel '%s'", name);
+		}
+	}
+
+	return err;
+}
+
+static struct android_usb_function diag_function = {
+	.name		= "diag",
+	.init		= diag_function_init,
+	.cleanup	= diag_function_cleanup,
+	.bind_config	= diag_function_bind_config,
+	.attributes	= diag_function_attributes,
+};
+
+/* SERIAL */
+static char serial_transports[32];	/*enabled FSERIAL ports - "tty[,sdio]"*/
+static ssize_t serial_transports_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(serial_transports, buff, sizeof(serial_transports));
+
+	return size;
+}
+
+static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
+static struct device_attribute *serial_function_attributes[] =
+					 { &dev_attr_transports, NULL };
+
+static void serial_function_cleanup(struct android_usb_function *f)
+{
+	gserial_cleanup();
+}
+
+static int serial_function_bind_config(struct android_usb_function *f,
+					struct usb_configuration *c)
+{
+	char *name;
+	char buf[32], *b;
+	int err = -1, i;
+	static int serial_initialized = 0, ports = 0;
+
+	if (serial_initialized)
+		goto bind_config;
+
+	serial_initialized = 1;
+	strlcpy(buf, serial_transports, sizeof(buf));
+	b = strim(buf);
+
+	while (b) {
+		name = strsep(&b, ",");
+
+		if (name) {
+			err = gserial_init_port(ports, name);
+			if (err) {
+				pr_err("serial: Cannot open port '%s'", name);
+				goto out;
+			}
+			ports++;
+		}
+	}
+	err = gport_setup(c);
+	if (err) {
+		pr_err("serial: Cannot setup transports");
+		goto out;
+	}
+
+bind_config:
+	for (i = 0; i < ports; i++) {
+		err = gser_bind_config(c, i);
+		if (err) {
+			pr_err("serial: bind_config failed for port %d", i);
+			goto out;
+		}
+	}
+
+out:
+	return err;
+}
+
+static struct android_usb_function serial_function = {
+	.name		= "serial",
+	.cleanup	= serial_function_cleanup,
+	.bind_config	= serial_function_bind_config,
+	.attributes	= serial_function_attributes,
+};
+
+/* ACM */
+static char acm_transports[32];	/*enabled ACM ports - "tty[,sdio]"*/
+static ssize_t acm_transports_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buff, size_t size)
+{
+	strlcpy(acm_transports, buff, sizeof(acm_transports));
+
+	return size;
+}
+
+static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store);
+static struct device_attribute *acm_function_attributes[] = {
+		&dev_attr_acm_transports, NULL };
+
 static void acm_function_cleanup(struct android_usb_function *f)
 {
 	gserial_cleanup();
-	kfree(f->config);
-	f->config = NULL;
 }
 
-static int
-acm_function_bind_config(struct android_usb_function *f,
-		struct usb_configuration *c)
+static int acm_function_bind_config(struct android_usb_function *f,
+					struct usb_configuration *c)
 {
-	int i;
-	int ret = 0;
-	struct acm_function_config *config = f->config;
+	char *name;
+	char buf[32], *b;
+	int err = -1, i;
+	static int acm_initialized, ports;
 
-	for (i = 0; i < config->instances; i++) {
-		ret = acm_bind_config(c, i);
-		if (ret) {
-			pr_err("Could not bind acm%u config\n", i);
-			break;
+	if (acm_initialized)
+		goto bind_config;
+
+	acm_initialized = 1;
+	strlcpy(buf, acm_transports, sizeof(buf));
+	b = strim(buf);
+
+	while (b) {
+		name = strsep(&b, ",");
+
+		if (name) {
+			err = acm_init_port(ports, name);
+			if (err) {
+				pr_err("acm: Cannot open port '%s'", name);
+				goto out;
+			}
+			ports++;
+		}
+	}
+	err = acm_port_setup(c);
+	if (err) {
+		pr_err("acm: Cannot setup transports");
+		goto out;
+	}
+
+bind_config:
+	for (i = 0; i < ports; i++) {
+		err = acm_bind_config(c, i);
+		if (err) {
+			pr_err("acm: bind_config failed for port %d", i);
+			goto out;
 		}
 	}
 
-	return ret;
+out:
+	return err;
 }
-
-static ssize_t acm_instances_show(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct android_usb_function *f = dev_get_drvdata(dev);
-	struct acm_function_config *config = f->config;
-	return sprintf(buf, "%d\n", config->instances);
-}
-
-static ssize_t acm_instances_store(struct device *dev,
-		struct device_attribute *attr, const char *buf, size_t size)
-{
-	struct android_usb_function *f = dev_get_drvdata(dev);
-	struct acm_function_config *config = f->config;
-	int value;
-
-	sscanf(buf, "%d", &value);
-	if (value > MAX_ACM_INSTANCES)
-		value = MAX_ACM_INSTANCES;
-	config->instances = value;
-	return size;
-}
-
-static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show,
-						 acm_instances_store);
-static struct device_attribute *acm_function_attributes[] = {
-	&dev_attr_instances,
-	NULL
-};
-
 static struct android_usb_function acm_function = {
 	.name		= "acm",
-	.init		= acm_function_init,
 	.cleanup	= acm_function_cleanup,
 	.bind_config	= acm_function_bind_config,
 	.attributes	= acm_function_attributes,
 };
 
+/* CCID */
+static int ccid_function_init(struct android_usb_function *f,
+					struct usb_composite_dev *cdev)
+{
+	return ccid_setup();
+}
 
-static int
-mtp_function_init(struct android_usb_function *f,
+static void ccid_function_cleanup(struct android_usb_function *f)
+{
+	ccid_cleanup();
+}
+
+static int ccid_function_bind_config(struct android_usb_function *f,
+						struct usb_configuration *c)
+{
+	return ccid_bind_config(c);
+}
+
+static struct android_usb_function ccid_function = {
+	.name		= "ccid",
+	.init		= ccid_function_init,
+	.cleanup	= ccid_function_cleanup,
+	.bind_config	= ccid_function_bind_config,
+};
+
+static int mtp_function_init(struct android_usb_function *f,
 		struct usb_composite_dev *cdev)
 {
 	return mtp_setup();
@@ -403,16 +819,13 @@
 	mtp_cleanup();
 }
 
-static int
-mtp_function_bind_config(struct android_usb_function *f,
+static int mtp_function_bind_config(struct android_usb_function *f,
 		struct usb_configuration *c)
 {
 	return mtp_bind_config(c, false);
 }
 
-static int
-ptp_function_init(struct android_usb_function *f,
-		struct usb_composite_dev *cdev)
+static int ptp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
 {
 	/* nothing to do - initialization is handled by mtp_function_init */
 	return 0;
@@ -423,9 +836,7 @@
 	/* nothing to do - cleanup is handled by mtp_function_cleanup */
 }
 
-static int
-ptp_function_bind_config(struct android_usb_function *f,
-		struct usb_configuration *c)
+static int ptp_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
 {
 	return mtp_bind_config(c, true);
 }
@@ -527,7 +938,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *config = f->config;
-	return sprintf(buf, "%s\n", config->manufacturer);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", config->manufacturer);
 }
 
 static ssize_t rndis_manufacturer_store(struct device *dev,
@@ -538,7 +950,8 @@
 
 	if (size >= sizeof(config->manufacturer))
 		return -EINVAL;
-	if (sscanf(buf, "%s", config->manufacturer) == 1)
+
+	if (sscanf(buf, "%255s", config->manufacturer) == 1)
 		return size;
 	return -1;
 }
@@ -551,7 +964,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *config = f->config;
-	return sprintf(buf, "%d\n", config->wceis);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", config->wceis);
 }
 
 static ssize_t rndis_wceis_store(struct device *dev,
@@ -576,7 +990,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *rndis = f->config;
-	return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+
+	return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
 		rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
 		rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
 }
@@ -603,7 +1018,8 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct rndis_function_config *config = f->config;
-	return sprintf(buf, "%04x\n", config->vendorID);
+
+	return snprintf(buf, PAGE_SIZE, "%04x\n", config->vendorID);
 }
 
 static ssize_t rndis_vendorID_store(struct device *dev,
@@ -671,6 +1087,7 @@
 				&common->luns[0].dev.kobj,
 				"lun");
 	if (err) {
+		fsg_common_release(&common->ref);
 		kfree(config);
 		return err;
 	}
@@ -698,7 +1115,7 @@
 {
 	struct android_usb_function *f = dev_get_drvdata(dev);
 	struct mass_storage_function_config *config = f->config;
-	return sprintf(buf, "%s\n", config->common->inquiry_string);
+	return snprintf(buf, PAGE_SIZE, "%s\n", config->common->inquiry_string);
 }
 
 static ssize_t mass_storage_inquiry_store(struct device *dev,
@@ -708,7 +1125,7 @@
 	struct mass_storage_function_config *config = f->config;
 	if (size >= sizeof(config->common->inquiry_string))
 		return -EINVAL;
-	if (sscanf(buf, "%s", config->common->inquiry_string) != 1)
+	if (sscanf(buf, "%28s", config->common->inquiry_string) != 1)
 		return -EINVAL;
 	return size;
 }
@@ -765,7 +1182,15 @@
 
 
 static struct android_usb_function *supported_functions[] = {
+	&mbim_function,
+	&rmnet_smd_function,
+	&rmnet_sdio_function,
+	&rmnet_smd_sdio_function,
+	&rmnet_function,
+	&diag_function,
+	&serial_function,
 	&adb_function,
+	&ccid_function,
 	&acm_function,
 	&mtp_function,
 	&ptp_function,
@@ -775,6 +1200,31 @@
 	NULL
 };
 
+static void android_cleanup_functions(struct android_usb_function **functions)
+{
+	struct android_usb_function *f;
+	struct device_attribute **attrs;
+	struct device_attribute *attr;
+
+	while (*functions) {
+		f = *functions++;
+
+		if (f->dev) {
+			device_destroy(android_class, f->dev->devt);
+			kfree(f->dev_name);
+		} else
+			continue;
+
+		if (f->cleanup)
+			f->cleanup(f);
+
+		attrs = f->attributes;
+		if (attrs) {
+			while ((attr = *attrs++))
+				device_remove_file(f->dev, attr);
+		}
+	}
+}
 
 static int android_init_functions(struct android_usb_function **functions,
 				  struct usb_composite_dev *cdev)
@@ -783,17 +1233,22 @@
 	struct android_usb_function *f;
 	struct device_attribute **attrs;
 	struct device_attribute *attr;
-	int err;
-	int index = 0;
+	int err = 0;
+	int index = 1; /* index 0 is for android0 device */
 
 	for (; (f = *functions++); index++) {
 		f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);
+		if (!f->dev_name) {
+			err = -ENOMEM;
+			goto err_out;
+		}
 		f->dev = device_create(android_class, dev->dev,
 				MKDEV(0, index), f, f->dev_name);
 		if (IS_ERR(f->dev)) {
 			pr_err("%s: Failed to create dev %s", __func__,
 							f->dev_name);
 			err = PTR_ERR(f->dev);
+			f->dev = NULL;
 			goto err_create;
 		}
 
@@ -802,7 +1257,7 @@
 			if (err) {
 				pr_err("%s: Failed to init %s", __func__,
 								f->name);
-				goto err_out;
+				goto err_init;
 			}
 		}
 
@@ -814,35 +1269,26 @@
 		if (err) {
 			pr_err("%s: Failed to create function %s attributes",
 					__func__, f->name);
-			goto err_out;
+			goto err_attrs;
 		}
 	}
 	return 0;
 
-err_out:
+err_attrs:
+	for (attr = *(attrs -= 2); attrs != f->attributes; attr = *(attrs--))
+		device_remove_file(f->dev, attr);
+	if (f->cleanup)
+		f->cleanup(f);
+err_init:
 	device_destroy(android_class, f->dev->devt);
 err_create:
+	f->dev = NULL;
 	kfree(f->dev_name);
+err_out:
+	android_cleanup_functions(dev->functions);
 	return err;
 }
 
-static void android_cleanup_functions(struct android_usb_function **functions)
-{
-	struct android_usb_function *f;
-
-	while (*functions) {
-		f = *functions++;
-
-		if (f->dev) {
-			device_destroy(android_class, f->dev->devt);
-			kfree(f->dev_name);
-		}
-
-		if (f->cleanup)
-			f->cleanup(f);
-	}
-}
-
 static int
 android_bind_enabled_functions(struct android_dev *dev,
 			       struct usb_configuration *c)
@@ -889,6 +1335,32 @@
 /*-------------------------------------------------------------------------*/
 /* /sys/class/android_usb/android%d/ interface */
 
+static ssize_t remote_wakeup_show(struct device *pdev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			!!(android_config_driver.bmAttributes &
+				USB_CONFIG_ATT_WAKEUP));
+}
+
+static ssize_t remote_wakeup_store(struct device *pdev,
+		struct device_attribute *attr, const char *buff, size_t size)
+{
+	int enable = 0;
+
+	sscanf(buff, "%d", &enable);
+
+	pr_debug("android_usb: %s remote wakeup\n",
+			enable ? "enabling" : "disabling");
+
+	if (enable)
+		android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	else
+		android_config_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+
+	return size;
+}
+
 static ssize_t
 functions_show(struct device *pdev, struct device_attribute *attr, char *buf)
 {
@@ -899,7 +1371,7 @@
 	mutex_lock(&dev->mutex);
 
 	list_for_each_entry(f, &dev->enabled_functions, enabled_list)
-		buff += sprintf(buff, "%s,", f->name);
+		buff += snprintf(buff, PAGE_SIZE, "%s,", f->name);
 
 	mutex_unlock(&dev->mutex);
 
@@ -926,7 +1398,7 @@
 
 	INIT_LIST_HEAD(&dev->enabled_functions);
 
-	strncpy(buf, buff, sizeof(buf));
+	strlcpy(buf, buff, sizeof(buf));
 	b = strim(buf);
 
 	while (b) {
@@ -947,7 +1419,8 @@
 			   char *buf)
 {
 	struct android_dev *dev = dev_get_drvdata(pdev);
-	return sprintf(buf, "%d\n", dev->enabled);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", dev->enabled);
 }
 
 static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
@@ -958,7 +1431,6 @@
 	struct android_usb_function *f;
 	int enabled = 0;
 
-
 	if (!cdev)
 		return -ENODEV;
 
@@ -996,6 +1468,26 @@
 	}
 
 	mutex_unlock(&dev->mutex);
+
+	return size;
+}
+
+static ssize_t pm_qos_show(struct device *pdev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct android_dev *dev = dev_get_drvdata(pdev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", dev->pm_qos);
+}
+
+static ssize_t pm_qos_store(struct device *pdev,
+			   struct device_attribute *attr,
+			   const char *buff, size_t size)
+{
+	struct android_dev *dev = dev_get_drvdata(pdev);
+
+	strlcpy(dev->pm_qos, buff, sizeof(dev->pm_qos));
+
 	return size;
 }
 
@@ -1017,7 +1509,7 @@
 		state = "CONNECTED";
 	spin_unlock_irqrestore(&cdev->lock, flags);
 out:
-	return sprintf(buf, "%s\n", state);
+	return snprintf(buf, PAGE_SIZE, "%s\n", state);
 }
 
 #define DESCRIPTOR_ATTR(field, format_string)				\
@@ -1025,7 +1517,8 @@
 field ## _show(struct device *dev, struct device_attribute *attr,	\
 		char *buf)						\
 {									\
-	return sprintf(buf, format_string, device_desc.field);		\
+	return snprintf(buf, PAGE_SIZE,					\
+			format_string, device_desc.field);		\
 }									\
 static ssize_t								\
 field ## _store(struct device *dev, struct device_attribute *attr,	\
@@ -1045,7 +1538,7 @@
 field ## _show(struct device *dev, struct device_attribute *attr,	\
 		char *buf)						\
 {									\
-	return sprintf(buf, "%s", buffer);				\
+	return snprintf(buf, PAGE_SIZE, "%s", buffer);			\
 }									\
 static ssize_t								\
 field ## _store(struct device *dev, struct device_attribute *attr,	\
@@ -1071,7 +1564,11 @@
 static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show,
 						 functions_store);
 static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+static DEVICE_ATTR(pm_qos, S_IRUGO | S_IWUSR,
+		pm_qos_show, pm_qos_store);
 static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+static DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR,
+		remote_wakeup_show, remote_wakeup_store);
 
 static struct device_attribute *android_usb_attributes[] = {
 	&dev_attr_idVendor,
@@ -1085,7 +1582,9 @@
 	&dev_attr_iSerial,
 	&dev_attr_functions,
 	&dev_attr_enable,
+	&dev_attr_pm_qos,
 	&dev_attr_state,
+	&dev_attr_remote_wakeup,
 	NULL
 };
 
@@ -1143,9 +1642,10 @@
 	device_desc.iProduct = id;
 
 	/* Default strings - should be updated by userspace */
-	strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1);
-	strncpy(product_string, "Android", sizeof(product_string) - 1);
-	strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
+	strlcpy(manufacturer_string, "Android",
+		sizeof(manufacturer_string) - 1);
+	strlcpy(product_string, "Android", sizeof(product_string) - 1);
+	strlcpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
 
 	id = usb_string_id(cdev);
 	if (id < 0)
@@ -1153,6 +1653,9 @@
 	strings_dev[STRING_SERIAL_IDX].id = id;
 	device_desc.iSerialNumber = id;
 
+	if (gadget_is_otg(cdev->gadget))
+		android_config_driver.descriptors = otg_desc;
+
 	gcnum = usb_gadget_controller_number(gadget);
 	if (gcnum >= 0)
 		device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
@@ -1162,7 +1665,6 @@
 		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
 	}
 
-	usb_gadget_set_selfpowered(gadget);
 	dev->cdev = cdev;
 
 	return 0;
@@ -1172,6 +1674,9 @@
 {
 	struct android_dev *dev = _android_dev;
 
+	manufacturer_string[0] = '\0';
+	product_string[0] = '\0';
+	serial_string[0] = '0';
 	cancel_work_sync(&dev->work);
 	android_cleanup_functions(dev->functions);
 	return 0;
@@ -1182,7 +1687,7 @@
 	.dev		= &device_desc,
 	.strings	= dev_strings,
 	.unbind		= android_usb_unbind,
-	.max_speed	= USB_SPEED_HIGH,
+	.max_speed	= USB_SPEED_SUPER
 };
 
 static int
@@ -1267,19 +1772,86 @@
 	return 0;
 }
 
-
-static int __init init(void)
+static void android_destroy_device(struct android_dev *dev)
 {
-	struct android_dev *dev;
-	int err;
+	struct device_attribute **attrs = android_usb_attributes;
+	struct device_attribute *attr;
+
+	while ((attr = *attrs++))
+		device_remove_file(dev->dev, attr);
+	device_destroy(android_class, dev->dev->devt);
+}
+
+static int __devinit android_probe(struct platform_device *pdev)
+{
+	struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+	struct android_dev *dev = _android_dev;
+	int ret = 0;
+
+	dev->pdata = pdata;
 
 	android_class = class_create(THIS_MODULE, "android_usb");
 	if (IS_ERR(android_class))
 		return PTR_ERR(android_class);
 
+	ret = android_create_device(dev);
+	if (ret) {
+		pr_err("%s(): android_create_device failed\n", __func__);
+		goto err_dev;
+	}
+
+	ret = usb_composite_probe(&android_usb_driver, android_bind);
+	if (ret) {
+		pr_err("%s(): Failed to register android "
+				 "composite driver\n", __func__);
+		goto err_probe;
+	}
+
+	/* pm qos request to prevent apps idle power collapse */
+	if (pdata && pdata->swfi_latency)
+		pm_qos_add_request(&dev->pm_qos_req_dma,
+			PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+	strlcpy(dev->pm_qos, "high", sizeof(dev->pm_qos));
+
+	return ret;
+err_probe:
+	android_destroy_device(dev);
+err_dev:
+	class_destroy(android_class);
+	return ret;
+}
+
+static int android_remove(struct platform_device *pdev)
+{
+	struct android_dev *dev = _android_dev;
+	struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+
+	android_destroy_device(dev);
+	class_destroy(android_class);
+	usb_composite_unregister(&android_usb_driver);
+	if (pdata && pdata->swfi_latency)
+		pm_qos_remove_request(&dev->pm_qos_req_dma);
+
+	return 0;
+}
+
+static struct platform_driver android_platform_driver = {
+	.driver = { .name = "android_usb"},
+	.probe = android_probe,
+	.remove = android_remove,
+};
+
+static int __init init(void)
+{
+	struct android_dev *dev;
+	int ret;
+
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev)
+	if (!dev) {
+		pr_err("%s(): Failed to alloc memory for android_dev\n",
+				__func__);
 		return -ENOMEM;
+	}
 
 	dev->disable_depth = 1;
 	dev->functions = supported_functions;
@@ -1287,27 +1859,26 @@
 	INIT_WORK(&dev->work, android_work);
 	mutex_init(&dev->mutex);
 
-	err = android_create_device(dev);
-	if (err) {
-		class_destroy(android_class);
-		kfree(dev);
-		return err;
-	}
-
 	_android_dev = dev;
 
 	/* Override composite driver functions */
 	composite_driver.setup = android_setup;
 	composite_driver.disconnect = android_disconnect;
 
-	return usb_composite_probe(&android_usb_driver, android_bind);
+	ret = platform_driver_register(&android_platform_driver);
+	if (ret) {
+		pr_err("%s(): Failed to register android"
+				 "platform driver\n", __func__);
+		kfree(dev);
+	}
+
+	return ret;
 }
 module_init(init);
 
 static void __exit cleanup(void)
 {
-	usb_composite_unregister(&android_usb_driver);
-	class_destroy(android_class);
+	platform_driver_unregister(&android_platform_driver);
 	kfree(_android_dev);
 	_android_dev = NULL;
 }
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index d07e44c..1cbaa8e 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -1,8 +1,14 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
  * only version 2 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>
@@ -10,59 +16,153 @@
 #include <linux/pm_runtime.h>
 #include <linux/usb/msm_hsusb_hw.h>
 #include <linux/usb/ulpi.h>
+#include <linux/gpio.h>
 
 #include "ci13xxx_udc.c"
 
 #define MSM_USB_BASE	(udc->regs)
 
+struct ci13xxx_udc_context {
+	int irq;
+	void __iomem *regs;
+	int wake_gpio;
+	int wake_irq;
+	bool wake_irq_state;
+};
+
+static struct ci13xxx_udc_context _udc_ctxt;
+
 static irqreturn_t msm_udc_irq(int irq, void *data)
 {
 	return udc_irq();
 }
 
+static void ci13xxx_msm_suspend(void)
+{
+	struct device *dev = _udc->gadget.dev.parent;
+	dev_dbg(dev, "ci13xxx_msm_suspend\n");
+
+	if (_udc_ctxt.wake_irq && !_udc_ctxt.wake_irq_state) {
+		enable_irq_wake(_udc_ctxt.wake_irq);
+		enable_irq(_udc_ctxt.wake_irq);
+		_udc_ctxt.wake_irq_state = true;
+	}
+}
+
+static void ci13xxx_msm_resume(void)
+{
+	struct device *dev = _udc->gadget.dev.parent;
+	dev_dbg(dev, "ci13xxx_msm_resume\n");
+
+	if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
+		disable_irq_wake(_udc_ctxt.wake_irq);
+		disable_irq(_udc_ctxt.wake_irq);
+		_udc_ctxt.wake_irq_state = false;
+	}
+}
+
 static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
 {
 	struct device *dev = udc->gadget.dev.parent;
-	int val;
 
 	switch (event) {
 	case CI13XXX_CONTROLLER_RESET_EVENT:
-		dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
+		dev_info(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
 		writel(0, USB_AHBBURST);
-		writel(0, USB_AHBMODE);
+		writel_relaxed(0x08, USB_AHBMODE);
 		break;
-	case CI13XXX_CONTROLLER_STOPPED_EVENT:
-		dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
-		/*
-		 * Put the transceiver in non-driving mode. Otherwise host
-		 * may not detect soft-disconnection.
-		 */
-		val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL);
-		val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
-		val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
-		usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
+	case CI13XXX_CONTROLLER_DISCONNECT_EVENT:
+		dev_info(dev, "CI13XXX_CONTROLLER_DISCONNECT_EVENT received\n");
+		ci13xxx_msm_resume();
 		break;
+	case CI13XXX_CONTROLLER_SUSPEND_EVENT:
+		dev_info(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
+		ci13xxx_msm_suspend();
+		break;
+	case CI13XXX_CONTROLLER_RESUME_EVENT:
+		dev_info(dev, "CI13XXX_CONTROLLER_RESUME_EVENT received\n");
+		ci13xxx_msm_resume();
+		break;
+
 	default:
 		dev_dbg(dev, "unknown ci13xxx_udc event\n");
 		break;
 	}
 }
 
+static irqreturn_t ci13xxx_msm_resume_irq(int irq, void *data)
+{
+	struct ci13xxx *udc = _udc;
+
+	if (udc->transceiver && udc->vbus_active && udc->suspended)
+		usb_phy_set_suspend(udc->transceiver, 0);
+	else if (!udc->suspended)
+		ci13xxx_msm_resume();
+
+	return IRQ_HANDLED;
+}
+
 static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
 	.name			= "ci13xxx_msm",
 	.flags			= CI13XXX_REGS_SHARED |
 				  CI13XXX_REQUIRE_TRANSCEIVER |
 				  CI13XXX_PULLUP_ON_VBUS |
-				  CI13XXX_DISABLE_STREAMING,
+				  CI13XXX_ZERO_ITC |
+				  CI13XXX_DISABLE_STREAMING |
+				  CI13XXX_IS_OTG,
 
 	.notify_event		= ci13xxx_msm_notify_event,
 };
 
+static int ci13xxx_msm_install_wake_gpio(struct platform_device *pdev,
+				struct resource *res)
+{
+	int wake_irq;
+	int ret;
+
+	dev_dbg(&pdev->dev, "ci13xxx_msm_install_wake_gpio\n");
+
+	_udc_ctxt.wake_gpio = res->start;
+	gpio_request(_udc_ctxt.wake_gpio, "USB_RESUME");
+	gpio_direction_input(_udc_ctxt.wake_gpio);
+	wake_irq = gpio_to_irq(_udc_ctxt.wake_gpio);
+	if (wake_irq < 0) {
+		dev_err(&pdev->dev, "could not register USB_RESUME GPIO.\n");
+		return -ENXIO;
+	}
+
+	dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
+			_udc_ctxt.wake_gpio, wake_irq);
+	ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
+		IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "usb resume", NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
+		goto gpio_free;
+	}
+	disable_irq(wake_irq);
+	_udc_ctxt.wake_irq = wake_irq;
+
+	return 0;
+
+gpio_free:
+	gpio_free(_udc_ctxt.wake_gpio);
+	_udc_ctxt.wake_gpio = 0;
+	return ret;
+}
+
+static void ci13xxx_msm_uninstall_wake_gpio(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "ci13xxx_msm_uninstall_wake_gpio\n");
+
+	if (_udc_ctxt.wake_gpio) {
+		gpio_free(_udc_ctxt.wake_gpio);
+		_udc_ctxt.wake_gpio = 0;
+	}
+}
+
 static int ci13xxx_msm_probe(struct platform_device *pdev)
 {
 	struct resource *res;
-	void __iomem *regs;
-	int irq;
 	int ret;
 
 	dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
@@ -73,29 +173,39 @@
 		return -ENXIO;
 	}
 
-	regs = ioremap(res->start, resource_size(res));
-	if (!regs) {
+	_udc_ctxt.regs = ioremap(res->start, resource_size(res));
+	if (!_udc_ctxt.regs) {
 		dev_err(&pdev->dev, "ioremap failed\n");
 		return -ENOMEM;
 	}
 
-	ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
+	ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, _udc_ctxt.regs);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "udc_probe failed\n");
 		goto iounmap;
 	}
 
-	irq = platform_get_irq(pdev, 0);
-	if (irq < 0) {
+	_udc_ctxt.irq = platform_get_irq(pdev, 0);
+	if (_udc_ctxt.irq < 0) {
 		dev_err(&pdev->dev, "IRQ not found\n");
 		ret = -ENXIO;
 		goto udc_remove;
 	}
 
-	ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO, "USB_RESUME");
+	if (res) {
+		ret = ci13xxx_msm_install_wake_gpio(pdev, res);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "gpio irq install failed\n");
+			goto udc_remove;
+		}
+	}
+
+	ret = request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name,
+					  pdev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "request_irq failed\n");
-		goto udc_remove;
+		goto gpio_uninstall;
 	}
 
 	pm_runtime_no_callbacks(&pdev->dev);
@@ -103,17 +213,30 @@
 
 	return 0;
 
+gpio_uninstall:
+	ci13xxx_msm_uninstall_wake_gpio(pdev);
 udc_remove:
 	udc_remove();
 iounmap:
-	iounmap(regs);
+	iounmap(_udc_ctxt.regs);
 
 	return ret;
 }
 
+int ci13xxx_msm_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	free_irq(_udc_ctxt.irq, pdev);
+	ci13xxx_msm_uninstall_wake_gpio(pdev);
+	udc_remove();
+	iounmap(_udc_ctxt.regs);
+	return 0;
+}
+
 static struct platform_driver ci13xxx_msm_driver = {
 	.probe = ci13xxx_msm_probe,
 	.driver = { .name = "msm_hsusb", },
+	.remove = ci13xxx_msm_remove,
 };
 MODULE_ALIAS("platform:msm_hsusb");
 
@@ -123,4 +246,10 @@
 }
 module_init(ci13xxx_msm_init);
 
+static void __exit ci13xxx_msm_exit(void)
+{
+	platform_driver_unregister(&ci13xxx_msm_driver);
+}
+module_exit(ci13xxx_msm_exit);
+
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/ci13xxx_msm_hsic.c b/drivers/usb/gadget/ci13xxx_msm_hsic.c
new file mode 100644
index 0000000..39d4720
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_msm_hsic.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/usb.h>
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_xo.h>
+
+#include "ci13xxx_udc.c"
+
+#define MSM_USB_BASE	(mhsic->regs)
+
+#define ULPI_IO_TIMEOUT_USEC			(10 * 1000)
+#define USB_PHY_VDD_DIG_VOL_SUSP_MIN	500000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN			1045000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX			1320000 /* uV */
+#define USB_PHY_VDD_DIG_LOAD			49360	/* uA */
+#define LINK_RESET_TIMEOUT_USEC			(250 * 1000)
+#define PHY_SUSPEND_TIMEOUT_USEC		(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC			(100 * 1000)
+#define HSIC_CFG_REG					0x30
+#define HSIC_IO_CAL_PER_REG				0x33
+#define HSIC_DBG1_REG					0x38
+
+struct msm_hsic_per *the_mhsic;
+
+struct msm_hsic_per {
+	struct device		*dev;
+	struct clk			*iface_clk;
+	struct clk			*core_clk;
+	struct clk			*alt_core_clk;
+	struct clk			*phy_clk;
+	struct clk			*cal_clk;
+	struct regulator	*hsic_vddcx;
+	bool				async_int;
+	void __iomem		*regs;
+	int					irq;
+	atomic_t			in_lpm;
+	struct wake_lock	wlock;
+	struct msm_xo_voter	*xo_handle;
+	struct workqueue_struct *wq;
+	struct work_struct	suspend_w;
+	struct msm_hsic_peripheral_platform_data *pdata;
+};
+
+static int msm_hsic_init_vddcx(struct msm_hsic_per *mhsic, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mhsic->hsic_vddcx = regulator_get(mhsic->dev, "HSIC_VDDCX");
+	if (IS_ERR(mhsic->hsic_vddcx)) {
+		dev_err(mhsic->dev, "unable to get hsic vddcx\n");
+		return PTR_ERR(mhsic->hsic_vddcx);
+	}
+
+	ret = regulator_set_voltage(mhsic->hsic_vddcx,
+			USB_PHY_VDD_DIG_VOL_MIN,
+			USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mhsic->dev, "unable to set the voltage"
+				"for hsic vddcx\n");
+		goto reg_set_voltage_err;
+	}
+
+	ret = regulator_set_optimum_mode(mhsic->hsic_vddcx,
+				USB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		pr_err("%s: Unable to set optimum mode of the regulator:"
+					"VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mhsic->hsic_vddcx);
+	if (ret) {
+		dev_err(mhsic->dev, "unable to enable hsic vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mhsic->hsic_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mhsic->hsic_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mhsic->hsic_vddcx, 0,
+				USB_PHY_VDD_DIG_VOL_MIN);
+reg_set_voltage_err:
+	regulator_put(mhsic->hsic_vddcx);
+
+	return ret;
+
+}
+
+static int ulpi_write(struct msm_hsic_per *mhsic, u32 val, u32 reg)
+{
+	int cnt = 0;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(mhsic->dev, "ulpi_write: timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int msm_hsic_phy_clk_reset(struct msm_hsic_per *mhsic)
+{
+	int ret;
+
+	ret = clk_reset(mhsic->core_clk, CLK_RESET_ASSERT);
+	if (ret) {
+		clk_disable(mhsic->alt_core_clk);
+		dev_err(mhsic->dev, "usb phy clk assert failed\n");
+		return ret;
+	}
+	usleep_range(10000, 12000);
+	clk_disable(mhsic->alt_core_clk);
+
+	ret = clk_reset(mhsic->core_clk, CLK_RESET_DEASSERT);
+	if (ret)
+		dev_err(mhsic->dev, "usb phy clk deassert failed\n");
+
+	return ret;
+}
+
+static int msm_hsic_phy_reset(struct msm_hsic_per *mhsic)
+{
+	u32 val;
+	int ret;
+
+	ret = msm_hsic_phy_clk_reset(mhsic);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	/*
+	 * Ensure that RESET operation is completed before
+	 * turning off clock.
+	 */
+	mb();
+	dev_dbg(mhsic->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+static int msm_hsic_enable_clocks(struct platform_device *pdev,
+				struct msm_hsic_per *mhsic, bool enable)
+{
+	int ret = 0;
+
+	if (!enable)
+		goto put_clocks;
+
+	mhsic->iface_clk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(mhsic->iface_clk)) {
+		dev_err(mhsic->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mhsic->iface_clk);
+		goto put_iface_clk;
+	}
+
+	mhsic->core_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(mhsic->core_clk)) {
+		dev_err(mhsic->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mhsic->core_clk);
+		goto put_core_clk;
+	}
+
+	mhsic->phy_clk = clk_get(&pdev->dev, "phy_clk");
+	if (IS_ERR(mhsic->phy_clk)) {
+		dev_err(mhsic->dev, "failed to get phy_clk\n");
+		ret = PTR_ERR(mhsic->phy_clk);
+		goto put_phy_clk;
+	}
+
+	mhsic->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk");
+	if (IS_ERR(mhsic->alt_core_clk)) {
+		dev_err(mhsic->dev, "failed to get alt_core_clk\n");
+		ret = PTR_ERR(mhsic->alt_core_clk);
+		goto put_alt_core_clk;
+	}
+
+	mhsic->cal_clk = clk_get(&pdev->dev, "cal_clk");
+	if (IS_ERR(mhsic->cal_clk)) {
+		dev_err(mhsic->dev, "failed to get cal_clk\n");
+		ret = PTR_ERR(mhsic->cal_clk);
+		goto put_cal_clk;
+	}
+
+	clk_enable(mhsic->iface_clk);
+	clk_enable(mhsic->core_clk);
+	clk_enable(mhsic->phy_clk);
+	clk_enable(mhsic->alt_core_clk);
+	clk_enable(mhsic->cal_clk);
+
+	return 0;
+
+put_clocks:
+	clk_disable(mhsic->iface_clk);
+	clk_disable(mhsic->core_clk);
+	clk_disable(mhsic->phy_clk);
+	clk_disable(mhsic->alt_core_clk);
+	clk_disable(mhsic->cal_clk);
+put_cal_clk:
+	clk_put(mhsic->cal_clk);
+put_alt_core_clk:
+	clk_put(mhsic->alt_core_clk);
+put_phy_clk:
+	clk_put(mhsic->phy_clk);
+put_core_clk:
+	clk_put(mhsic->core_clk);
+put_iface_clk:
+	clk_put(mhsic->iface_clk);
+
+	return ret;
+}
+
+static int msm_hsic_reset(struct msm_hsic_per *mhsic)
+{
+	int cnt = 0;
+	int ret;
+
+	ret = msm_hsic_phy_reset(mhsic);
+	if (ret) {
+		dev_err(mhsic->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+	while (cnt < LINK_RESET_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= LINK_RESET_TIMEOUT_USEC)
+		return -ETIMEDOUT;
+
+	/* Reset PORTSC and select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+	return 0;
+}
+
+static void msm_hsic_wakeup(void)
+{
+	if (atomic_read(&the_mhsic->in_lpm))
+		pm_runtime_resume(the_mhsic->dev);
+}
+
+static void msm_hsic_start(void)
+{
+	int ret;
+
+	/* programmable length of connect signaling (33.2ns) */
+	ret = ulpi_write(the_mhsic, 3, HSIC_DBG1_REG);
+	if (ret) {
+		pr_err("%s: Unable to program length of connect signaling\n",
+			    __func__);
+	}
+
+	/*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */
+	ret = ulpi_write(the_mhsic, 0xFF, HSIC_IO_CAL_PER_REG);
+
+	if (ret) {
+		pr_err("%s: Unable to set periodic calibration interval\n",
+			    __func__);
+	}
+
+	/* Enable periodic IO calibration in HSIC_CFG register */
+	ret = ulpi_write(the_mhsic, 0xE9, HSIC_CFG_REG);
+	if (ret) {
+		pr_err("%s: Unable to enable periodic IO calibration\n",
+			    __func__);
+	}
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_suspend(struct msm_hsic_per *mhsic)
+{
+	int cnt = 0, ret;
+	u32 val;
+
+	if (atomic_read(&mhsic->in_lpm)) {
+		dev_dbg(mhsic->dev, "%s called while in lpm\n", __func__);
+		return 0;
+	}
+	disable_irq(mhsic->irq);
+
+	/*
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	val = readl_relaxed(USB_PORTSC) | PORTSC_PHCD;
+	writel_relaxed(val, USB_PORTSC);
+
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
+		dev_err(mhsic->dev, "Unable to suspend PHY\n");
+		msm_hsic_reset(mhsic);
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+		clk_disable(mhsic->iface_clk);
+		clk_disable(mhsic->core_clk);
+	}
+	clk_disable(mhsic->phy_clk);
+	clk_disable(mhsic->cal_clk);
+
+	ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(mhsic->dev, "%s failed to devote for TCXO %d\n"
+				, __func__, ret);
+
+	ret = regulator_set_voltage(mhsic->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_SUSP_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mhsic->dev, "unable to set vddcx voltage: min:0.5v max:1.32v\n");
+
+	if (device_may_wakeup(mhsic->dev))
+		enable_irq_wake(mhsic->irq);
+
+	atomic_set(&mhsic->in_lpm, 1);
+	enable_irq(mhsic->irq);
+	wake_unlock(&mhsic->wlock);
+
+	dev_info(mhsic->dev, "HSIC-USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_hsic_resume(struct msm_hsic_per *mhsic)
+{
+	int cnt = 0, ret;
+	unsigned temp;
+
+	if (!atomic_read(&mhsic->in_lpm)) {
+		dev_dbg(mhsic->dev, "%s called while not in lpm\n", __func__);
+		return 0;
+	}
+
+	wake_lock(&mhsic->wlock);
+	ret = regulator_set_voltage(mhsic->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mhsic->dev,
+				"unable to set vddcx voltage: min:1.045v max:1.32v\n");
+
+	ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(mhsic->dev, "%s failed to vote for TCXO %d\n",
+				__func__, ret);
+
+	if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+		clk_enable(mhsic->iface_clk);
+		clk_enable(mhsic->core_clk);
+	}
+	clk_enable(mhsic->phy_clk);
+	clk_enable(mhsic->cal_clk);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD;
+	writel_relaxed(temp, USB_PORTSC);
+	while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) &&
+			(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+		/*
+		 * This is a fatal error. Reset the link and
+		 * PHY to make hsic working.
+		 */
+		dev_err(mhsic->dev, "Unable to resume USB. Reset the hsic\n");
+		msm_hsic_reset(mhsic);
+	}
+skip_phy_resume:
+	if (device_may_wakeup(mhsic->dev))
+		disable_irq_wake(mhsic->irq);
+
+	atomic_set(&mhsic->in_lpm, 0);
+
+	if (mhsic->async_int) {
+		mhsic->async_int = false;
+		enable_irq(mhsic->irq);
+	}
+
+	dev_info(mhsic->dev, "HSIC-USB exited from low power mode\n");
+
+	return 0;
+}
+
+static int msm_hsic_pm_suspend(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral PM suspend\n");
+
+	return msm_hsic_suspend(mhsic);
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hsic_pm_resume(struct device *dev)
+{
+	dev_dbg(dev, "MSM HSIC Peripheral PM resume\n");
+
+	/*
+	 * Do not resume hardware as part of system resume,
+	 * rather, wait for the ASYNC INT from the h/w
+	 */
+	return 0;
+}
+#else
+static int msm_hsic_pm_resume(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral PM resume\n");
+
+	return msm_hsic_resume(mhsic);
+}
+#endif
+
+static void msm_hsic_pm_suspend_work(struct work_struct *w)
+{
+	pm_runtime_put_noidle(the_mhsic->dev);
+	pm_runtime_suspend(the_mhsic->dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hsic_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "MSM HSIC Peripheral runtime idle\n");
+
+	return 0;
+}
+
+static int msm_hsic_runtime_suspend(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral runtime suspend\n");
+
+	return msm_hsic_suspend(mhsic);
+}
+
+static int msm_hsic_runtime_resume(struct device *dev)
+{
+	struct msm_hsic_per *mhsic = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "MSM HSIC Peripheral runtime resume\n");
+	pm_runtime_get_noresume(mhsic->dev);
+
+	return msm_hsic_resume(mhsic);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
+	SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
+				msm_hsic_runtime_idle)
+};
+#endif
+
+/**
+ * Dummy match function - will be called only for HSIC msm
+ * device (msm_device_gadget_hsic_peripheral).
+ */
+static inline int __match(struct device *dev, void *data) { return 1; }
+
+static void msm_hsic_connect_peripheral(struct device *msm_udc_dev)
+{
+	struct device *dev;
+	struct usb_gadget *gadget;
+
+	dev = device_find_child(msm_udc_dev, NULL, __match);
+	gadget = dev_to_usb_gadget(dev);
+	usb_gadget_vbus_connect(gadget);
+}
+
+static irqreturn_t msm_udc_hsic_irq(int irq, void *data)
+{
+	struct msm_hsic_per *mhsic = data;
+
+	if (atomic_read(&mhsic->in_lpm)) {
+		disable_irq_nosync(mhsic->irq);
+		mhsic->async_int = true;
+		pm_request_resume(mhsic->dev);
+		return IRQ_HANDLED;
+	}
+
+	return udc_irq();
+}
+
+static void ci13xxx_msm_hsic_notify_event(struct ci13xxx *udc, unsigned event)
+{
+	struct device *dev = udc->gadget.dev.parent;
+	struct msm_hsic_per *mhsic = the_mhsic;
+
+	switch (event) {
+	case CI13XXX_CONTROLLER_RESET_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
+		writel_relaxed(0, USB_AHBBURST);
+		writel_relaxed(0x08, USB_AHBMODE);
+		break;
+	case CI13XXX_CONTROLLER_CONNECT_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_CONNECT_EVENT received\n");
+		msm_hsic_start();
+		break;
+	case CI13XXX_CONTROLLER_SUSPEND_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
+		queue_work(mhsic->wq, &mhsic->suspend_w);
+		break;
+	case CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT:
+		dev_dbg(dev, "CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT received\n");
+		msm_hsic_wakeup();
+		break;
+	default:
+		dev_dbg(dev, "unknown ci13xxx_udc event\n");
+		break;
+	}
+}
+
+static struct ci13xxx_udc_driver ci13xxx_msm_udc_hsic_driver = {
+	.name			= "ci13xxx_msm_hsic",
+	.flags			= CI13XXX_REGS_SHARED |
+				  CI13XXX_PULLUP_ON_VBUS |
+				  CI13XXX_DISABLE_STREAMING |
+				  CI13XXX_ZERO_ITC,
+
+	.notify_event		= ci13xxx_msm_hsic_notify_event,
+};
+
+static int __devinit msm_hsic_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct msm_hsic_per *mhsic;
+	int ret = 0;
+	struct msm_hsic_peripheral_platform_data *pdata;
+
+	dev_dbg(&pdev->dev, "msm-hsic probe\n");
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
+		return -ENODEV;
+	} else {
+		pdata = pdev->dev.platform_data;
+	}
+
+	mhsic = kzalloc(sizeof(struct msm_hsic_per), GFP_KERNEL);
+	if (!mhsic) {
+		dev_err(&pdev->dev, "unable to allocate msm_hsic\n");
+		return -ENOMEM;
+	}
+	the_mhsic = mhsic;
+	platform_set_drvdata(pdev, mhsic);
+	mhsic->dev = &pdev->dev;
+	mhsic->pdata = pdata;
+
+	mhsic->irq = platform_get_irq(pdev, 0);
+	if (mhsic->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = mhsic->irq;
+		goto error;
+	}
+
+	mhsic->wq = alloc_workqueue("mhsic_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!mhsic->wq) {
+		pr_err("%s: Unable to create workqueue mhsic wq\n",
+				__func__);
+		ret = -ENOMEM;
+		goto error;
+	}
+	INIT_WORK(&mhsic->suspend_w, msm_hsic_pm_suspend_work);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto error;
+	}
+	mhsic->regs = ioremap(res->start, resource_size(res));
+	if (!mhsic->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto unmap;
+	}
+	dev_info(&pdev->dev, "HSIC Peripheral regs = %p\n", mhsic->regs);
+
+	mhsic->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "hsic_peripheral");
+	if (IS_ERR(mhsic->xo_handle)) {
+		dev_err(&pdev->dev, "%s not able to get the handle "
+			"to vote for TCXO\n", __func__);
+		ret = PTR_ERR(mhsic->xo_handle);
+		goto unmap;
+	}
+
+	ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO %d\n",
+				__func__, ret);
+		goto free_xo_handle;
+	}
+
+	ret = msm_hsic_enable_clocks(pdev, mhsic, true);
+
+	if (ret) {
+		dev_err(&pdev->dev, "msm_hsic_enable_clocks failed\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+	ret = msm_hsic_init_vddcx(mhsic, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_vddcx;
+	}
+
+	ret = msm_hsic_reset(mhsic);
+	if (ret) {
+		dev_err(&pdev->dev, "msm_hsic_reset failed\n");
+		ret = -ENODEV;
+		goto deinit_vddcx;
+	}
+
+	ret = udc_probe(&ci13xxx_msm_udc_hsic_driver, &pdev->dev, mhsic->regs);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "udc_probe failed\n");
+		ret = -ENODEV;
+		goto deinit_vddcx;
+	}
+
+	msm_hsic_connect_peripheral(&pdev->dev);
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mhsic->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mhsic->wlock);
+
+	ret = request_irq(mhsic->irq, msm_udc_hsic_irq,
+					  IRQF_SHARED, pdev->name, mhsic);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "request_irq failed\n");
+		ret = -ENODEV;
+		goto udc_remove;
+	}
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	return 0;
+udc_remove:
+	udc_remove();
+deinit_vddcx:
+	msm_hsic_init_vddcx(mhsic, 0);
+deinit_clocks:
+	msm_hsic_enable_clocks(pdev, mhsic, 0);
+	msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_OFF);
+free_xo_handle:
+	msm_xo_put(mhsic->xo_handle);
+unmap:
+	iounmap(mhsic->regs);
+error:
+	destroy_workqueue(mhsic->wq);
+	kfree(mhsic);
+	return ret;
+}
+
+static int __devexit hsic_msm_remove(struct platform_device *pdev)
+{
+	struct msm_hsic_per *mhsic = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	msm_hsic_init_vddcx(mhsic, 0);
+	msm_hsic_enable_clocks(pdev, mhsic, 0);
+	msm_xo_put(mhsic->xo_handle);
+	wake_lock_destroy(&mhsic->wlock);
+	destroy_workqueue(mhsic->wq);
+	udc_remove();
+	iounmap(mhsic->regs);
+	kfree(mhsic);
+
+	return 0;
+}
+
+static struct platform_driver msm_hsic_peripheral_driver = {
+	.probe	= msm_hsic_probe,
+	.remove	= __devexit_p(hsic_msm_remove),
+	.driver = {
+		.name = "msm_hsic_peripheral",
+#ifdef CONFIG_PM
+		.pm = &msm_hsic_dev_pm_ops,
+#endif
+	},
+};
+
+static int __init msm_hsic_peripheral_init(void)
+{
+	return platform_driver_probe(&msm_hsic_peripheral_driver,
+								msm_hsic_probe);
+}
+
+static void __exit msm_hsic_peripheral_exit(void)
+{
+	platform_driver_unregister(&msm_hsic_peripheral_driver);
+}
+
+module_init(msm_hsic_peripheral_init);
+module_exit(msm_hsic_peripheral_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 243ef1a..466fa15 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -60,10 +60,12 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/msm_hsusb.h>
 
 #include "ci13xxx_udc.h"
 
@@ -158,6 +160,7 @@
 #define CAP_ENDPTLISTADDR   (0x018UL)
 #define CAP_PORTSC          (0x044UL)
 #define CAP_DEVLC           (0x084UL)
+#define CAP_ENDPTPIPEID     (0x0BCUL)
 #define CAP_USBMODE         (hw_bank.lpm ? 0x0C8UL : 0x068UL)
 #define CAP_ENDPTSETUPSTAT  (hw_bank.lpm ? 0x0D8UL : 0x06CUL)
 #define CAP_ENDPTPRIME      (hw_bank.lpm ? 0x0DCUL : 0x070UL)
@@ -331,6 +334,17 @@
 	hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
 	hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);  /* HW >= 2.3 */
 
+	/*
+	 * ITC (Interrupt Threshold Control) field is to set the maximum
+	 * rate at which the device controller will issue interrupts.
+	 * The maximum interrupt interval measured in micro frames.
+	 * Valid values are 0, 1, 2, 4, 8, 16, 32, 64. The default value is
+	 * 8 micro frames. If CPU can handle interrupts at faster rate, ITC
+	 * can be set to lesser value to gain performance.
+	 */
+	if (udc->udc_driver->flags & CI13XXX_ZERO_ITC)
+		hw_cwrite(CAP_USBCMD, USBCMD_ITC_MASK, USBCMD_ITC(0));
+
 	if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
 		pr_err("cannot enter in device mode");
 		pr_err("lpm = %i", hw_bank.lpm);
@@ -430,6 +444,10 @@
 		data |= ENDPTCTRL_RXE;
 	}
 	hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), mask, data);
+
+	/* make sure endpoint is enabled before returning */
+	mb();
+
 	return 0;
 }
 
@@ -497,13 +515,18 @@
  */
 static int hw_ep_set_halt(int num, int dir, int value)
 {
+	u32 addr, mask_xs, mask_xr;
+
 	if (value != 0 && value != 1)
 		return -EINVAL;
 
 	do {
-		u32 addr = CAP_ENDPTCTRL + num * sizeof(u32);
-		u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
-		u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
+		if (hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
+			return 0;
+
+		addr = CAP_ENDPTCTRL + num * sizeof(u32);
+		mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+		mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
 
 		/* data toggle - reserved for EP0 but it's in ESS */
 		hw_cwrite(addr, mask_xs|mask_xr, value ? mask_xs : mask_xr);
@@ -855,6 +878,32 @@
 	*idx = (*idx + 1) & (DBG_DATA_MAX-1);
 }
 
+
+static unsigned int ep_addr_txdbg_mask;
+module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR);
+static unsigned int ep_addr_rxdbg_mask;
+module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR);
+
+static int allow_dbg_print(u8 addr)
+{
+	int dir, num;
+
+	/* allow bus wide events */
+	if (addr == 0xff)
+		return 1;
+
+	dir = addr & USB_ENDPOINT_DIR_MASK ? TX : RX;
+	num = addr & ~USB_ENDPOINT_DIR_MASK;
+	num = 1 << num;
+
+	if ((dir == TX) && (num & ep_addr_txdbg_mask))
+		return 1;
+	if ((dir == RX) && (num & ep_addr_rxdbg_mask))
+		return 1;
+
+	return 0;
+}
+
 /**
  * dbg_print:  prints the common part of the event
  * @addr:   endpoint address
@@ -868,6 +917,9 @@
 	unsigned int stamp;
 	unsigned long flags;
 
+	if (!allow_dbg_print(addr))
+		return;
+
 	write_lock_irqsave(&dbg_data.lck, flags);
 
 	do_gettimeofday(&tval);
@@ -1246,6 +1298,9 @@
 		dev_err(dev, "[%s] EINVAL\n", __func__);
 		return 0;
 	}
+	dump = kmalloc(2048, GFP_KERNEL);
+	if (dump == NULL)
+		return -ENOMEM;
 
 	dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
 	if (!dump) {
@@ -1343,6 +1398,142 @@
 }
 static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
 
+/* EP# and Direction */
+static ssize_t prime_ept(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+	struct ci13xxx_ep *mEp;
+	unsigned int ep_num, dir;
+	int n;
+	struct ci13xxx_req *mReq = NULL;
+
+	if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
+		dev_err(dev, "<ep_num> <dir>: prime the ep");
+		goto done;
+	}
+
+	if (dir)
+		mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+	else
+		mEp = &udc->ci13xxx_ep[ep_num];
+
+	n = hw_ep_bit(mEp->num, mEp->dir);
+	mReq =  list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+	mEp->qh.ptr->td.next   = mReq->dma;
+	mEp->qh.ptr->td.token &= ~TD_STATUS;
+
+	wmb();
+
+	hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
+	while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
+		cpu_relax();
+
+	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s\n", __func__,
+			hw_cread(CAP_ENDPTPRIME, ~0),
+			hw_cread(CAP_ENDPTSTAT, ~0),
+			mEp->num, mEp->dir ? "IN" : "OUT");
+done:
+	return count;
+
+}
+static DEVICE_ATTR(prime, S_IWUSR, NULL, prime_ept);
+
+/* EP# and Direction */
+static ssize_t print_dtds(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+	struct ci13xxx_ep *mEp;
+	unsigned int ep_num, dir;
+	int n;
+	struct list_head   *ptr = NULL;
+	struct ci13xxx_req *req = NULL;
+
+	if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
+		dev_err(dev, "<ep_num> <dir>: to print dtds");
+		goto done;
+	}
+
+	if (dir)
+		mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+	else
+		mEp = &udc->ci13xxx_ep[ep_num];
+
+	n = hw_ep_bit(mEp->num, mEp->dir);
+	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
+			"dTD_update_fail_count: %lu "
+			"mEp->dTD_update_fail_count: %lu\n", __func__,
+			hw_cread(CAP_ENDPTPRIME, ~0),
+			hw_cread(CAP_ENDPTSTAT, ~0),
+			mEp->num, mEp->dir ? "IN" : "OUT",
+			udc->dTD_update_fail_count,
+			mEp->dTD_update_fail_count);
+
+	pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
+			mEp->qh.ptr->cap, mEp->qh.ptr->curr,
+			mEp->qh.ptr->td.next, mEp->qh.ptr->td.token);
+
+	list_for_each(ptr, &mEp->qh.queue) {
+		req = list_entry(ptr, struct ci13xxx_req, queue);
+
+		pr_info("\treq:%08x next:%08x token:%08x page0:%08x status:%d\n",
+				req->dma, req->ptr->next, req->ptr->token,
+				req->ptr->page[0], req->req.status);
+	}
+done:
+	return count;
+
+}
+static DEVICE_ATTR(dtds, S_IWUSR, NULL, print_dtds);
+
+static int ci13xxx_wakeup(struct usb_gadget *_gadget)
+{
+	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	trace();
+
+	spin_lock_irqsave(udc->lock, flags);
+	if (!udc->remote_wakeup) {
+		ret = -EOPNOTSUPP;
+		dbg_trace("remote wakeup feature is not enabled\n");
+		goto out;
+	}
+	spin_unlock_irqrestore(udc->lock, flags);
+
+	udc->udc_driver->notify_event(udc,
+		CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT);
+
+	if (udc->transceiver)
+		usb_phy_set_suspend(udc->transceiver, 0);
+
+	spin_lock_irqsave(udc->lock, flags);
+	if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
+		ret = -EINVAL;
+		dbg_trace("port is not suspended\n");
+		goto out;
+	}
+	hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+out:
+	spin_unlock_irqrestore(udc->lock, flags);
+	return ret;
+}
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+
+	ci13xxx_wakeup(&udc->gadget);
+
+	return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+
 /**
  * dbg_create_files: initializes the attribute interface
  * @dev: device
@@ -1379,8 +1570,24 @@
 	retval = device_create_file(dev, &dev_attr_requests);
 	if (retval)
 		goto rm_registers;
+	retval = device_create_file(dev, &dev_attr_wakeup);
+	if (retval)
+		goto rm_remote_wakeup;
+	retval = device_create_file(dev, &dev_attr_prime);
+	if (retval)
+		goto rm_prime;
+	retval = device_create_file(dev, &dev_attr_dtds);
+	if (retval)
+		goto rm_dtds;
+
 	return 0;
 
+rm_dtds:
+	device_remove_file(dev, &dev_attr_dtds);
+rm_prime:
+	device_remove_file(dev, &dev_attr_prime);
+rm_remote_wakeup:
+	device_remove_file(dev, &dev_attr_wakeup);
  rm_registers:
 	device_remove_file(dev, &dev_attr_registers);
  rm_qheads:
@@ -1417,6 +1624,7 @@
 	device_remove_file(dev, &dev_attr_events);
 	device_remove_file(dev, &dev_attr_driver);
 	device_remove_file(dev, &dev_attr_device);
+	device_remove_file(dev, &dev_attr_wakeup);
 	return 0;
 }
 
@@ -1497,6 +1705,23 @@
 		if (!mReq->req.no_interrupt)
 			mReq->ptr->token  |= TD_IOC;
 	}
+
+	/* MSM Specific: updating the request as required for
+	 * SPS mode. Enable MSM proprietary DMA engine acording
+	 * to the UDC private data in the request.
+	 */
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+		if (mReq->req.udc_priv & MSM_SPS_MODE) {
+			mReq->ptr->token = TD_STATUS_ACTIVE;
+			if (mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER)
+				mReq->ptr->next = TD_TERMINATE;
+			else
+				mReq->ptr->next = MSM_ETD_TYPE | mReq->dma;
+			if (!mReq->req.no_interrupt)
+				mReq->ptr->token |= MSM_ETD_IOC;
+		}
+	}
+
 	mReq->ptr->page[0]  = mReq->req.dma;
 	for (i = 1; i < 5; i++)
 		mReq->ptr->page[i] =
@@ -1526,10 +1751,56 @@
 	}
 
 	/*  QH configuration */
+	if (!list_empty(&mEp->qh.queue)) {
+		struct ci13xxx_req *mReq = \
+			list_entry(mEp->qh.queue.next,
+				   struct ci13xxx_req, queue);
+
+		if (TD_STATUS_ACTIVE & mReq->ptr->token) {
+			mEp->qh.ptr->td.next   = mReq->dma;
+			mEp->qh.ptr->td.token &= ~TD_STATUS;
+			goto prime;
+		}
+	}
+
 	mEp->qh.ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */
+
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+		if (mReq->req.udc_priv & MSM_SPS_MODE) {
+			mEp->qh.ptr->td.next   |= MSM_ETD_TYPE;
+			i = hw_cread(CAP_ENDPTPIPEID +
+						 mEp->num * sizeof(u32), ~0);
+			/* Read current value of this EPs pipe id */
+			i = (mEp->dir == TX) ?
+				((i >> MSM_TX_PIPE_ID_OFS) & MSM_PIPE_ID_MASK) :
+					(i & MSM_PIPE_ID_MASK);
+			/* If requested pipe id is different from current,
+			   then write it */
+			if (i != (mReq->req.udc_priv & MSM_PIPE_ID_MASK)) {
+				if (mEp->dir == TX)
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+							mEp->num * sizeof(u32),
+						MSM_PIPE_ID_MASK <<
+							MSM_TX_PIPE_ID_OFS,
+						(mReq->req.udc_priv &
+						 MSM_PIPE_ID_MASK)
+							<< MSM_TX_PIPE_ID_OFS);
+				else
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+							mEp->num * sizeof(u32),
+						MSM_PIPE_ID_MASK,
+						mReq->req.udc_priv &
+							MSM_PIPE_ID_MASK);
+			}
+		}
+	}
+
 	mEp->qh.ptr->td.token &= ~TD_STATUS;   /* clear status */
 	mEp->qh.ptr->cap |=  QH_ZLT;
 
+prime:
 	wmb();   /* synchronize before ep prime */
 
 	ret = hw_ep_prime(mEp->num, mEp->dir,
@@ -1552,9 +1823,16 @@
 	if (mReq->req.status != -EALREADY)
 		return -EINVAL;
 
+	/* clean speculative fetches on req->ptr->token */
+	mb();
+
 	if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
 		return -EBUSY;
 
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
+		if ((mReq->req.udc_priv & MSM_SPS_MODE) &&
+			(mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER))
+			return -EBUSY;
 	if (mReq->zptr) {
 		if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
 			return -EBUSY;
@@ -1598,6 +1876,9 @@
 __releases(mEp->lock)
 __acquires(mEp->lock)
 {
+	struct ci13xxx_ep *mEpTemp = mEp;
+	unsigned val;
+
 	trace("%p", mEp);
 
 	if (mEp == NULL)
@@ -1612,11 +1893,37 @@
 			list_entry(mEp->qh.queue.next,
 				   struct ci13xxx_req, queue);
 		list_del_init(&mReq->queue);
+
+		/* MSM Specific: Clear end point proprietary register */
+		if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+			if (mReq->req.udc_priv & MSM_SPS_MODE) {
+				val = hw_cread(CAP_ENDPTPIPEID +
+					mEp->num * sizeof(u32),
+					~0);
+
+				if (val != MSM_EP_PIPE_ID_RESET_VAL)
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+						 mEp->num * sizeof(u32),
+						~0, MSM_EP_PIPE_ID_RESET_VAL);
+			}
+		}
 		mReq->req.status = -ESHUTDOWN;
 
+		if (mReq->map) {
+			dma_unmap_single(mEp->device, mReq->req.dma,
+				mReq->req.length,
+				mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+			mReq->req.dma = 0;
+			mReq->map     = 0;
+		}
+
 		if (mReq->req.complete != NULL) {
 			spin_unlock(mEp->lock);
-			mReq->req.complete(&mEp->ep, &mReq->req);
+			if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+				mReq->req.length)
+				mEpTemp = &_udc->ep0in;
+			mReq->req.complete(&mEpTemp->ep, &mReq->req);
 			spin_lock(mEp->lock);
 		}
 	}
@@ -1644,8 +1951,14 @@
 	udc->gadget.speed = USB_SPEED_UNKNOWN;
 	udc->remote_wakeup = 0;
 	udc->suspended = 0;
+	udc->configured = 0;
 	spin_unlock_irqrestore(udc->lock, flags);
 
+	gadget->b_hnp_enable = 0;
+	gadget->a_hnp_support = 0;
+	gadget->host_request = 0;
+	gadget->otg_srp_reqd = 0;
+
 	/* flush all endpoints */
 	gadget_for_each_ep(ep, gadget) {
 		usb_ep_fifo_flush(ep);
@@ -1693,6 +2006,11 @@
 	dbg_event(0xFF, "BUS RST", 0);
 
 	spin_unlock(udc->lock);
+
+	/*stop charging upon reset */
+	if (udc->transceiver)
+		usb_phy_set_power(udc->transceiver, 0);
+
 	retval = _gadget_stop_activity(&udc->gadget);
 	if (retval)
 		goto done;
@@ -1713,6 +2031,51 @@
 }
 
 /**
+ * isr_resume_handler: USB PCI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_resume_handler(struct ci13xxx *udc)
+{
+	udc->gadget.speed = hw_port_is_high_speed() ?
+		USB_SPEED_HIGH : USB_SPEED_FULL;
+	if (udc->suspended) {
+		spin_unlock(udc->lock);
+		if (udc->udc_driver->notify_event)
+			udc->udc_driver->notify_event(udc,
+			  CI13XXX_CONTROLLER_RESUME_EVENT);
+		if (udc->transceiver)
+			usb_phy_set_suspend(udc->transceiver, 0);
+		udc->driver->resume(&udc->gadget);
+		spin_lock(udc->lock);
+		udc->suspended = 0;
+	}
+}
+
+/**
+ * isr_resume_handler: USB SLI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_suspend_handler(struct ci13xxx *udc)
+{
+	if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+		udc->vbus_active) {
+		if (udc->suspended == 0) {
+			spin_unlock(udc->lock);
+			udc->driver->suspend(&udc->gadget);
+			if (udc->udc_driver->notify_event)
+				udc->udc_driver->notify_event(udc,
+				CI13XXX_CONTROLLER_SUSPEND_EVENT);
+			if (udc->transceiver)
+				usb_phy_set_suspend(udc->transceiver, 1);
+			spin_lock(udc->lock);
+			udc->suspended = 1;
+		}
+	}
+}
+
+/**
  * isr_get_status_complete: get_status request complete function
  * @ep:  endpoint
  * @req: request handled
@@ -1769,8 +2132,15 @@
 	}
 
 	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
-		/* Assume that device is bus powered for now. */
-		*((u16 *)req->buf) = _udc->remote_wakeup << 1;
+		if (setup->wIndex == OTG_STATUS_SELECTOR) {
+			*((u8 *)req->buf) = _udc->gadget.host_request <<
+						HOST_REQUEST_FLAG;
+			req->length = 1;
+		} else {
+			/* Assume that device is bus powered for now. */
+			*((u16 *)req->buf) = _udc->remote_wakeup << 1;
+		}
+		/* TODO: D1 - Remote Wakeup; D0 - Self Powered */
 		retval = 0;
 	} else if ((setup->bRequestType & USB_RECIP_MASK) \
 		   == USB_RECIP_ENDPOINT) {
@@ -1860,17 +2230,35 @@
 	struct ci13xxx_req *mReq, *mReqTemp;
 	struct ci13xxx_ep *mEpTemp = mEp;
 	int uninitialized_var(retval);
+	int req_dequeue = 1;
+	struct ci13xxx *udc = _udc;
 
 	trace("%p", mEp);
 
 	if (list_empty(&mEp->qh.queue))
-		return -EINVAL;
+		return 0;
 
 	list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
 			queue) {
+dequeue:
 		retval = _hardware_dequeue(mEp, mReq);
-		if (retval < 0)
+		if (retval < 0) {
+			/*
+			 * FIXME: don't know exact delay
+			 * required for HW to update dTD status
+			 * bits. This is a temporary workaround till
+			 * HW designers come back on this.
+			 */
+			if (retval == -EBUSY && req_dequeue && mEp->dir == 0) {
+				req_dequeue = 0;
+				udc->dTD_update_fail_count++;
+				mEp->dTD_update_fail_count++;
+				udelay(10);
+				goto dequeue;
+			}
 			break;
+		}
+		req_dequeue = 0;
 		list_del_init(&mReq->queue);
 		dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
 		if (mReq->req.complete != NULL) {
@@ -1955,6 +2343,8 @@
 		do {
 			hw_test_and_set_setup_guard();
 			memcpy(&req, &mEp->qh.ptr->setup, sizeof(req));
+			/* Ensure buffer is read before acknowledging to h/w */
+			mb();
 		} while (!hw_test_and_clear_setup_guard());
 
 		type = req.bRequestType;
@@ -2000,8 +2390,7 @@
 			    type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
 			    type != (USB_DIR_IN|USB_RECIP_INTERFACE))
 				goto delegate;
-			if (le16_to_cpu(req.wLength) != 2 ||
-			    le16_to_cpu(req.wValue)  != 0)
+			if (le16_to_cpu(req.wValue)  != 0)
 				break;
 			err = isr_get_status_response(udc, &req);
 			break;
@@ -2016,6 +2405,10 @@
 				break;
 			err = isr_setup_status_phase(udc);
 			break;
+		case USB_REQ_SET_CONFIGURATION:
+			if (type == (USB_DIR_OUT|USB_TYPE_STANDARD))
+				udc->configured = !!req.wValue;
+			goto delegate;
 		case USB_REQ_SET_FEATURE:
 			if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
 					le16_to_cpu(req.wValue) ==
@@ -2041,6 +2434,16 @@
 					udc->remote_wakeup = 1;
 					err = isr_setup_status_phase(udc);
 					break;
+				case USB_DEVICE_B_HNP_ENABLE:
+					udc->gadget.b_hnp_enable = 1;
+					err = isr_setup_status_phase(udc);
+					break;
+				case USB_DEVICE_A_HNP_SUPPORT:
+					udc->gadget.a_hnp_support = 1;
+					err = isr_setup_status_phase(udc);
+					break;
+				case USB_DEVICE_A_ALT_HNP_SUPPORT:
+					break;
 				case USB_DEVICE_TEST_MODE:
 					tmode = le16_to_cpu(req.wIndex) >> 8;
 					switch (tmode) {
@@ -2053,11 +2456,21 @@
 						err = isr_setup_status_phase(
 								udc);
 						break;
+					case TEST_OTG_SRP_REQD:
+						udc->gadget.otg_srp_reqd = 1;
+						err = isr_setup_status_phase(
+								udc);
+						break;
+					case TEST_OTG_HNP_REQD:
+						udc->gadget.host_request = 1;
+						err = isr_setup_status_phase(
+								udc);
+						break;
 					default:
 						break;
 					}
 				default:
-					goto delegate;
+					break;
 				}
 			} else {
 				goto delegate;
@@ -2129,12 +2542,15 @@
 	else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
 		mEp->qh.ptr->cap &= ~QH_MULT;
 	else
-		mEp->qh.ptr->cap &= ~QH_ZLT;
+		mEp->qh.ptr->cap |= QH_ZLT;
 
 	mEp->qh.ptr->cap |=
 		(mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
 	mEp->qh.ptr->td.next |= TD_TERMINATE;   /* needed? */
 
+	/* complete all the updates to ept->head before enabling endpoint*/
+	mb();
+
 	/*
 	 * Enable endpoints in the HW other than ep0 as ep0
 	 * is always enabled
@@ -2266,6 +2682,7 @@
 	struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
 	int retval = 0;
 	unsigned long flags;
+	struct ci13xxx *udc = _udc;
 
 	trace("%p, %p, %X", ep, req, gfp_flags);
 
@@ -2274,6 +2691,15 @@
 
 	spin_lock_irqsave(mEp->lock, flags);
 
+	if (!udc->configured && mEp->type !=
+		USB_ENDPOINT_XFER_CONTROL) {
+		spin_unlock_irqrestore(mEp->lock, flags);
+		trace("usb is not configured"
+			"ept #%d, ept name#%s\n",
+			mEp->num, mEp->ep.name);
+		return -ESHUTDOWN;
+	}
+
 	if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
 		if (req->length)
 			mEp = (_udc->ep0_dir == RX) ?
@@ -2326,6 +2752,7 @@
 static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 {
 	struct ci13xxx_ep  *mEp  = container_of(ep,  struct ci13xxx_ep, ep);
+	struct ci13xxx_ep *mEpTemp = mEp;
 	struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
 	unsigned long flags;
 
@@ -2354,7 +2781,10 @@
 
 	if (mReq->req.complete != NULL) {
 		spin_unlock(mEp->lock);
-		mReq->req.complete(&mEp->ep, &mReq->req);
+		if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+				mReq->req.length)
+			mEpTemp = &_udc->ep0in;
+		mReq->req.complete(&mEpTemp->ep, &mReq->req);
 		spin_lock(mEp->lock);
 	}
 
@@ -2362,6 +2792,12 @@
 	return 0;
 }
 
+static int is_sps_req(struct ci13xxx_req *mReq)
+{
+	return (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID &&
+			mReq->req.udc_priv & MSM_SPS_MODE);
+}
+
 /**
  * ep_set_halt: sets the endpoint halt feature
  *
@@ -2383,7 +2819,9 @@
 #ifndef STALL_IN
 	/* g_file_storage MS compliant but g_zero fails chapter 9 compliance */
 	if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX &&
-	    !list_empty(&mEp->qh.queue)) {
+		!list_empty(&mEp->qh.queue) &&
+		!is_sps_req(list_entry(mEp->qh.queue.next, struct ci13xxx_req,
+							   queue))){
 		spin_unlock_irqrestore(mEp->lock, flags);
 		return -EAGAIN;
 	}
@@ -2494,13 +2932,14 @@
 		if (is_active) {
 			pm_runtime_get_sync(&_gadget->dev);
 			hw_device_reset(udc);
-			hw_device_state(udc->ep0out.qh.dma);
+			if (udc->softconnect)
+				hw_device_state(udc->ep0out.qh.dma);
 		} else {
 			hw_device_state(0);
+			_gadget_stop_activity(&udc->gadget);
 			if (udc->udc_driver->notify_event)
 				udc->udc_driver->notify_event(udc,
-				CI13XXX_CONTROLLER_STOPPED_EVENT);
-			_gadget_stop_activity(&udc->gadget);
+					CI13XXX_CONTROLLER_DISCONNECT_EVENT);
 			pm_runtime_put_sync(&_gadget->dev);
 		}
 	}
@@ -2508,31 +2947,6 @@
 	return 0;
 }
 
-static int ci13xxx_wakeup(struct usb_gadget *_gadget)
-{
-	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
-	unsigned long flags;
-	int ret = 0;
-
-	trace();
-
-	spin_lock_irqsave(udc->lock, flags);
-	if (!udc->remote_wakeup) {
-		ret = -EOPNOTSUPP;
-		trace("remote wakeup feature is not enabled\n");
-		goto out;
-	}
-	if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
-		ret = -EINVAL;
-		trace("port is not suspended\n");
-		goto out;
-	}
-	hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
-out:
-	spin_unlock_irqrestore(udc->lock, flags);
-	return ret;
-}
-
 static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
 {
 	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
@@ -2542,6 +2956,32 @@
 	return -ENOTSUPP;
 }
 
+static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(udc->lock, flags);
+	udc->softconnect = is_active;
+	if (((udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) &&
+			!udc->vbus_active) || !udc->driver) {
+		spin_unlock_irqrestore(udc->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(udc->lock, flags);
+
+	if (is_active) {
+		hw_device_state(udc->ep0out.qh.dma);
+		if (udc->udc_driver->notify_event)
+			udc->udc_driver->notify_event(udc,
+				CI13XXX_CONTROLLER_CONNECT_EVENT);
+	}
+	else
+		hw_device_state(0);
+
+	return 0;
+}
+
 static int ci13xxx_start(struct usb_gadget_driver *driver,
 		int (*bind)(struct usb_gadget *));
 static int ci13xxx_stop(struct usb_gadget_driver *driver);
@@ -2554,6 +2994,7 @@
 	.vbus_session	= ci13xxx_vbus_session,
 	.wakeup		= ci13xxx_wakeup,
 	.vbus_draw	= ci13xxx_vbus_draw,
+	.pullup		= ci13xxx_pullup,
 	.start		= ci13xxx_start,
 	.stop		= ci13xxx_stop,
 };
@@ -2573,6 +3014,7 @@
 	unsigned long flags;
 	int i, j;
 	int retval = -ENOMEM;
+	bool put = false;
 
 	trace("%p", driver);
 
@@ -2660,8 +3102,10 @@
 	/* bind gadget */
 	driver->driver.bus     = NULL;
 	udc->gadget.dev.driver = &driver->driver;
+	udc->softconnect = 1;
 
 	spin_unlock_irqrestore(udc->lock, flags);
+	pm_runtime_get_sync(&udc->gadget.dev);
 	retval = bind(&udc->gadget);                /* MAY SLEEP */
 	spin_lock_irqsave(udc->lock, flags);
 
@@ -2671,23 +3115,27 @@
 	}
 
 	udc->driver = driver;
-	pm_runtime_get_sync(&udc->gadget.dev);
 	if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
 		if (udc->vbus_active) {
 			if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
 				hw_device_reset(udc);
 		} else {
-			pm_runtime_put_sync(&udc->gadget.dev);
+			put = true;
 			goto done;
 		}
 	}
 
+	if (!udc->softconnect) {
+		put = true;
+		goto done;
+	}
+
 	retval = hw_device_state(udc->ep0out.qh.dma);
-	if (retval)
-		pm_runtime_put_sync(&udc->gadget.dev);
 
  done:
 	spin_unlock_irqrestore(udc->lock, flags);
+	if (retval || put)
+		pm_runtime_put_sync(&udc->gadget.dev);
 	return retval;
 }
 
@@ -2715,9 +3163,6 @@
 	if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
 			udc->vbus_active) {
 		hw_device_state(0);
-		if (udc->udc_driver->notify_event)
-			udc->udc_driver->notify_event(udc,
-			CI13XXX_CONTROLLER_STOPPED_EVENT);
 		spin_unlock_irqrestore(udc->lock, flags);
 		_gadget_stop_activity(&udc->gadget);
 		spin_lock_irqsave(udc->lock, flags);
@@ -2803,14 +3248,7 @@
 		}
 		if (USBi_PCI & intr) {
 			isr_statistics.pci++;
-			udc->gadget.speed = hw_port_is_high_speed() ?
-				USB_SPEED_HIGH : USB_SPEED_FULL;
-			if (udc->suspended && udc->driver->resume) {
-				spin_unlock(udc->lock);
-				udc->driver->resume(&udc->gadget);
-				spin_lock(udc->lock);
-				udc->suspended = 0;
-			}
+			isr_resume_handler(udc);
 		}
 		if (USBi_UEI & intr)
 			isr_statistics.uei++;
@@ -2819,13 +3257,7 @@
 			isr_tr_complete_handler(udc);
 		}
 		if (USBi_SLI & intr) {
-			if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
-			    udc->driver->suspend) {
-				udc->suspended = 1;
-				spin_unlock(udc->lock);
-				udc->driver->suspend(&udc->gadget);
-				spin_lock(udc->lock);
-			}
+			isr_suspend_handler(udc);
 			isr_statistics.sli++;
 		}
 		retval = IRQ_HANDLED;
@@ -2866,7 +3298,7 @@
 		void __iomem *regs)
 {
 	struct ci13xxx *udc;
-	int retval = 0;
+	int retval = 0, i;
 
 	trace("%p, %p, %p", dev, regs, driver->name);
 
@@ -2885,7 +3317,10 @@
 	udc->gadget.ops          = &usb_gadget_ops;
 	udc->gadget.speed        = USB_SPEED_UNKNOWN;
 	udc->gadget.max_speed    = USB_SPEED_HIGH;
-	udc->gadget.is_otg       = 0;
+	if (udc->udc_driver->flags & CI13XXX_IS_OTG)
+		udc->gadget.is_otg       = 1;
+	else
+		udc->gadget.is_otg       = 0;
 	udc->gadget.name         = driver->name;
 
 	INIT_LIST_HEAD(&udc->gadget.ep_list);
@@ -2897,19 +3332,23 @@
 	udc->gadget.dev.parent   = dev;
 	udc->gadget.dev.release  = udc_release;
 
-	retval = hw_device_init(regs);
-	if (retval < 0)
-		goto free_udc;
-
-	udc->transceiver = usb_get_transceiver();
-
 	if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
+		udc->transceiver = usb_get_transceiver();
 		if (udc->transceiver == NULL) {
 			retval = -ENODEV;
 			goto free_udc;
 		}
 	}
 
+	retval = hw_device_init(regs);
+	if (retval < 0)
+		goto put_transceiver;
+
+	for (i = 0; i < hw_ep_max; i++) {
+		struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+		INIT_LIST_HEAD(&mEp->ep.ep_list);
+	}
+
 	if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
 		retval = hw_device_reset(udc);
 		if (retval)
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 0d31af5..4376804 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -25,6 +25,14 @@
 #define RX        (0)  /* similar to USB_DIR_OUT but can be used as an index */
 #define TX        (1)  /* similar to USB_DIR_IN  but can be used as an index */
 
+/* UDC private data:
+ *  16MSb - Vendor ID | 16 LSb Vendor private data
+ */
+#define CI13XX_REQ_VENDOR_ID(id)  (id & 0xFFFF0000UL)
+
+#define MSM_ETD_TYPE			BIT(1)
+#define MSM_EP_PIPE_ID_RESET_VAL	0x1F001F
+
 /******************************************************************************
  * STRUCTURES
  *****************************************************************************/
@@ -98,6 +106,7 @@
 	spinlock_t                            *lock;
 	struct device                         *device;
 	struct dma_pool                       *td_pool;
+	unsigned long dTD_update_fail_count;
 };
 
 struct ci13xxx;
@@ -108,9 +117,15 @@
 #define CI13XXX_REQUIRE_TRANSCEIVER	BIT(1)
 #define CI13XXX_PULLUP_ON_VBUS		BIT(2)
 #define CI13XXX_DISABLE_STREAMING	BIT(3)
+#define CI13XXX_ZERO_ITC		BIT(4)
+#define CI13XXX_IS_OTG			BIT(5)
 
-#define CI13XXX_CONTROLLER_RESET_EVENT		0
-#define CI13XXX_CONTROLLER_STOPPED_EVENT	1
+#define CI13XXX_CONTROLLER_RESET_EVENT			0
+#define CI13XXX_CONTROLLER_CONNECT_EVENT		1
+#define CI13XXX_CONTROLLER_SUSPEND_EVENT		2
+#define CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT	3
+#define CI13XXX_CONTROLLER_RESUME_EVENT	        4
+#define CI13XXX_CONTROLLER_DISCONNECT_EVENT	    5
 	void	(*notify_event) (struct ci13xxx *udc, unsigned event);
 };
 
@@ -131,11 +146,14 @@
 	u8                         remote_wakeup; /* Is remote wakeup feature
 							enabled by the host? */
 	u8                         suspended;  /* suspended by the host */
+	u8                         configured;  /* is device configured */
 	u8                         test_mode;  /* the selected test mode */
 
 	struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */
 	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
 	int                        vbus_active; /* is VBUS active */
+	int                        softconnect; /* is pull-up enable allowed */
+	unsigned long dTD_update_fail_count;
 	struct usb_phy            *transceiver; /* Transceiver struct */
 };
 
@@ -189,6 +207,8 @@
 #define    USBMODE_CM_HOST    (0x03UL <<  0)
 #define USBMODE_SLOM          BIT(3)
 #define USBMODE_SDIS          BIT(4)
+#define USBCMD_ITC(n)         (n << 16) /* n = 0, 1, 2, 4, 8, 16, 32, 64 */
+#define USBCMD_ITC_MASK       (0xFF << 16)
 
 /* ENDPTCTRL */
 #define ENDPTCTRL_RXS         BIT(0)
@@ -212,7 +232,10 @@
 			   "[%s] " format "\n", __func__, ## args); \
 } while (0)
 
+#ifndef err
 #define err(format, args...)    ci13xxx_printk(KERN_ERR, format, ## args)
+#endif
+
 #define warn(format, args...)   ci13xxx_printk(KERN_WARNING, format, ## args)
 #define info(format, args...)   ci13xxx_printk(KERN_INFO, format, ## args)
 
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 8f82fc0..d35d861 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -29,7 +29,7 @@
  */
 
 /* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ	1024
+#define USB_BUFSIZ	4096
 
 static struct usb_composite_driver *composite;
 static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
@@ -175,13 +175,14 @@
 	_ep->comp_desc = comp_desc;
 	if (g->speed == USB_SPEED_SUPER) {
 		switch (usb_endpoint_type(_ep->desc)) {
-		case USB_ENDPOINT_XFER_ISOC:
-			/* mult: bits 1:0 of bmAttributes */
-			_ep->mult = comp_desc->bmAttributes & 0x3;
 		case USB_ENDPOINT_XFER_BULK:
 		case USB_ENDPOINT_XFER_INT:
 			_ep->maxburst = comp_desc->bMaxBurst;
 			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			/* mult: bits 1:0 of bmAttributes */
+			_ep->mult = comp_desc->bmAttributes & 0x3;
+			break;
 		default:
 			/* Do nothing for control endpoints */
 			break;
@@ -687,6 +688,7 @@
 	power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
 done:
 	usb_gadget_vbus_draw(gadget, power);
+
 	if (result >= 0 && cdev->delayed_status)
 		result = USB_GADGET_DELAYED_STATUS;
 	return result;
@@ -1080,6 +1082,11 @@
 	u16				w_length = le16_to_cpu(ctrl->wLength);
 	struct usb_function		*f = NULL;
 	u8				endp;
+	struct usb_configuration *c;
+
+
+	if (w_length > USB_BUFSIZ)
+		return value;
 
 	/* partial re-init of the response message; the function or the
 	 * gadget might need to intercept e.g. a control-OUT completion
@@ -1133,6 +1140,16 @@
 			if (value >= 0)
 				value = min(w_length, (u16) value);
 			break;
+		case USB_DT_OTG:
+			if (!gadget_is_otg(gadget))
+				break;
+			c = list_first_entry(&cdev->configs,
+				struct usb_configuration, list);
+			if (c && c->descriptors)
+				value = usb_find_descriptor_fillbuf(req->buf,
+						USB_BUFSIZ, c->descriptors,
+						USB_DT_OTG);
+			break;
 		case USB_DT_STRING:
 			value = get_string(cdev, req->buf,
 					w_index, w_value & 0xff);
@@ -1606,6 +1623,8 @@
 int usb_composite_probe(struct usb_composite_driver *driver,
 			       int (*bind)(struct usb_composite_dev *cdev))
 {
+	int retval;
+
 	if (!driver || !driver->dev || !bind || composite)
 		return -EINVAL;
 
@@ -1620,7 +1639,10 @@
 	composite = driver;
 	composite_gadget_bind = bind;
 
-	return usb_gadget_probe_driver(&composite_driver, composite_bind);
+	retval = usb_gadget_probe_driver(&composite_driver, composite_bind);
+	if (retval)
+		composite = NULL;
+	return retval;
 }
 
 /**
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 7542a72..c52d8b6 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -19,6 +19,40 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
+/**
+ * usb_find_descriptor_fillbuf - fill buffer with the requested descriptor
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ * @desc_type: bDescriptorType field of the requested descriptor.
+ *
+ * Copies the requested descriptor into the buffer, returning the length
+ * or a negative error code if it is not found or can't be copied.  Useful
+ * when DT_OTG descriptor is requested.
+ */
+int
+usb_find_descriptor_fillbuf(void *buf, unsigned buflen,
+		const struct usb_descriptor_header **src, u8 desc_type)
+{
+	if (!src)
+		return -EINVAL;
+
+	for (; NULL != *src; src++) {
+		unsigned len;
+
+		if ((*src)->bDescriptorType != desc_type)
+			continue;
+
+		len = (*src)->bLength;
+		if (len > buflen)
+			return -EINVAL;
+
+		memcpy(buf, *src, len);
+		return len;
+	}
+
+	return -ENOENT;
+}
 
 /**
  * usb_descriptor_fillbuf - fill buffer with descriptors
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index a581822..108caf9 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -296,7 +296,7 @@
 	}
 }
 
-static int __init create_bulk_endpoints(struct acc_dev *dev,
+static int create_bulk_endpoints(struct acc_dev *dev,
 				struct usb_endpoint_descriptor *in_desc,
 				struct usb_endpoint_descriptor *out_desc)
 {
@@ -688,19 +688,31 @@
 	DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt);
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
-	if (ret)
-		return ret;
-
+	if (ret) {
+		dev->ep_in->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->ep_in->name, ret);
+			return ret;
+	}
 	ret = usb_ep_enable(dev->ep_in);
-	if (ret)
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_in->name, ret);
 		return ret;
+	}
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
-	if (ret)
+	if (ret) {
+		dev->ep_out->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
 		return ret;
-
+	}
 	ret = usb_ep_enable(dev->ep_out);
 	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+				dev->ep_out->name, ret);
 		usb_ep_disable(dev->ep_in);
 		return ret;
 	}
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index d672250a..cc151cb 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2008 by David Brownell
  * Copyright (C) 2008 by Nokia Corporation
  * Copyright (C) 2009 by Samsung Electronics
+ * Copyright (c) 2011 Code Aurora Forum. All rights reserved.
  * Author: Michal Nazarewicz (mina86@mina86.com)
  *
  * This software is distributed under the terms of the GNU General
@@ -17,6 +18,8 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <linux/usb/android_composite.h>
+#include <mach/usb_gadget_xport.h>
 
 #include "u_serial.h"
 #include "gadget_chips.h"
@@ -43,6 +46,7 @@
 	struct gserial			port;
 	u8				ctrl_id, data_id;
 	u8				port_num;
+	enum transport_type		transport;
 
 	u8				pending;
 
@@ -73,6 +77,17 @@
 #define ACM_CTRL_DCD		(1 << 0)
 };
 
+static unsigned int no_acm_tty_ports;
+static unsigned int no_acm_sdio_ports;
+static unsigned int no_acm_smd_ports;
+static unsigned int nr_acm_ports;
+
+static struct acm_port_info {
+	enum transport_type	transport;
+	unsigned		port_num;
+	unsigned		client_port_num;
+} gacm_ports[GSERIAL_NO_PORTS];
+
 static inline struct f_acm *func_to_acm(struct usb_function *f)
 {
 	return container_of(f, struct f_acm, port.func);
@@ -83,6 +98,82 @@
 	return container_of(p, struct f_acm, port);
 }
 
+static int acm_port_setup(struct usb_configuration *c)
+{
+	int ret = 0;
+
+	pr_debug("%s: no_acm_tty_ports:%u no_acm_sdio_ports: %u nr_acm_ports:%u\n",
+			__func__, no_acm_tty_ports, no_acm_sdio_ports,
+				nr_acm_ports);
+
+	if (no_acm_tty_ports)
+		ret = gserial_setup(c->cdev->gadget, no_acm_tty_ports);
+	if (no_acm_sdio_ports)
+		ret = gsdio_setup(c->cdev->gadget, no_acm_sdio_ports);
+	if (no_acm_smd_ports)
+		ret = gsmd_setup(c->cdev->gadget, no_acm_smd_ports);
+
+	return ret;
+}
+
+static int acm_port_connect(struct f_acm *acm)
+{
+	unsigned port_num;
+
+	port_num = gacm_ports[acm->port_num].client_port_num;
+
+
+	pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_port_no:%d\n",
+			__func__, xport_to_str(acm->transport),
+			acm, &acm->port, acm->port_num, port_num);
+
+	switch (acm->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_connect(&acm->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_connect(&acm->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_connect(&acm->port, port_num);
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(acm->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int acm_port_disconnect(struct f_acm *acm)
+{
+	unsigned port_num;
+
+	port_num = gacm_ports[acm->port_num].client_port_num;
+
+	pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_pno:%d\n",
+			__func__, xport_to_str(acm->transport),
+			acm, &acm->port, acm->port_num, port_num);
+
+	switch (acm->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_disconnect(&acm->port);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_disconnect(&acm->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_disconnect(&acm->port, port_num);
+		break;
+	default:
+		pr_err("%s: Un-supported transport:%s\n", __func__,
+				xport_to_str(acm->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
 /*-------------------------------------------------------------------------*/
 
 /* notification endpoint uses smallish and infrequent fixed-size messages */
@@ -359,8 +450,7 @@
 	/* SET_LINE_CODING ... just read and save what the host sends */
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_SET_LINE_CODING:
-		if (w_length != sizeof(struct usb_cdc_line_coding)
-				|| w_index != acm->ctrl_id)
+		if (w_length != sizeof(struct usb_cdc_line_coding))
 			goto invalid;
 
 		value = w_length;
@@ -371,8 +461,6 @@
 	/* GET_LINE_CODING ... return what host sent, or initial value */
 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_GET_LINE_CODING:
-		if (w_index != acm->ctrl_id)
-			goto invalid;
 
 		value = min_t(unsigned, w_length,
 				sizeof(struct usb_cdc_line_coding));
@@ -382,9 +470,6 @@
 	/* SET_CONTROL_LINE_STATE ... save what the host sent */
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
-		if (w_index != acm->ctrl_id)
-			goto invalid;
-
 		value = 0;
 
 		/* FIXME we should not allow data to flow until the
@@ -392,6 +477,12 @@
 		 * that bit, we should return to that no-flow state.
 		 */
 		acm->port_handshake_bits = w_value;
+		if (acm->port.notify_modem) {
+			unsigned port_num =
+				gacm_ports[acm->port_num].client_port_num;
+
+			acm->port.notify_modem(&acm->port, port_num, w_value);
+		}
 		break;
 
 	default:
@@ -431,16 +522,17 @@
 			usb_ep_disable(acm->notify);
 		} else {
 			VDBG(cdev, "init acm ctrl interface %d\n", intf);
-			if (config_ep_by_speed(cdev->gadget, f, acm->notify))
-				return -EINVAL;
 		}
+		if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+			return -EINVAL;
+
 		usb_ep_enable(acm->notify);
 		acm->notify->driver_data = acm;
 
 	} else if (intf == acm->data_id) {
 		if (acm->port.in->driver_data) {
 			DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
-			gserial_disconnect(&acm->port);
+			acm_port_disconnect(acm);
 		}
 		if (!acm->port.in->desc || !acm->port.out->desc) {
 			DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
@@ -453,7 +545,16 @@
 				return -EINVAL;
 			}
 		}
-		gserial_connect(&acm->port, acm->port_num);
+		if (config_ep_by_speed(cdev->gadget, f,
+				acm->port.in) ||
+			config_ep_by_speed(cdev->gadget, f,
+				acm->port.out)) {
+			acm->port.in->desc = NULL;
+			acm->port.out->desc = NULL;
+			return -EINVAL;
+		}
+
+		acm_port_connect(acm);
 
 	} else
 		return -EINVAL;
@@ -467,7 +568,7 @@
 	struct usb_composite_dev *cdev = f->config->cdev;
 
 	DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
-	gserial_disconnect(&acm->port);
+	acm_port_disconnect(acm);
 	usb_ep_disable(acm->notify);
 	acm->notify->driver_data = NULL;
 }
@@ -598,6 +699,15 @@
 	return acm_notify_serial_state(acm);
 }
 
+static int acm_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
+{
+	struct f_acm *acm = port_to_acm(port);
+
+	acm->serial_state = ctrl_bits;
+
+	return acm_notify_serial_state(acm);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* ACM function driver setup/binding */
@@ -678,6 +788,8 @@
 
 		/* copy descriptors */
 		f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
+		if (!f->hs_descriptors)
+			goto fail;
 	}
 	if (gadget_is_superspeed(c->cdev->gadget)) {
 		acm_ss_in_desc.bEndpointAddress =
@@ -700,6 +812,11 @@
 	return 0;
 
 fail:
+	if (f->hs_descriptors)
+		usb_free_descriptors(f->hs_descriptors);
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+
 	if (acm->notify_req)
 		gs_free_req(acm->notify, acm->notify_req);
 
@@ -727,6 +844,7 @@
 		usb_free_descriptors(f->ss_descriptors);
 	usb_free_descriptors(f->descriptors);
 	gs_free_req(acm->notify, acm->notify_req);
+	kfree(acm->port.func.name);
 	kfree(acm);
 }
 
@@ -793,12 +911,18 @@
 	spin_lock_init(&acm->lock);
 
 	acm->port_num = port_num;
+	acm->transport = gacm_ports[port_num].transport;
 
 	acm->port.connect = acm_connect;
 	acm->port.disconnect = acm_disconnect;
 	acm->port.send_break = acm_send_break;
+	acm->port.send_modem_ctrl_bits = acm_send_modem_ctrl_bits;
 
-	acm->port.func.name = "acm";
+	acm->port.func.name = kasprintf(GFP_KERNEL, "acm%u", port_num + 1);
+	if (!acm->port.func.name) {
+		kfree(acm);
+		return -ENOMEM;
+	}
 	acm->port.func.strings = acm_strings;
 	/* descriptors are per-instance copies */
 	acm->port.func.bind = acm_bind;
@@ -812,3 +936,44 @@
 		kfree(acm);
 	return status;
 }
+
+/**
+ * acm_init_port - bind a acm_port to its transport
+ */
+static int acm_init_port(int port_num, const char *name)
+{
+	enum transport_type transport;
+
+	if (port_num >= GSERIAL_NO_PORTS)
+		return -ENODEV;
+
+	transport = str_to_xport(name);
+	pr_debug("%s, port:%d, transport:%s\n", __func__,
+			port_num, xport_to_str(transport));
+
+	gacm_ports[port_num].transport = transport;
+	gacm_ports[port_num].port_num = port_num;
+
+	switch (transport) {
+	case USB_GADGET_XPORT_TTY:
+		gacm_ports[port_num].client_port_num = no_acm_tty_ports;
+		no_acm_tty_ports++;
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gacm_ports[port_num].client_port_num = no_acm_sdio_ports;
+		no_acm_sdio_ports++;
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gacm_ports[port_num].client_port_num = no_acm_smd_ports;
+		no_acm_smd_ports++;
+		break;
+	default:
+		pr_err("%s: Un-supported transport transport: %u\n",
+				__func__, gacm_ports[port_num].transport);
+		return -ENODEV;
+	}
+
+	nr_acm_ports++;
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 1629ffb..9778673 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -42,8 +42,8 @@
 	struct usb_ep *ep_in;
 	struct usb_ep *ep_out;
 
-	int online;
-	int error;
+	atomic_t online;
+	atomic_t error;
 
 	atomic_t read_excl;
 	atomic_t write_excl;
@@ -195,7 +195,7 @@
 	struct adb_dev *dev = _adb_dev;
 
 	if (req->status != 0)
-		dev->error = 1;
+		atomic_set(&dev->error, 1);
 
 	adb_req_put(dev, &dev->tx_idle, req);
 
@@ -208,7 +208,7 @@
 
 	dev->rx_done = 1;
 	if (req->status != 0 && req->status != -ECONNRESET)
-		dev->error = 1;
+		atomic_set(&dev->error, 1);
 
 	wake_up(&dev->read_wq);
 }
@@ -283,16 +283,17 @@
 		return -EBUSY;
 
 	/* we will block until we're online */
-	while (!(dev->online || dev->error)) {
+	while (!(atomic_read(&dev->online) || atomic_read(&dev->error))) {
 		pr_debug("adb_read: waiting for online state\n");
 		ret = wait_event_interruptible(dev->read_wq,
-				(dev->online || dev->error));
+			(atomic_read(&dev->online) ||
+			atomic_read(&dev->error)));
 		if (ret < 0) {
 			adb_unlock(&dev->read_excl);
 			return ret;
 		}
 	}
-	if (dev->error) {
+	if (atomic_read(&dev->error)) {
 		r = -EIO;
 		goto done;
 	}
@@ -306,7 +307,7 @@
 	if (ret < 0) {
 		pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret);
 		r = -EIO;
-		dev->error = 1;
+		atomic_set(&dev->error, 1);
 		goto done;
 	} else {
 		pr_debug("rx %p queue\n", req);
@@ -316,12 +317,12 @@
 	ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
 	if (ret < 0) {
 		if (ret != -ERESTARTSYS)
-			dev->error = 1;
+		atomic_set(&dev->error, 1);
 		r = ret;
 		usb_ep_dequeue(dev->ep_out, req);
 		goto done;
 	}
-	if (!dev->error) {
+	if (!atomic_read(&dev->error)) {
 		/* If we got a 0-len packet, throw it back and try again. */
 		if (req->actual == 0)
 			goto requeue_req;
@@ -356,7 +357,7 @@
 		return -EBUSY;
 
 	while (count > 0) {
-		if (dev->error) {
+		if (atomic_read(&dev->error)) {
 			pr_debug("adb_write dev->error\n");
 			r = -EIO;
 			break;
@@ -365,7 +366,8 @@
 		/* get an idle tx request to use */
 		req = 0;
 		ret = wait_event_interruptible(dev->write_wq,
-			(req = adb_req_get(dev, &dev->tx_idle)) || dev->error);
+			((req = adb_req_get(dev, &dev->tx_idle)) ||
+			 atomic_read(&dev->error)));
 
 		if (ret < 0) {
 			r = ret;
@@ -386,7 +388,7 @@
 			ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);
 			if (ret < 0) {
 				pr_debug("adb_write: xfer error %d\n", ret);
-				dev->error = 1;
+				atomic_set(&dev->error, 1);
 				r = -EIO;
 				break;
 			}
@@ -419,7 +421,7 @@
 	fp->private_data = _adb_dev;
 
 	/* clear the error latch */
-	_adb_dev->error = 0;
+	atomic_set(&_adb_dev->error, 0);
 
 	adb_ready_callback();
 
@@ -498,8 +500,8 @@
 	struct usb_request *req;
 
 
-	dev->online = 0;
-	dev->error = 1;
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->error, 1);
 
 	wake_up(&dev->read_wq);
 
@@ -518,23 +520,35 @@
 	DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
-	if (ret)
+	if (ret) {
+		dev->ep_in->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->ep_in->name, ret);
 		return ret;
-
+	}
 	ret = usb_ep_enable(dev->ep_in);
-	if (ret)
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_in->name, ret);
 		return ret;
+	}
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
-	if (ret)
-		return ret;
-
-	ret = usb_ep_enable(dev->ep_out);
 	if (ret) {
+		dev->ep_out->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_out->name, ret);
 		usb_ep_disable(dev->ep_in);
 		return ret;
 	}
-	dev->online = 1;
+	ret = usb_ep_enable(dev->ep_out);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+				dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
+		return ret;
+	}
+	atomic_set(&dev->online, 1);
 
 	/* readers may be blocked waiting for us to go online */
 	wake_up(&dev->read_wq);
@@ -547,8 +561,8 @@
 	struct usb_composite_dev	*cdev = dev->cdev;
 
 	DBG(cdev, "adb_function_disable cdev %p\n", cdev);
-	dev->online = 0;
-	dev->error = 1;
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->error, 1);
 	usb_ep_disable(dev->ep_in);
 	usb_ep_disable(dev->ep_out);
 
diff --git a/drivers/usb/gadget/f_ccid.c b/drivers/usb/gadget/f_ccid.c
new file mode 100644
index 0000000..c8f144a
--- /dev/null
+++ b/drivers/usb/gadget/f_ccid.c
@@ -0,0 +1,999 @@
+/*
+ * f_ccid.c -- CCID function Driver
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/usb/android_composite.h>
+#include <linux/fs.h>
+#include <linux/usb/ccid_desc.h>
+#include <linux/miscdevice.h>
+
+#include "f_ccid.h"
+
+#define BULK_IN_BUFFER_SIZE sizeof(struct ccid_bulk_in_header)
+#define BULK_OUT_BUFFER_SIZE sizeof(struct ccid_bulk_out_header)
+#define CTRL_BUF_SIZE	4
+#define FUNCTION_NAME	"ccid"
+#define CCID_NOTIFY_INTERVAL	5
+#define CCID_NOTIFY_MAXPACKET	4
+
+/* number of tx requests to allocate */
+#define TX_REQ_MAX 4
+
+struct ccid_ctrl_dev {
+	atomic_t opened;
+	struct list_head tx_q;
+	wait_queue_head_t tx_wait_q;
+	unsigned char buf[CTRL_BUF_SIZE];
+	int tx_ctrl_done;
+};
+
+struct ccid_bulk_dev {
+	atomic_t error;
+	atomic_t opened;
+	atomic_t rx_req_busy;
+	wait_queue_head_t read_wq;
+	wait_queue_head_t write_wq;
+	struct usb_request *rx_req;
+	int rx_done;
+	struct list_head tx_idle;
+};
+
+struct f_ccid {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+	int ifc_id;
+	spinlock_t lock;
+	atomic_t online;
+	/* usb eps*/
+	struct usb_ep *notify;
+	struct usb_ep *in;
+	struct usb_ep *out;
+	struct usb_request *notify_req;
+	struct ccid_ctrl_dev ctrl_dev;
+	struct ccid_bulk_dev bulk_dev;
+	int dtr_state;
+};
+
+static struct f_ccid *_ccid_dev;
+static struct miscdevice ccid_bulk_device;
+static struct miscdevice ccid_ctrl_device;
+
+/* Interface Descriptor: */
+static struct usb_interface_descriptor ccid_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_CSCID,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+};
+/* CCID Class Descriptor */
+static struct usb_ccid_class_descriptor ccid_class_desc = {
+	.bLength =		sizeof(ccid_class_desc),
+	.bDescriptorType =	CCID_DECRIPTOR_TYPE,
+	.bcdCCID =		CCID1_10,
+	.bMaxSlotIndex =	0,
+	/* This value indicates what voltages the CCID can supply to slots */
+	.bVoltageSupport =	VOLTS_3_0,
+	.dwProtocols =		PROTOCOL_TO,
+	/* Default ICC clock frequency in KHz */
+	.dwDefaultClock =	3580,
+	/* Maximum supported ICC clock frequency in KHz */
+	.dwMaximumClock =	3580,
+	.bNumClockSupported =	0,
+	/* Default ICC I/O data rate in bps */
+	.dwDataRate =		9600,
+	/* Maximum supported ICC I/O data rate in bps */
+	.dwMaxDataRate =	9600,
+	.bNumDataRatesSupported = 0,
+	.dwMaxIFSD =		0,
+	.dwSynchProtocols =	0,
+	.dwMechanical =		0,
+	/* This value indicates what intelligent features the CCID has */
+	.dwFeatures =		CCID_FEATURES_EXC_SAPDU |
+				CCID_FEATURES_AUTO_PNEGO |
+				CCID_FEATURES_AUTO_BAUD |
+				CCID_FEATURES_AUTO_CLOCK |
+				CCID_FEATURES_AUTO_VOLT |
+				CCID_FEATURES_AUTO_ACTIV |
+				CCID_FEATURES_AUTO_PCONF,
+	/* extended APDU level Message Length */
+	.dwMaxCCIDMessageLength = 0x200,
+	.bClassGetResponse =	0x0,
+	.bClassEnvelope =	0x0,
+	.wLcdLayout =		0,
+	.bPINSupport =		0,
+	.bMaxCCIDBusySlots =	1
+};
+/* Full speed support: */
+static struct usb_endpoint_descriptor ccid_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		1 << CCID_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor ccid_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	__constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor ccid_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	 __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *ccid_fs_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_fs_notify_desc,
+	(struct usb_descriptor_header *) &ccid_fs_in_desc,
+	(struct usb_descriptor_header *) &ccid_fs_out_desc,
+	NULL,
+};
+
+/* High speed support: */
+static struct usb_endpoint_descriptor ccid_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		CCID_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor ccid_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor ccid_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ccid_hs_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_hs_notify_desc,
+	(struct usb_descriptor_header *) &ccid_hs_in_desc,
+	(struct usb_descriptor_header *) &ccid_hs_out_desc,
+	NULL,
+};
+
+static inline struct f_ccid *func_to_ccid(struct usb_function *f)
+{
+	return container_of(f, struct f_ccid, function);
+}
+
+static void ccid_req_put(struct f_ccid *ccid_dev, struct list_head *head,
+		struct usb_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	list_add_tail(&req->list, head);
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+}
+
+static struct usb_request *ccid_req_get(struct f_ccid *ccid_dev,
+					struct list_head *head)
+{
+	unsigned long flags;
+	struct usb_request *req = NULL;
+
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	if (!list_empty(head)) {
+		req = list_first_entry(head, struct usb_request, list);
+		list_del(&req->list);
+	}
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	return req;
+}
+
+static void ccid_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		break;
+	default:
+		pr_err("CCID notify ep error %d\n", req->status);
+	}
+}
+
+static void ccid_bulk_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+
+	if (req->status != 0)
+		atomic_set(&bulk_dev->error, 1);
+
+	ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+	wake_up(&bulk_dev->write_wq);
+}
+
+static void ccid_bulk_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	if (req->status != 0)
+		atomic_set(&bulk_dev->error, 1);
+
+	bulk_dev->rx_done = 1;
+	wake_up(&bulk_dev->read_wq);
+}
+
+static struct usb_request *
+ccid_request_alloc(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+static void ccid_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int
+ccid_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_ccid *ccid_dev = container_of(f, struct f_ccid, function);
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int ret = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	if (!atomic_read(&ccid_dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_ABORT:
+		if (w_length != 0)
+			goto invalid;
+		ctrl_dev->buf[0] = CCIDGENERICREQ_ABORT;
+		ctrl_dev->buf[1] = w_value & 0xFF;
+		ctrl_dev->buf[2] = (w_value >> 8) & 0xFF;
+		ctrl_dev->buf[3] = 0x00;
+		ctrl_dev->tx_ctrl_done = 1;
+		wake_up(&ctrl_dev->tx_wait_q);
+		return 0;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_GET_CLOCK_FREQUENCIES:
+		if (w_length > req->length)
+			goto invalid;
+		*(u32 *) req->buf =
+				cpu_to_le32(ccid_class_desc.dwDefaultClock);
+		ret = min_t(u32, w_length,
+				sizeof(ccid_class_desc.dwDefaultClock));
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_GET_DATA_RATES:
+		if (w_length > req->length)
+			goto invalid;
+		*(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDataRate);
+		ret = min_t(u32, w_length, sizeof(ccid_class_desc.dwDataRate));
+		break;
+
+	default:
+invalid:
+	pr_debug("invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		pr_debug("ccid req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			pr_err("ccid ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void ccid_function_disable(struct usb_function *f)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	struct usb_request *req;
+
+	/* Disable endpoints */
+	usb_ep_disable(ccid_dev->notify);
+	usb_ep_disable(ccid_dev->in);
+	usb_ep_disable(ccid_dev->out);
+	/* Free endpoint related requests */
+	ccid_request_free(ccid_dev->notify_req, ccid_dev->notify);
+	if (!atomic_read(&bulk_dev->rx_req_busy))
+		ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+	while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)))
+		ccid_request_free(req, ccid_dev->in);
+
+	ccid_dev->dtr_state = 0;
+	atomic_set(&ccid_dev->online, 0);
+	/* Wake up threads */
+	wake_up(&bulk_dev->write_wq);
+	wake_up(&bulk_dev->read_wq);
+	wake_up(&ctrl_dev->tx_wait_q);
+
+}
+
+static int
+ccid_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct usb_composite_dev *cdev = ccid_dev->cdev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req;
+	int ret = 0;
+	int i;
+
+	ccid_dev->notify_req = ccid_request_alloc(ccid_dev->notify,
+			sizeof(struct usb_ccid_notification), GFP_ATOMIC);
+	if (IS_ERR(ccid_dev->notify_req)) {
+		pr_err("%s: unable to allocate memory for notify req\n",
+				__func__);
+		return PTR_ERR(ccid_dev->notify_req);
+	}
+	ccid_dev->notify_req->complete = ccid_notify_complete;
+	ccid_dev->notify_req->context = ccid_dev;
+
+	/* now allocate requests for our endpoints */
+	req = ccid_request_alloc(ccid_dev->out, BULK_OUT_BUFFER_SIZE,
+							GFP_ATOMIC);
+	if (IS_ERR(req)) {
+		pr_err("%s: unable to allocate memory for out req\n",
+				__func__);
+		ret = PTR_ERR(req);
+		goto free_notify;
+	}
+	req->complete = ccid_bulk_complete_out;
+	req->context = ccid_dev;
+	bulk_dev->rx_req = req;
+
+	for (i = 0; i < TX_REQ_MAX; i++) {
+		req = ccid_request_alloc(ccid_dev->in, BULK_IN_BUFFER_SIZE,
+								GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			pr_err("%s: unable to allocate memory for in req\n",
+					__func__);
+			ret = PTR_ERR(req);
+			goto free_bulk_out;
+		}
+		req->complete = ccid_bulk_complete_in;
+		req->context = ccid_dev;
+		ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+	}
+
+	/* choose the descriptors and enable endpoints */
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->notify);
+	if (ret) {
+		ccid_dev->notify->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->notify->name, ret);
+		goto free_bulk_in;
+	}
+	ret = usb_ep_enable(ccid_dev->notify);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->notify->name, ret);
+		goto free_bulk_in;
+	}
+	ccid_dev->notify->driver_data = ccid_dev;
+
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->in);
+	if (ret) {
+		ccid_dev->in->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->in->name, ret);
+		goto disable_ep_notify;
+	}
+	ret = usb_ep_enable(ccid_dev->in);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->in->name, ret);
+		goto disable_ep_notify;
+	}
+
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->out);
+	if (ret) {
+		ccid_dev->out->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->out->name, ret);
+		goto disable_ep_in;
+	}
+	ret = usb_ep_enable(ccid_dev->out);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->out->name, ret);
+		goto disable_ep_in;
+	}
+	ccid_dev->dtr_state = 1;
+	atomic_set(&ccid_dev->online, 1);
+	return ret;
+
+disable_ep_in:
+	usb_ep_disable(ccid_dev->in);
+disable_ep_notify:
+	usb_ep_disable(ccid_dev->notify);
+	ccid_dev->notify->driver_data = NULL;
+free_bulk_in:
+	while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)))
+		ccid_request_free(req, ccid_dev->in);
+free_bulk_out:
+	ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+free_notify:
+	ccid_request_free(ccid_dev->notify_req, ccid_dev->notify);
+	return ret;
+}
+
+static void ccid_function_unbind(struct usb_configuration *c,
+					struct usb_function *f)
+{
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+}
+
+static int ccid_function_bind(struct usb_configuration *c,
+					struct usb_function *f)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct usb_ep *ep;
+	struct usb_composite_dev *cdev = c->cdev;
+	int ret = -ENODEV;
+
+	ccid_dev->ifc_id = usb_interface_id(c, f);
+	if (ccid_dev->ifc_id < 0) {
+		pr_err("%s: unable to allocate ifc id, err:%d",
+				__func__, ccid_dev->ifc_id);
+		return ccid_dev->ifc_id;
+	}
+	ccid_interface_desc.bInterfaceNumber = ccid_dev->ifc_id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_notify_desc);
+	if (!ep) {
+		pr_err("%s: usb epnotify autoconfig failed\n", __func__);
+		return -ENODEV;
+	}
+	ccid_dev->notify = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc);
+	if (!ep) {
+		pr_err("%s: usb epin autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_in_fail;
+	}
+	ccid_dev->in = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc);
+	if (!ep) {
+		pr_err("%s: usb epout autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_out_fail;
+	}
+	ccid_dev->out = ep;
+	ep->driver_data = cdev;
+
+	f->descriptors = usb_copy_descriptors(ccid_fs_descs);
+	if (!f->descriptors)
+		goto ep_auto_out_fail;
+
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		ccid_hs_in_desc.bEndpointAddress =
+				ccid_fs_in_desc.bEndpointAddress;
+		ccid_hs_out_desc.bEndpointAddress =
+				ccid_fs_out_desc.bEndpointAddress;
+		ccid_hs_notify_desc.bEndpointAddress =
+				ccid_fs_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(ccid_hs_descs);
+		if (!f->hs_descriptors)
+			goto ep_auto_out_fail;
+	}
+
+	pr_debug("%s: CCID %s Speed, IN:%s OUT:%s\n", __func__,
+			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
+			ccid_dev->in->name, ccid_dev->out->name);
+
+	return 0;
+
+ep_auto_out_fail:
+	ccid_dev->out->driver_data = NULL;
+	ccid_dev->out = NULL;
+ep_auto_in_fail:
+	ccid_dev->in->driver_data = NULL;
+	ccid_dev->in = NULL;
+
+	return ret;
+}
+
+static int ccid_bulk_open(struct inode *ip, struct file *fp)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_open\n");
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (atomic_read(&bulk_dev->opened)) {
+		pr_debug("%s: bulk device is already opened\n", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&bulk_dev->opened, 1);
+	/* clear the error latch */
+	atomic_set(&bulk_dev->error, 0);
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	fp->private_data = ccid_dev;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+	return 0;
+}
+
+static int ccid_bulk_release(struct inode *ip, struct file *fp)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+
+	pr_debug("ccid_bulk_release\n");
+	atomic_set(&bulk_dev->opened, 0);
+	return 0;
+}
+
+static ssize_t ccid_bulk_read(struct file *fp, char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req;
+	int r = count, xfer;
+	int ret;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_read(%d)\n", count);
+
+	if (count > BULK_OUT_BUFFER_SIZE) {
+		pr_err("%s: max_buffer_size:%d given_pkt_size:%d\n",
+				__func__, BULK_OUT_BUFFER_SIZE, count);
+		return -ENOMEM;
+	}
+
+	if (atomic_read(&bulk_dev->error)) {
+		r = -EIO;
+		pr_err("%s bulk_dev_error\n", __func__);
+		goto done;
+	}
+
+requeue_req:
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	/* queue a request */
+	req = bulk_dev->rx_req;
+	req->length = count;
+	bulk_dev->rx_done = 0;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	ret = usb_ep_queue(ccid_dev->out, req, GFP_KERNEL);
+	if (ret < 0) {
+		r = -EIO;
+		pr_err("%s usb ep queue failed\n", __func__);
+		atomic_set(&bulk_dev->error, 1);
+		goto done;
+	}
+	/* wait for a request to complete */
+	ret = wait_event_interruptible(bulk_dev->read_wq, bulk_dev->rx_done ||
+					atomic_read(&bulk_dev->error) ||
+					!atomic_read(&ccid_dev->online));
+	if (ret < 0) {
+		atomic_set(&bulk_dev->error, 1);
+		r = ret;
+		usb_ep_dequeue(ccid_dev->out, req);
+		goto done;
+	}
+	if (!atomic_read(&bulk_dev->error)) {
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		if (!atomic_read(&ccid_dev->online)) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n", __func__);
+			r = -ENODEV;
+			goto done;
+		}
+		/* If we got a 0-len packet, throw it back and try again. */
+		if (req->actual == 0) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			goto requeue_req;
+		}
+		xfer = (req->actual < count) ? req->actual : count;
+		atomic_set(&bulk_dev->rx_req_busy, 1);
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+		if (copy_to_user(buf, req->buf, xfer))
+			r = -EFAULT;
+
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		atomic_set(&bulk_dev->rx_req_busy, 0);
+		if (!atomic_read(&ccid_dev->online)) {
+			ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n", __func__);
+			r = -ENODEV;
+			goto done;
+		}
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	} else {
+		r = -EIO;
+	}
+done:
+	pr_debug("ccid_bulk_read returning %d\n", r);
+	return r;
+}
+
+static ssize_t ccid_bulk_write(struct file *fp, const char __user *buf,
+				 size_t count, loff_t *pos)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req = 0;
+	int r = count;
+	int ret;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_write(%d)\n", count);
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("%s: zero length ctrl pkt\n", __func__);
+		return -ENODEV;
+	}
+	if (count > BULK_IN_BUFFER_SIZE) {
+		pr_err("%s: max_buffer_size:%d given_pkt_size:%d\n",
+				__func__, BULK_IN_BUFFER_SIZE, count);
+		return -ENOMEM;
+	}
+
+
+	/* get an idle tx request to use */
+	ret = wait_event_interruptible(bulk_dev->write_wq,
+		((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)) ||
+		 atomic_read(&bulk_dev->error)));
+
+	if (ret < 0) {
+		r = ret;
+		goto done;
+	}
+
+	if (atomic_read(&bulk_dev->error)) {
+		pr_err(" %s dev->error\n", __func__);
+		r = -EIO;
+		goto done;
+	}
+	if (copy_from_user(req->buf, buf, count)) {
+		if (!atomic_read(&ccid_dev->online)) {
+			pr_debug("%s: USB cable not connected\n",
+						__func__);
+			ccid_request_free(req, ccid_dev->in);
+			r = -ENODEV;
+		} else {
+			ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+			r = -EFAULT;
+		}
+		goto done;
+	}
+	req->length = count;
+	ret = usb_ep_queue(ccid_dev->in, req, GFP_KERNEL);
+	if (ret < 0) {
+		pr_debug("ccid_bulk_write: xfer error %d\n", ret);
+		atomic_set(&bulk_dev->error, 1);
+		ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+		r = -EIO;
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		if (!atomic_read(&ccid_dev->online)) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n",
+							__func__);
+			while ((req = ccid_req_get(ccid_dev,
+						&bulk_dev->tx_idle)))
+				ccid_request_free(req, ccid_dev->in);
+			r = -ENODEV;
+		}
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+		goto done;
+	}
+done:
+	pr_debug("ccid_bulk_write returning %d\n", r);
+	return r;
+}
+
+static const struct file_operations ccid_bulk_fops = {
+	.owner = THIS_MODULE,
+	.read = ccid_bulk_read,
+	.write = ccid_bulk_write,
+	.open = ccid_bulk_open,
+	.release = ccid_bulk_release,
+};
+
+static struct miscdevice ccid_bulk_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ccid_bulk",
+	.fops = &ccid_bulk_fops,
+};
+
+static int ccid_bulk_device_init(struct f_ccid *dev)
+{
+	int ret;
+	struct ccid_bulk_dev *bulk_dev = &dev->bulk_dev;
+
+	init_waitqueue_head(&bulk_dev->read_wq);
+	init_waitqueue_head(&bulk_dev->write_wq);
+	INIT_LIST_HEAD(&bulk_dev->tx_idle);
+
+	ret = misc_register(&ccid_bulk_device);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccid_ctrl_open(struct inode *inode, struct file *fp)
+{
+	struct f_ccid *ccid_dev =  _ccid_dev;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	unsigned long flags;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	if (atomic_read(&ctrl_dev->opened)) {
+		pr_debug("%s: ctrl device is already opened\n", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&ctrl_dev->opened, 1);
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	fp->private_data = ccid_dev;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+	return 0;
+}
+
+
+static int ccid_ctrl_release(struct inode *inode, struct file *fp)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+
+	atomic_set(&ctrl_dev->opened, 0);
+
+	return 0;
+}
+
+static ssize_t ccid_ctrl_read(struct file *fp, char __user *buf,
+		      size_t count, loff_t *ppos)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	int ret = 0;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	if (count > CTRL_BUF_SIZE)
+		count = CTRL_BUF_SIZE;
+
+	ret = wait_event_interruptible(ctrl_dev->tx_wait_q,
+					 ctrl_dev->tx_ctrl_done);
+	if (ret < 0)
+		return ret;
+	ctrl_dev->tx_ctrl_done = 0;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	ret = copy_to_user(buf, ctrl_dev->buf, count);
+	if (ret)
+		return -EFAULT;
+
+	return count;
+}
+
+static long
+ccid_ctrl_ioctl(struct file *fp, unsigned cmd, u_long arg)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct usb_request              *req = ccid_dev->notify_req;
+	struct usb_ccid_notification     *ccid_notify = req->buf;
+	void __user *argp = (void __user *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case CCID_NOTIFY_CARD:
+		if (copy_from_user(ccid_notify, argp,
+				sizeof(struct usb_ccid_notification)))
+			return -EFAULT;
+		req->length = 2;
+		break;
+	case CCID_NOTIFY_HWERROR:
+		if (copy_from_user(ccid_notify, argp,
+				sizeof(struct usb_ccid_notification)))
+			return -EFAULT;
+		req->length = 4;
+		break;
+	case CCID_READ_DTR:
+		if (copy_to_user((int *)arg, &ccid_dev->dtr_state, sizeof(int)))
+			return -EFAULT;
+		return 0;
+	}
+	ret = usb_ep_queue(ccid_dev->notify, ccid_dev->notify_req, GFP_KERNEL);
+	if (ret < 0) {
+		pr_err("ccid notify ep enqueue error %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static const struct file_operations ccid_ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ccid_ctrl_open,
+	.release	= ccid_ctrl_release,
+	.read		= ccid_ctrl_read,
+	.unlocked_ioctl	= ccid_ctrl_ioctl,
+};
+
+static struct miscdevice ccid_ctrl_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ccid_ctrl",
+	.fops = &ccid_ctrl_fops,
+};
+
+static int ccid_ctrl_device_init(struct f_ccid *dev)
+{
+	int ret;
+	struct ccid_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	INIT_LIST_HEAD(&ctrl_dev->tx_q);
+	init_waitqueue_head(&ctrl_dev->tx_wait_q);
+
+	ret = misc_register(&ccid_ctrl_device);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccid_bind_config(struct usb_configuration *c)
+{
+	struct f_ccid *ccid_dev = _ccid_dev;
+
+	pr_debug("ccid_bind_config\n");
+	ccid_dev->cdev = c->cdev;
+	ccid_dev->function.name = FUNCTION_NAME;
+	ccid_dev->function.descriptors = ccid_fs_descs;
+	ccid_dev->function.hs_descriptors = ccid_hs_descs;
+	ccid_dev->function.bind = ccid_function_bind;
+	ccid_dev->function.unbind = ccid_function_unbind;
+	ccid_dev->function.set_alt = ccid_function_set_alt;
+	ccid_dev->function.setup = ccid_function_setup;
+	ccid_dev->function.disable = ccid_function_disable;
+
+	return usb_add_function(c, &ccid_dev->function);
+
+}
+
+static int ccid_setup(void)
+{
+	struct f_ccid  *ccid_dev;
+	int ret;
+
+	ccid_dev = kzalloc(sizeof(*ccid_dev), GFP_KERNEL);
+	if (!ccid_dev)
+		return -ENOMEM;
+
+	_ccid_dev = ccid_dev;
+	spin_lock_init(&ccid_dev->lock);
+
+	ret = ccid_ctrl_device_init(ccid_dev);
+	if (ret) {
+		pr_err("%s: ccid_ctrl_device_init failed, err:%d\n",
+				__func__, ret);
+		goto err_ctrl_init;
+	}
+	ret = ccid_bulk_device_init(ccid_dev);
+	if (ret) {
+		pr_err("%s: ccid_bulk_device_init failed, err:%d\n",
+				__func__, ret);
+		goto err_bulk_init;
+	}
+
+	return 0;
+err_bulk_init:
+	misc_deregister(&ccid_ctrl_device);
+err_ctrl_init:
+	kfree(ccid_dev);
+	pr_err("ccid gadget driver failed to initialize\n");
+	return ret;
+}
+
+static void ccid_cleanup(void)
+{
+	misc_deregister(&ccid_bulk_device);
+	misc_deregister(&ccid_ctrl_device);
+	kfree(_ccid_dev);
+}
diff --git a/drivers/usb/gadget/f_ccid.h b/drivers/usb/gadget/f_ccid.h
new file mode 100644
index 0000000..4d6a0ea
--- /dev/null
+++ b/drivers/usb/gadget/f_ccid.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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
+ */
+
+#ifndef __F_CCID_H
+#define __F_CCID_H
+
+#define PROTOCOL_TO 0x01
+#define PROTOCOL_T1 0x02
+#define ABDATA_SIZE 512
+
+/* define for dwFeatures for Smart Card Device Class Descriptors */
+/* No special characteristics */
+#define CCID_FEATURES_NADA       0x00000000
+/* Automatic parameter configuration based on ATR data */
+#define CCID_FEATURES_AUTO_PCONF 0x00000002
+/* Automatic activation of ICC on inserting */
+#define CCID_FEATURES_AUTO_ACTIV 0x00000004
+/* Automatic ICC voltage selection */
+#define CCID_FEATURES_AUTO_VOLT  0x00000008
+/* Automatic ICC clock frequency change */
+#define CCID_FEATURES_AUTO_CLOCK 0x00000010
+/* Automatic baud rate change */
+#define CCID_FEATURES_AUTO_BAUD  0x00000020
+/*Automatic parameters negotiation made by the CCID */
+#define CCID_FEATURES_AUTO_PNEGO 0x00000040
+/* Automatic PPS made by the CCID according to the active parameters */
+#define CCID_FEATURES_AUTO_PPS   0x00000080
+/* CCID can set ICC in clock stop mode */
+#define CCID_FEATURES_ICCSTOP    0x00000100
+/* NAD value other than 00 accepted (T=1 protocol in use) */
+#define CCID_FEATURES_NAD        0x00000200
+/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */
+#define CCID_FEATURES_AUTO_IFSD  0x00000400
+/* TPDU level exchanges with CCID */
+#define CCID_FEATURES_EXC_TPDU   0x00010000
+/* Short APDU level exchange with CCID */
+#define CCID_FEATURES_EXC_SAPDU  0x00020000
+/* Short and Extended APDU level exchange with CCID */
+#define CCID_FEATURES_EXC_APDU   0x00040000
+/* USB Wake up signaling supported on card insertion and removal */
+#define CCID_FEATURES_WAKEUP     0x00100000
+
+#define CCID_NOTIFY_CARD	_IOW('C', 1, struct usb_ccid_notification)
+#define CCID_NOTIFY_HWERROR	_IOW('C', 2, struct usb_ccid_notification)
+#define CCID_READ_DTR		_IOR('C', 3, int)
+
+struct usb_ccid_notification {
+	unsigned char buf[4];
+} __packed;
+
+struct ccid_bulk_in_header {
+	unsigned char bMessageType;
+	unsigned long wLength;
+	unsigned char bSlot;
+	unsigned char bSeq;
+	unsigned char bStatus;
+	unsigned char bError;
+	unsigned char bSpecific;
+	unsigned char abData[ABDATA_SIZE];
+	unsigned char bSizeToSend;
+} __packed;
+
+struct ccid_bulk_out_header {
+	unsigned char bMessageType;
+	unsigned long wLength;
+	unsigned char bSlot;
+	unsigned char bSeq;
+	unsigned char bSpecific_0;
+	unsigned char bSpecific_1;
+	unsigned char bSpecific_2;
+	unsigned char APDU[ABDATA_SIZE];
+} __packed;
+#endif
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
new file mode 100644
index 0000000..72bff49
--- /dev/null
+++ b/drivers/usb/gadget/f_diag.c
@@ -0,0 +1,759 @@
+/* drivers/usb/gadget/f_diag.c
+ * Diag Function Device - Route ARM9 and ARM11 DIAG messages
+ * between HOST and DEVICE.
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <mach/usbdiag.h>
+#include <mach/rpc_hsusb.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+
+static DEFINE_SPINLOCK(ch_lock);
+static LIST_HEAD(usb_diag_ch_list);
+
+static struct usb_interface_descriptor intf_desc = {
+	.bLength            =	sizeof intf_desc,
+	.bDescriptorType    =	USB_DT_INTERFACE,
+	.bNumEndpoints      =	2,
+	.bInterfaceClass    =	0xFF,
+	.bInterfaceSubClass =	0xFF,
+	.bInterfaceProtocol =	0xFF,
+};
+
+static struct usb_endpoint_descriptor hs_bulk_in_desc = {
+	.bLength 			=	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType 	=	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes 		=	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize 	=	__constant_cpu_to_le16(512),
+	.bInterval 			=	0,
+};
+static struct usb_endpoint_descriptor fs_bulk_in_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+	.bInterval        =	0,
+};
+
+static struct usb_endpoint_descriptor hs_bulk_out_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(512),
+	.bInterval        =	0,
+};
+
+static struct usb_endpoint_descriptor fs_bulk_out_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+	.bInterval        =	0,
+};
+
+static struct usb_descriptor_header *fs_diag_desc[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &fs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fs_bulk_out_desc,
+	NULL,
+	};
+static struct usb_descriptor_header *hs_diag_desc[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &hs_bulk_in_desc,
+	(struct usb_descriptor_header *) &hs_bulk_out_desc,
+	NULL,
+};
+
+/**
+ * struct diag_context - USB diag function driver private structure
+ * @function: function structure for USB interface
+ * @out: USB OUT endpoint struct
+ * @in: USB IN endpoint struct
+ * @in_desc: USB IN endpoint descriptor struct
+ * @out_desc: USB OUT endpoint descriptor struct
+ * @read_pool: List of requests used for Rx (OUT ep)
+ * @write_pool: List of requests used for Tx (IN ep)
+ * @config_work: Work item schedule after interface is configured to notify
+ *               CONNECT event to diag char driver and updating product id
+ *               and serial number to MODEM/IMEM.
+ * @lock: Spinlock to proctect read_pool, write_pool lists
+ * @cdev: USB composite device struct
+ * @ch: USB diag channel
+ *
+ */
+struct diag_context {
+	struct usb_function function;
+	struct usb_ep *out;
+	struct usb_ep *in;
+	struct list_head read_pool;
+	struct list_head write_pool;
+	struct work_struct config_work;
+	spinlock_t lock;
+	unsigned configured;
+	struct usb_composite_dev *cdev;
+	int (*update_pid_and_serial_num)(uint32_t, const char *);
+	struct usb_diag_ch ch;
+
+	/* pkt counters */
+	unsigned long dpkts_tolaptop;
+	unsigned long dpkts_tomodem;
+	unsigned dpkts_tolaptop_pending;
+};
+
+static inline struct diag_context *func_to_diag(struct usb_function *f)
+{
+	return container_of(f, struct diag_context, function);
+}
+
+static void usb_config_work_func(struct work_struct *work)
+{
+	struct diag_context *ctxt = container_of(work,
+			struct diag_context, config_work);
+	struct usb_composite_dev *cdev = ctxt->cdev;
+	struct usb_gadget_strings *table;
+	struct usb_string *s;
+
+	if (ctxt->ch.notify)
+		ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_CONNECT, NULL);
+
+	if (!ctxt->update_pid_and_serial_num)
+		return;
+
+	/* pass on product id and serial number to dload */
+	if (!cdev->desc.iSerialNumber) {
+		ctxt->update_pid_and_serial_num(
+					cdev->desc.idProduct, 0);
+		return;
+	}
+
+	/*
+	 * Serial number is filled by the composite driver. So
+	 * it is fair enough to assume that it will always be
+	 * found at first table of strings.
+	 */
+	table = *(cdev->driver->strings);
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == cdev->desc.iSerialNumber) {
+			ctxt->update_pid_and_serial_num(
+					cdev->desc.idProduct, s->s);
+			break;
+		}
+}
+
+static void diag_write_complete(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct diag_context *ctxt = ep->driver_data;
+	struct diag_request *d_req = req->context;
+	unsigned long flags;
+
+	ctxt->dpkts_tolaptop_pending--;
+
+	if (!req->status) {
+		if ((req->length >= ep->maxpacket) &&
+				((req->length % ep->maxpacket) == 0)) {
+			ctxt->dpkts_tolaptop_pending++;
+			req->length = 0;
+			d_req->actual = req->actual;
+			d_req->status = req->status;
+			/* Queue zero length packet */
+			usb_ep_queue(ctxt->in, req, GFP_ATOMIC);
+			return;
+		}
+	}
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+	list_add_tail(&req->list, &ctxt->write_pool);
+	if (req->length != 0) {
+		d_req->actual = req->actual;
+		d_req->status = req->status;
+	}
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	if (ctxt->ch.notify)
+		ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_WRITE_DONE, d_req);
+}
+
+static void diag_read_complete(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct diag_context *ctxt = ep->driver_data;
+	struct diag_request *d_req = req->context;
+	unsigned long flags;
+
+	d_req->actual = req->actual;
+	d_req->status = req->status;
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+	list_add_tail(&req->list, &ctxt->read_pool);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	ctxt->dpkts_tomodem++;
+
+	if (ctxt->ch.notify)
+		ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_READ_DONE, d_req);
+}
+
+/**
+ * usb_diag_open() - Open a diag channel over USB
+ * @name: Name of the channel
+ * @priv: Private structure pointer which will be passed in notify()
+ * @notify: Callback function to receive notifications
+ *
+ * This function iterates overs the available channels and returns
+ * the channel handler if the name matches. The notify callback is called
+ * for CONNECT, DISCONNECT, READ_DONE and WRITE_DONE events.
+ *
+ */
+struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
+		void (*notify)(void *, unsigned, struct diag_request *))
+{
+	struct usb_diag_ch *ch;
+	struct diag_context *ctxt;
+	unsigned long flags;
+	int found = 0;
+
+	spin_lock_irqsave(&ch_lock, flags);
+	/* Check if we already have a channel with this name */
+	list_for_each_entry(ch, &usb_diag_ch_list, list) {
+		if (!strcmp(name, ch->name)) {
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&ch_lock, flags);
+
+	if (!found) {
+		ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
+		if (!ctxt)
+			return ERR_PTR(-ENOMEM);
+
+		ch = &ctxt->ch;
+	}
+
+	ch->name = name;
+	ch->priv = priv;
+	ch->notify = notify;
+
+	spin_lock_irqsave(&ch_lock, flags);
+	list_add_tail(&ch->list, &usb_diag_ch_list);
+	spin_unlock_irqrestore(&ch_lock, flags);
+
+	return ch;
+}
+EXPORT_SYMBOL(usb_diag_open);
+
+/**
+ * usb_diag_close() - Close a diag channel over USB
+ * @ch: Channel handler
+ *
+ * This function closes the diag channel.
+ *
+ */
+void usb_diag_close(struct usb_diag_ch *ch)
+{
+	struct diag_context *dev = container_of(ch, struct diag_context, ch);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ch_lock, flags);
+	ch->priv = NULL;
+	ch->notify = NULL;
+	/* Free-up the resources if channel is no more active */
+	if (!ch->priv_usb) {
+		list_del(&ch->list);
+		kfree(dev);
+	}
+
+	spin_unlock_irqrestore(&ch_lock, flags);
+}
+EXPORT_SYMBOL(usb_diag_close);
+
+/**
+ * usb_diag_free_req() - Free USB requests
+ * @ch: Channel handler
+ *
+ * This function free read and write USB requests for the interface
+ * associated with this channel.
+ *
+ */
+void usb_diag_free_req(struct usb_diag_ch *ch)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+
+	if (!ctxt)
+		return;
+
+	list_for_each_safe(act, tmp, &ctxt->write_pool) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ctxt->in, req);
+	}
+
+	list_for_each_safe(act, tmp, &ctxt->read_pool) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ctxt->out, req);
+	}
+}
+EXPORT_SYMBOL(usb_diag_free_req);
+
+/**
+ * usb_diag_alloc_req() - Allocate USB requests
+ * @ch: Channel handler
+ * @n_write: Number of requests for Tx
+ * @n_read: Number of requests for Rx
+ *
+ * This function allocate read and write USB requests for the interface
+ * associated with this channel. The actual buffer is not allocated.
+ * The buffer is passed by diag char driver.
+ *
+ */
+int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	struct usb_request *req;
+	int i;
+
+	if (!ctxt)
+		return -ENODEV;
+
+	for (i = 0; i < n_write; i++) {
+		req = usb_ep_alloc_request(ctxt->in, GFP_ATOMIC);
+		if (!req)
+			goto fail;
+		req->complete = diag_write_complete;
+		list_add_tail(&req->list, &ctxt->write_pool);
+	}
+
+	for (i = 0; i < n_read; i++) {
+		req = usb_ep_alloc_request(ctxt->out, GFP_ATOMIC);
+		if (!req)
+			goto fail;
+		req->complete = diag_read_complete;
+		list_add_tail(&req->list, &ctxt->read_pool);
+	}
+
+	return 0;
+
+fail:
+	usb_diag_free_req(ch);
+	return -ENOMEM;
+
+}
+EXPORT_SYMBOL(usb_diag_alloc_req);
+
+/**
+ * usb_diag_read() - Read data from USB diag channel
+ * @ch: Channel handler
+ * @d_req: Diag request struct
+ *
+ * Enqueue a request on OUT endpoint of the interface corresponding to this
+ * channel. This function returns proper error code when interface is not
+ * in configured state, no Rx requests available and ep queue is failed.
+ *
+ * This function operates asynchronously. READ_DONE event is notified after
+ * completion of OUT request.
+ *
+ */
+int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	unsigned long flags;
+	struct usb_request *req;
+
+	if (!ctxt)
+		return -ENODEV;
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+
+	if (!ctxt->configured) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		return -EIO;
+	}
+
+	if (list_empty(&ctxt->read_pool)) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
+		return -EAGAIN;
+	}
+
+	req = list_first_entry(&ctxt->read_pool, struct usb_request, list);
+	list_del(&req->list);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	req->buf = d_req->buf;
+	req->length = d_req->length;
+	req->context = d_req;
+	if (usb_ep_queue(ctxt->out, req, GFP_ATOMIC)) {
+		/* If error add the link to linked list again*/
+		spin_lock_irqsave(&ctxt->lock, flags);
+		list_add_tail(&req->list, &ctxt->read_pool);
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: cannot queue"
+				" read request\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_diag_read);
+
+/**
+ * usb_diag_write() - Write data from USB diag channel
+ * @ch: Channel handler
+ * @d_req: Diag request struct
+ *
+ * Enqueue a request on IN endpoint of the interface corresponding to this
+ * channel. This function returns proper error code when interface is not
+ * in configured state, no Tx requests available and ep queue is failed.
+ *
+ * This function operates asynchronously. WRITE_DONE event is notified after
+ * completion of IN request.
+ *
+ */
+int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	struct diag_context *ctxt = ch->priv_usb;
+	unsigned long flags;
+	struct usb_request *req = NULL;
+
+	if (!ctxt)
+		return -ENODEV;
+
+	spin_lock_irqsave(&ctxt->lock, flags);
+
+	if (!ctxt->configured) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		return -EIO;
+	}
+
+	if (list_empty(&ctxt->write_pool)) {
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
+		return -EAGAIN;
+	}
+
+	req = list_first_entry(&ctxt->write_pool, struct usb_request, list);
+	list_del(&req->list);
+	spin_unlock_irqrestore(&ctxt->lock, flags);
+
+	req->buf = d_req->buf;
+	req->length = d_req->length;
+	req->context = d_req;
+	if (usb_ep_queue(ctxt->in, req, GFP_ATOMIC)) {
+		/* If error add the link to linked list again*/
+		spin_lock_irqsave(&ctxt->lock, flags);
+		list_add_tail(&req->list, &ctxt->write_pool);
+		spin_unlock_irqrestore(&ctxt->lock, flags);
+		ERROR(ctxt->cdev, "%s: cannot queue"
+				" read request\n", __func__);
+		return -EIO;
+	}
+
+	ctxt->dpkts_tolaptop++;
+	ctxt->dpkts_tolaptop_pending++;
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_diag_write);
+
+static void diag_function_disable(struct usb_function *f)
+{
+	struct diag_context  *dev = func_to_diag(f);
+	unsigned long flags;
+
+	DBG(dev->cdev, "diag_function_disable\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->configured = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (dev->ch.notify)
+		dev->ch.notify(dev->ch.priv, USB_DIAG_DISCONNECT, NULL);
+
+	usb_ep_disable(dev->in);
+	dev->in->driver_data = NULL;
+
+	usb_ep_disable(dev->out);
+	dev->out->driver_data = NULL;
+
+}
+
+static int diag_function_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct diag_context  *dev = func_to_diag(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	unsigned long flags;
+	int rc = 0;
+
+	if (config_ep_by_speed(cdev->gadget, f, dev->in) ||
+	    config_ep_by_speed(cdev->gadget, f, dev->out)) {
+		dev->in->desc = NULL;
+		dev->out->desc = NULL;
+		return -EINVAL;
+	}
+
+	dev->in->driver_data = dev;
+	rc = usb_ep_enable(dev->in);
+	if (rc) {
+		ERROR(dev->cdev, "can't enable %s, result %d\n",
+						dev->in->name, rc);
+		return rc;
+	}
+	dev->out->driver_data = dev;
+	rc = usb_ep_enable(dev->out);
+	if (rc) {
+		ERROR(dev->cdev, "can't enable %s, result %d\n",
+						dev->out->name, rc);
+		usb_ep_disable(dev->in);
+		return rc;
+	}
+	schedule_work(&dev->config_work);
+
+	dev->dpkts_tolaptop = 0;
+	dev->dpkts_tomodem = 0;
+	dev->dpkts_tolaptop_pending = 0;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->configured = 1;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return rc;
+}
+
+static void diag_function_unbind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct diag_context *ctxt = func_to_diag(f);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+
+	usb_free_descriptors(f->descriptors);
+	ctxt->ch.priv_usb = NULL;
+}
+
+static int diag_function_bind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct diag_context *ctxt = func_to_diag(f);
+	struct usb_ep *ep;
+	int status = -ENODEV;
+
+	intf_desc.bInterfaceNumber =  usb_interface_id(c, f);
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_in_desc);
+	if (!ep)
+		goto fail;
+	ctxt->in = ep;
+	ep->driver_data = ctxt;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_out_desc);
+	if (!ep)
+		goto fail;
+	ctxt->out = ep;
+	ep->driver_data = ctxt;
+
+	/* copy descriptors, and track endpoint copies */
+	f->descriptors = usb_copy_descriptors(fs_diag_desc);
+	if (!f->descriptors)
+		goto fail;
+
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_bulk_in_desc.bEndpointAddress =
+				fs_bulk_in_desc.bEndpointAddress;
+		hs_bulk_out_desc.bEndpointAddress =
+				fs_bulk_out_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(hs_diag_desc);
+	}
+	return 0;
+fail:
+	if (ctxt->out)
+		ctxt->out->driver_data = NULL;
+	if (ctxt->in)
+		ctxt->in->driver_data = NULL;
+	return status;
+
+}
+
+int diag_function_add(struct usb_configuration *c, const char *name,
+			int (*update_pid)(uint32_t, const char *))
+{
+	struct diag_context *dev;
+	struct usb_diag_ch *_ch;
+	int found = 0, ret;
+
+	DBG(c->cdev, "diag_function_add\n");
+
+	list_for_each_entry(_ch, &usb_diag_ch_list, list) {
+		if (!strcmp(name, _ch->name)) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		ERROR(c->cdev, "unable to get diag usb channel\n");
+		return -ENODEV;
+	}
+
+	dev = container_of(_ch, struct diag_context, ch);
+	/* claim the channel for this USB interface */
+	_ch->priv_usb = dev;
+
+	dev->update_pid_and_serial_num = update_pid;
+	dev->cdev = c->cdev;
+	dev->function.name = _ch->name;
+	dev->function.descriptors = fs_diag_desc;
+	dev->function.hs_descriptors = hs_diag_desc;
+	dev->function.bind = diag_function_bind;
+	dev->function.unbind = diag_function_unbind;
+	dev->function.set_alt = diag_function_set_alt;
+	dev->function.disable = diag_function_disable;
+	spin_lock_init(&dev->lock);
+	INIT_LIST_HEAD(&dev->read_pool);
+	INIT_LIST_HEAD(&dev->write_pool);
+	INIT_WORK(&dev->config_work, usb_config_work_func);
+
+	ret = usb_add_function(c, &dev->function);
+	if (ret) {
+		INFO(c->cdev, "usb_add_function failed\n");
+		_ch->priv_usb = NULL;
+	}
+
+	return ret;
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	char *buf = debug_buffer;
+	int temp = 0;
+	struct usb_diag_ch *ch;
+
+	list_for_each_entry(ch, &usb_diag_ch_list, list) {
+		struct diag_context *ctxt = ch->priv_usb;
+
+		if (ctxt)
+			temp += scnprintf(buf + temp, PAGE_SIZE - temp,
+					"---Name: %s---\n"
+					"endpoints: %s, %s\n"
+					"dpkts_tolaptop: %lu\n"
+					"dpkts_tomodem:  %lu\n"
+					"pkts_tolaptop_pending: %u\n",
+					ch->name,
+					ctxt->in->name, ctxt->out->name,
+					ctxt->dpkts_tolaptop,
+					ctxt->dpkts_tomodem,
+					ctxt->dpkts_tolaptop_pending);
+	}
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+}
+
+static ssize_t debug_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_diag_ch *ch;
+
+	list_for_each_entry(ch, &usb_diag_ch_list, list) {
+		struct diag_context *ctxt = ch->priv_usb;
+
+		if (ctxt) {
+			ctxt->dpkts_tolaptop = 0;
+			ctxt->dpkts_tomodem = 0;
+			ctxt->dpkts_tolaptop_pending = 0;
+		}
+	}
+
+	return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations debug_fdiag_ops = {
+	.open = debug_open,
+	.read = debug_read_stats,
+	.write = debug_reset_stats,
+};
+
+struct dentry *dent_diag;
+static void fdiag_debugfs_init(void)
+{
+	dent_diag = debugfs_create_dir("usb_diag", 0);
+	if (IS_ERR(dent_diag))
+		return;
+
+	debugfs_create_file("status", 0444, dent_diag, 0, &debug_fdiag_ops);
+}
+#else
+static void fdiag_debugfs_init(void)
+{
+	return;
+}
+#endif
+
+static void diag_cleanup(void)
+{
+	struct diag_context *dev;
+	struct list_head *act, *tmp;
+	struct usb_diag_ch *_ch;
+	unsigned long flags;
+
+	debugfs_remove_recursive(dent_diag);
+
+	list_for_each_safe(act, tmp, &usb_diag_ch_list) {
+		_ch = list_entry(act, struct usb_diag_ch, list);
+		dev = container_of(_ch, struct diag_context, ch);
+
+		spin_lock_irqsave(&ch_lock, flags);
+		/* Free if diagchar is not using the channel anymore */
+		if (!_ch->priv) {
+			list_del(&_ch->list);
+			kfree(dev);
+		}
+		spin_unlock_irqrestore(&ch_lock, flags);
+	}
+}
+
+static int diag_setup(void)
+{
+	fdiag_debugfs_init();
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/f_diag.h b/drivers/usb/gadget/f_diag.h
new file mode 100644
index 0000000..82d9a25
--- /dev/null
+++ b/drivers/usb/gadget/f_diag.h
@@ -0,0 +1,24 @@
+/* drivers/usb/gadget/f_diag.h
+ *
+ * Diag Function Device - Route DIAG frames between SMD and USB
+ *
+ * Copyright (C) 2008-2009 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#ifndef __F_DIAG_H
+#define __F_DIAG_H
+
+int diag_function_add(struct usb_configuration *c, const char *);
+
+#endif /* __F_DIAG_H */
+
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index cb8c162..278e04e 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -310,7 +310,10 @@
 
 #include "storage_common.c"
 
-
+#ifdef CONFIG_USB_CSW_HACK
+static int write_error_after_csw_sent;
+static int csw_hack_sent;
+#endif
 /*-------------------------------------------------------------------------*/
 
 struct fsg_dev;
@@ -467,6 +470,7 @@
 }
 
 typedef void (*fsg_routine_t)(struct fsg_dev *);
+static int send_status(struct fsg_common *common);
 
 static int exception_in_progress(struct fsg_common *common)
 {
@@ -746,6 +750,9 @@
 	loff_t			file_offset, file_offset_tmp;
 	unsigned int		amount;
 	ssize_t			nread;
+#ifdef CONFIG_USB_MSC_PROFILING
+	ktime_t			start, diff;
+#endif
 
 	/*
 	 * Get the starting Logical Block Address and check that it's
@@ -813,11 +820,20 @@
 
 		/* Perform the read */
 		file_offset_tmp = file_offset;
+
+#ifdef CONFIG_USB_MSC_PROFILING
+		start = ktime_get();
+#endif
 		nread = vfs_read(curlun->filp,
 				 (char __user *)bh->buf,
 				 amount, &file_offset_tmp);
 		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
-		      (unsigned long long)file_offset, (int)nread);
+		     (unsigned long long) file_offset, (int) nread);
+#ifdef CONFIG_USB_MSC_PROFILING
+		diff = ktime_sub(ktime_get(), start);
+		curlun->perf.rbytes += nread;
+		curlun->perf.rtime = ktime_add(curlun->perf.rtime, diff);
+#endif
 		if (signal_pending(current))
 			return -EINTR;
 
@@ -879,6 +895,13 @@
 	ssize_t			nwritten;
 	int			rc;
 
+#ifdef CONFIG_USB_CSW_HACK
+	int			i;
+#endif
+
+#ifdef CONFIG_USB_MSC_PROFILING
+	ktime_t			start, diff;
+#endif
 	if (curlun->ro) {
 		curlun->sense_data = SS_WRITE_PROTECTED;
 		return -EINVAL;
@@ -971,7 +994,17 @@
 		bh = common->next_buffhd_to_drain;
 		if (bh->state == BUF_STATE_EMPTY && !get_some_more)
 			break;			/* We stopped early */
+#ifdef CONFIG_USB_CSW_HACK
+		/*
+		 * If the csw packet is already submmitted to the hardware,
+		 * by marking the state of buffer as full, then by checking
+		 * the residue, we make sure that this csw packet is not
+		 * written on to the storage media.
+		 */
+		if (bh->state == BUF_STATE_FULL && common->residue) {
+#else
 		if (bh->state == BUF_STATE_FULL) {
+#endif
 			smp_rmb();
 			common->next_buffhd_to_drain = bh->next;
 			bh->state = BUF_STATE_EMPTY;
@@ -1006,11 +1039,20 @@
 
 			/* Perform the write */
 			file_offset_tmp = file_offset;
+#ifdef CONFIG_USB_MSC_PROFILING
+			start = ktime_get();
+#endif
 			nwritten = vfs_write(curlun->filp,
 					     (char __user *)bh->buf,
 					     amount, &file_offset_tmp);
 			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
 			      (unsigned long long)file_offset, (int)nwritten);
+#ifdef CONFIG_USB_MSC_PROFILING
+			diff = ktime_sub(ktime_get(), start);
+			curlun->perf.wbytes += nwritten;
+			curlun->perf.wtime =
+					ktime_add(curlun->perf.wtime, diff);
+#endif
 			if (signal_pending(current))
 				return -EINTR;		/* Interrupted! */
 
@@ -1033,9 +1075,37 @@
 				curlun->sense_data_info =
 					file_offset >> curlun->blkbits;
 				curlun->info_valid = 1;
+#ifdef CONFIG_USB_CSW_HACK
+				write_error_after_csw_sent = 1;
+				goto write_error;
+#endif
 				break;
 			}
 
+#ifdef CONFIG_USB_CSW_HACK
+write_error:
+			if ((nwritten == amount) && !csw_hack_sent) {
+				if (write_error_after_csw_sent)
+					break;
+				/*
+				 * Check if any of the buffer is in the
+				 * busy state, if any buffer is in busy state,
+				 * means the complete data is not received
+				 * yet from the host. So there is no point in
+				 * csw right away without the complete data.
+				 */
+				for (i = 0; i < fsg_num_buffers; i++) {
+					if (common->buffhds[i].state ==
+							BUF_STATE_BUSY)
+						break;
+				}
+				if (!amount_left_to_req && i == fsg_num_buffers) {
+					csw_hack_sent = 1;
+					send_status(common);
+				}
+			}
+#endif
+
  empty_write:
 			/* Did the host decide to stop early? */
 			if (bh->outreq->actual < bh->bulk_out_intended_length) {
@@ -1497,8 +1567,7 @@
 		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
 		return -EINVAL;
 	}
-
-	if (curlun->prevent_medium_removal && !prevent)
+	if (!curlun->nofua && curlun->prevent_medium_removal && !prevent)
 		fsg_lun_fsync_sub(curlun);
 	curlun->prevent_medium_removal = prevent;
 	return 0;
@@ -1778,6 +1847,19 @@
 	csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
 	csw->Tag = common->tag;
 	csw->Residue = cpu_to_le32(common->residue);
+#ifdef CONFIG_USB_CSW_HACK
+	/* Since csw is being sent early, before
+	 * writing on to storage media, need to set
+	 * residue to zero,assuming that write will succeed.
+	 */
+	if (write_error_after_csw_sent) {
+		write_error_after_csw_sent = 0;
+		csw->Residue = cpu_to_le32(common->residue);
+	} else
+		csw->Residue = 0;
+#else
+	csw->Residue = cpu_to_le32(common->residue);
+#endif
 	csw->Status = status;
 
 	bh->inreq->length = US_BULK_CS_WRAP_LEN;
@@ -2366,15 +2448,6 @@
 			}
 		}
 
-		/* Disable the endpoints */
-		if (fsg->bulk_in_enabled) {
-			usb_ep_disable(fsg->bulk_in);
-			fsg->bulk_in_enabled = 0;
-		}
-		if (fsg->bulk_out_enabled) {
-			usb_ep_disable(fsg->bulk_out);
-			fsg->bulk_out_enabled = 0;
-		}
 
 		common->fsg = NULL;
 		wake_up(&common->fsg_wait);
@@ -2387,28 +2460,6 @@
 	common->fsg = new_fsg;
 	fsg = common->fsg;
 
-	/* Enable the endpoints */
-	rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
-	if (rc)
-		goto reset;
-	rc = usb_ep_enable(fsg->bulk_in);
-	if (rc)
-		goto reset;
-	fsg->bulk_in->driver_data = common;
-	fsg->bulk_in_enabled = 1;
-
-	rc = config_ep_by_speed(common->gadget, &(fsg->function),
-				fsg->bulk_out);
-	if (rc)
-		goto reset;
-	rc = usb_ep_enable(fsg->bulk_out);
-	if (rc)
-		goto reset;
-	fsg->bulk_out->driver_data = common;
-	fsg->bulk_out_enabled = 1;
-	common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
-	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
-
 	/* Allocate the requests */
 	for (i = 0; i < fsg_num_buffers; ++i) {
 		struct fsg_buffhd	*bh = &common->buffhds[i];
@@ -2437,14 +2488,55 @@
 static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct fsg_dev *fsg = fsg_from_func(f);
+	struct fsg_common *common = fsg->common;
+	int rc;
+
+	/* Enable the endpoints */
+	rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+	if (rc)
+		return rc;
+	rc = usb_ep_enable(fsg->bulk_in);
+	if (rc)
+		return rc;
+	fsg->bulk_in->driver_data = common;
+	fsg->bulk_in_enabled = 1;
+
+	rc = config_ep_by_speed(common->gadget, &(fsg->function),
+				fsg->bulk_out);
+	if (rc)
+		goto reset_bulk_int;
+	rc = usb_ep_enable(fsg->bulk_out);
+	if (rc)
+		goto reset_bulk_int;
+	fsg->bulk_out->driver_data = common;
+	fsg->bulk_out_enabled = 1;
+	common->bulk_out_maxpacket = le16_to_cpu(fsg->bulk_in->desc->wMaxPacketSize);
+	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
 	fsg->common->new_fsg = fsg;
 	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
 	return USB_GADGET_DELAYED_STATUS;
+
+reset_bulk_int:
+	usb_ep_disable(fsg->bulk_in);
+	fsg->bulk_in_enabled = 0;
+	return rc;
 }
 
 static void fsg_disable(struct usb_function *f)
 {
 	struct fsg_dev *fsg = fsg_from_func(f);
+
+	/* Disable the endpoints */
+	if (fsg->bulk_in_enabled) {
+		usb_ep_disable(fsg->bulk_in);
+		fsg->bulk_in_enabled = 0;
+		fsg->bulk_in->driver_data = NULL;
+	}
+	if (fsg->bulk_out_enabled) {
+		usb_ep_disable(fsg->bulk_out);
+		fsg->bulk_out_enabled = 0;
+		fsg->bulk_out->driver_data = NULL;
+	}
 	fsg->common->new_fsg = NULL;
 	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
 }
@@ -2651,6 +2743,16 @@
 			common->state = FSG_STATE_STATUS_PHASE;
 		spin_unlock_irq(&common->lock);
 
+#ifdef CONFIG_USB_CSW_HACK
+		/* Since status is already sent for write scsi command,
+		 * need to skip sending status once again if it is a
+		 * write scsi command.
+		 */
+		if (csw_hack_sent) {
+			csw_hack_sent = 0;
+			continue;
+		}
+#endif
 		if (send_status(common))
 			continue;
 
@@ -2691,7 +2793,9 @@
 static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
 static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
 static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
-
+#ifdef CONFIG_USB_MSC_PROFILING
+static DEVICE_ATTR(perf, 0644, fsg_show_perf, fsg_store_perf);
+#endif
 
 /****************************** FSG COMMON ******************************/
 
@@ -2788,6 +2892,7 @@
 		curlun->ro = lcfg->cdrom || lcfg->ro;
 		curlun->initially_ro = curlun->ro;
 		curlun->removable = lcfg->removable;
+		curlun->nofua = lcfg->nofua;
 		curlun->dev.release = fsg_lun_release;
 		curlun->dev.parent = &gadget->dev;
 		/* curlun->dev.driver = &fsg_driver.driver; XXX */
@@ -2815,7 +2920,12 @@
 		rc = device_create_file(&curlun->dev, &dev_attr_nofua);
 		if (rc)
 			goto error_luns;
-
+#ifdef CONFIG_USB_MSC_PROFILING
+		rc = device_create_file(&curlun->dev, &dev_attr_perf);
+		if (rc)
+			dev_err(&gadget->dev, "failed to create sysfs entry:"
+				"(dev_attr_perf) error: %d\n", rc);
+#endif
 		if (lcfg->filename) {
 			rc = fsg_lun_open(curlun, lcfg->filename);
 			if (rc)
@@ -2944,6 +3054,9 @@
 
 		/* In error recovery common->nluns may be zero. */
 		for (; i; --i, ++lun) {
+#ifdef CONFIG_USB_MSC_PROFILING
+			device_remove_file(&lun->dev, &dev_attr_perf);
+#endif
 			device_remove_file(&lun->dev, &dev_attr_nofua);
 			device_remove_file(&lun->dev, &dev_attr_ro);
 			device_remove_file(&lun->dev, &dev_attr_file);
@@ -3085,7 +3198,7 @@
 	if (unlikely(!fsg))
 		return -ENOMEM;
 
-	fsg->function.name        = FSG_DRIVER_DESC;
+	fsg->function.name        = "mass_storage";
 	fsg->function.strings     = fsg_strings_array;
 	fsg->function.bind        = fsg_bind;
 	fsg->function.unbind      = fsg_unbind;
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
new file mode 100644
index 0000000..41a1777
--- /dev/null
+++ b/drivers/usb/gadget/f_mbim.c
@@ -0,0 +1,1804 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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 pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <linux/usb/cdc.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+
+#include <linux/spinlock.h>
+
+/*
+ * This function is a "Mobile Broadband Interface Model" (MBIM) link.
+ * MBIM is intended to be used with high-speed network attachments.
+ *
+ * Note that MBIM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+#define MBIM_BULK_BUFFER_SIZE		4096
+
+#define MBIM_IOCTL_MAGIC		'o'
+#define MBIM_GET_NTB_SIZE		_IOR(MBIM_IOCTL_MAGIC, 2, u32)
+#define MBIM_GET_DATAGRAM_COUNT		_IOR(MBIM_IOCTL_MAGIC, 3, u16)
+
+#define NR_MBIM_PORTS			1
+
+struct ctrl_pkt {
+	void			*buf;
+	int			len;
+	struct list_head	list;
+};
+
+struct mbim_ep_descs {
+	struct usb_endpoint_descriptor	*in;
+	struct usb_endpoint_descriptor	*out;
+	struct usb_endpoint_descriptor	*notify;
+};
+
+struct mbim_notify_port {
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	u8				notify_state;
+	atomic_t			notify_count;
+};
+
+enum mbim_notify_state {
+	NCM_NOTIFY_NONE,
+	NCM_NOTIFY_CONNECT,
+	NCM_NOTIFY_SPEED,
+};
+
+struct f_mbim {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	atomic_t	online;
+	bool		is_open;
+
+	atomic_t	open_excl;
+	atomic_t	ioctl_excl;
+	atomic_t	read_excl;
+	atomic_t	write_excl;
+
+	wait_queue_head_t read_wq;
+	wait_queue_head_t write_wq;
+
+	u8				port_num;
+	struct data_port		bam_port;
+	struct mbim_notify_port		not_port;
+
+	struct mbim_ep_descs		fs;
+	struct mbim_ep_descs		hs;
+
+	u8				ctrl_id, data_id;
+
+	struct ndp_parser_opts		*parser_opts;
+
+	spinlock_t			lock;
+
+	struct list_head	cpkt_req_q;
+	struct list_head	cpkt_resp_q;
+
+	u32			ntb_input_size;
+	u16			ntb_max_datagrams;
+
+	atomic_t		error;
+};
+
+struct mbim_ntb_input_size {
+	u32	ntb_input_size;
+	u16	ntb_max_datagrams;
+	u16	reserved;
+};
+
+/* temporary variable used between mbim_open() and mbim_gadget_bind() */
+static struct f_mbim *_mbim_dev;
+
+static unsigned int nr_mbim_ports;
+
+static struct mbim_ports {
+	struct f_mbim	*port;
+	unsigned	port_num;
+} mbim_ports[NR_MBIM_PORTS];
+
+static inline struct f_mbim *func_to_mbim(struct usb_function *f)
+{
+	return container_of(f, struct f_mbim, function);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned mbim_bitrate(struct usb_gadget *g)
+{
+	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+		return 13 * 512 * 8 * 1000 * 8;
+	else
+		return 19 *  64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define NTB_DEFAULT_IN_SIZE	(0x4000)
+#define NTB_OUT_SIZE		(0x1000)
+#define NDP_IN_DIVISOR		(0x4)
+
+#define FORMATS_SUPPORTED	USB_CDC_NCM_NTB16_SUPPORTED
+
+static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
+	.wLength = sizeof ntb_parameters,
+	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
+	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
+	.wNdpInDivisor = cpu_to_le16(NDP_IN_DIVISOR),
+	.wNdpInPayloadRemainder = cpu_to_le16(0),
+	.wNdpInAlignment = cpu_to_le16(4),
+
+	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+	.wNdpOutDivisor = cpu_to_le16(4),
+	.wNdpOutPayloadRemainder = cpu_to_le16(0),
+	.wNdpOutAlignment = cpu_to_le16(4),
+	.wNtbOutMaxDatagrams = 0,
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC	5	/* 1 << 5 == 32 msec */
+#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor mbim_iad_desc = {
+	.bLength =		sizeof mbim_iad_desc,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount =	2,	/* control + data */
+	.bFunctionClass =	2,
+	.bFunctionSubClass =	0x0e,
+	.bFunctionProtocol =	0,
+	/* .iFunction =		DYNAMIC */
+};
+
+/* interface descriptor: */
+static struct usb_interface_descriptor mbim_control_intf = {
+	.bLength =		sizeof mbim_control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	0x02,
+	.bInterfaceSubClass =	0x0e,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc mbim_header_desc = {
+	.bLength =		sizeof mbim_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc mbim_union_desc = {
+	.bLength =		sizeof(mbim_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_mbb_desc mbb_desc = {
+	.bLength =		sizeof mbb_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_MBB_TYPE,
+
+	.bcdMbbVersion =	cpu_to_le16(0x0100),
+
+	.wMaxControlMessage =	cpu_to_le16(0x1000),
+	.bNumberFilters =	0x10,
+	.bMaxFilterSize =	0x80,
+	.wMaxSegmentSize =	cpu_to_le16(0xfe0),
+	.bmNetworkCapabilities = 0x20,
+};
+
+/* the default data interface has no endpoints ... */
+static struct usb_interface_descriptor mbim_data_nop_intf = {
+	.bLength =		sizeof mbim_data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+static struct usb_interface_descriptor mbim_data_intf = {
+	.bLength =		sizeof mbim_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_mbim_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor fs_mbim_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_mbim_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *mbim_fs_function[] = {
+	(struct usb_descriptor_header *) &mbim_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_control_intf,
+	(struct usb_descriptor_header *) &mbim_header_desc,
+	(struct usb_descriptor_header *) &mbb_desc,
+	(struct usb_descriptor_header *) &fs_mbim_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_data_intf,
+	(struct usb_descriptor_header *) &fs_mbim_in_desc,
+	(struct usb_descriptor_header *) &fs_mbim_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_mbim_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor hs_mbim_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_mbim_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *mbim_hs_function[] = {
+	(struct usb_descriptor_header *) &mbim_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_control_intf,
+	(struct usb_descriptor_header *) &mbim_header_desc,
+	(struct usb_descriptor_header *) &mbb_desc,
+	(struct usb_descriptor_header *) &hs_mbim_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_data_intf,
+	(struct usb_descriptor_header *) &hs_mbim_in_desc,
+	(struct usb_descriptor_header *) &hs_mbim_out_desc,
+	NULL,
+};
+
+/* string descriptors: */
+
+#define STRING_CTRL_IDX	0
+#define STRING_DATA_IDX	1
+
+static struct usb_string mbim_string_defs[] = {
+	[STRING_CTRL_IDX].s = "MBIM Control",
+	[STRING_DATA_IDX].s = "MBIM Data",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings mbim_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		mbim_string_defs,
+};
+
+static struct usb_gadget_strings *mbim_strings[] = {
+	&mbim_string_table,
+	NULL,
+};
+
+/*
+ * Here are options for the Datagram Pointer table (NDP) parser.
+ * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
+ * in NDP16 offsets and sizes fields are 1 16bit word wide,
+ * in NDP32 -- 2 16bit words wide. Also signatures are different.
+ * To make the parser code the same, put the differences in the structure,
+ * and switch pointers to the structures when the format is changed.
+ */
+
+struct ndp_parser_opts {
+	u32		nth_sign;
+	u32		ndp_sign;
+	unsigned	nth_size;
+	unsigned	ndp_size;
+	unsigned	ndplen_align;
+	/* sizes in u16 units */
+	unsigned	dgram_item_len; /* index or length */
+	unsigned	block_length;
+	unsigned	fp_index;
+	unsigned	reserved1;
+	unsigned	reserved2;
+	unsigned	next_fp_index;
+};
+
+#define INIT_NDP16_OPTS {				\
+	.nth_sign = USB_CDC_NCM_NTH16_SIGN,		\
+	.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	\
+	.nth_size = sizeof(struct usb_cdc_ncm_nth16),	\
+	.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	\
+	.ndplen_align = 4,				\
+	.dgram_item_len = 1,				\
+	.block_length = 1,				\
+	.fp_index = 1,					\
+	.reserved1 = 0,					\
+	.reserved2 = 0,					\
+	.next_fp_index = 1,				\
+}
+
+#define INIT_NDP32_OPTS {				\
+	.nth_sign = USB_CDC_NCM_NTH32_SIGN,		\
+	.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	\
+	.nth_size = sizeof(struct usb_cdc_ncm_nth32),	\
+	.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	\
+	.ndplen_align = 8,				\
+	.dgram_item_len = 2,				\
+	.block_length = 2,				\
+	.fp_index = 2,					\
+	.reserved1 = 1,					\
+	.reserved2 = 2,					\
+	.next_fp_index = 2,				\
+}
+
+static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+
+static inline int mbim_lock(atomic_t *excl)
+{
+	if (atomic_inc_return(excl) == 1) {
+		return 0;
+	} else {
+		atomic_dec(excl);
+		return -EBUSY;
+	}
+}
+
+static inline void mbim_unlock(atomic_t *excl)
+{
+	atomic_dec(excl);
+}
+
+static struct ctrl_pkt *mbim_alloc_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void mbim_free_ctrl_pkt(struct ctrl_pkt *pkt)
+{
+	if (pkt) {
+		kfree(pkt->buf);
+		kfree(pkt);
+	}
+}
+
+static struct usb_request *mbim_alloc_req(struct usb_ep *ep, int buffer_size)
+{
+	struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!req)
+		return NULL;
+
+	req->buf = kmalloc(buffer_size, GFP_KERNEL);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+	req->length = buffer_size;
+	return req;
+}
+
+void fmbim_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static void fmbim_ctrl_response_available(struct f_mbim *dev)
+{
+	struct usb_request		*req = dev->not_port.notify_req;
+	struct usb_cdc_notification	*event = NULL;
+	unsigned long			flags;
+	int				ret;
+
+	int notif_c = 0;
+
+	pr_info("dev:%p portno#%d\n", dev, dev->port_num);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		pr_info("dev:%p is not online\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (!req) {
+		pr_info("dev:%p req is NULL\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (!req->buf) {
+		pr_info("dev:%p req->buf is NULL\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	notif_c = atomic_inc_return(&dev->not_port.notify_count);
+	pr_info("atomic_inc_return[notif_c] = %d", notif_c);
+
+	event = req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ctrl_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_info("Call usb_ep_queue");
+
+	ret = usb_ep_queue(dev->not_port.notify,
+			   dev->not_port.notify_req, GFP_ATOMIC);
+	if (ret) {
+		atomic_dec(&dev->not_port.notify_count);
+		pr_err("ep enqueue error %d\n", ret);
+	}
+
+	pr_info("Succcessfull Exit");
+}
+
+static int
+fmbim_send_cpkt_response(struct f_mbim *gr, struct ctrl_pkt *cpkt)
+{
+	struct f_mbim	*dev = gr;
+	unsigned long	flags;
+
+	if (!gr || !cpkt) {
+		pr_err("Invalid cpkt, dev:%p cpkt:%p\n",
+				gr, cpkt);
+		return -ENODEV;
+	}
+
+	pr_info("dev:%p port_num#%d\n", dev, dev->port_num);
+
+	if (!atomic_read(&dev->online)) {
+		pr_info("dev:%p is not connected\n", dev);
+		mbim_free_ctrl_pkt(cpkt);
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	fmbim_ctrl_response_available(dev);
+
+	return 0;
+}
+
+/* ---------------------------- BAM INTERFACE ----------------------------- */
+
+static int mbim_bam_setup(int no_ports)
+{
+	int ret;
+
+	pr_info("no_ports:%d\n", no_ports);
+
+	ret = bam_data_setup(no_ports);
+	if (ret) {
+		pr_err("bam_data_setup failed err: %d\n", ret);
+		return ret;
+	}
+
+	pr_info("Initialized %d ports\n", no_ports);
+	return 0;
+}
+
+static int mbim_bam_connect(struct f_mbim *dev)
+{
+	int ret;
+
+	pr_info("dev:%p portno:%d\n", dev, dev->port_num);
+
+	ret = bam_data_connect(&dev->bam_port, dev->port_num, dev->port_num);
+	if (ret) {
+		pr_err("bam_data_setup failed: err:%d\n",
+				ret);
+		return ret;
+	} else {
+		pr_info("mbim bam connected\n");
+	}
+
+	return 0;
+}
+
+static int mbim_bam_disconnect(struct f_mbim *dev)
+{
+	pr_info("dev:%p port:%d. Do nothing.\n",
+			dev, dev->port_num);
+
+	/* bam_data_disconnect(&dev->bam_port, dev->port_num); */
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------*/
+
+static inline void mbim_reset_values(struct f_mbim *mbim)
+{
+	mbim->parser_opts = &ndp16_opts;
+
+	mbim->ntb_input_size = NTB_DEFAULT_IN_SIZE;
+
+	atomic_set(&mbim->not_port.notify_count, 0);
+	atomic_set(&mbim->online, 0);
+}
+
+static void mbim_reset_function_queue(struct f_mbim *dev)
+{
+	struct ctrl_pkt	*cpkt = NULL;
+
+	pr_debug("Queue empty packet for QBI");
+
+	spin_lock(&dev->lock);
+	if (!dev->is_open) {
+		pr_err("%s: mbim file handler %p is not open", __func__, dev);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(0, GFP_ATOMIC);
+	if (!cpkt) {
+		pr_err("%s: Unable to allocate reset function pkt\n", __func__);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	list_add_tail(&cpkt->list, &dev->cpkt_req_q);
+	spin_unlock(&dev->lock);
+
+	pr_debug("%s: Wake up read queue", __func__);
+	wake_up(&dev->read_wq);
+}
+
+static void fmbim_reset_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim		*dev = req->context;
+
+	mbim_reset_function_queue(dev);
+}
+
+static void mbim_clear_queues(struct f_mbim *mbim)
+{
+	struct ctrl_pkt	*cpkt = NULL;
+	struct list_head *act, *tmp;
+
+	spin_lock(&mbim->lock);
+	list_for_each_safe(act, tmp, &mbim->cpkt_req_q) {
+		cpkt = list_entry(act, struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		mbim_free_ctrl_pkt(cpkt);
+	}
+	list_for_each_safe(act, tmp, &mbim->cpkt_resp_q) {
+		cpkt = list_entry(act, struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		mbim_free_ctrl_pkt(cpkt);
+	}
+	spin_unlock(&mbim->lock);
+}
+
+/*
+ * Context: mbim->lock held
+ */
+static void mbim_do_notify(struct f_mbim *mbim)
+{
+	struct usb_request		*req = mbim->not_port.notify_req;
+	struct usb_cdc_notification	*event;
+	struct usb_composite_dev	*cdev = mbim->cdev;
+	__le32				*data;
+	int				status;
+
+	pr_info("notify_state: %d", mbim->not_port.notify_state);
+
+	if (!req)
+		return;
+
+	event = req->buf;
+
+	switch (mbim->not_port.notify_state) {
+
+	case NCM_NOTIFY_NONE:
+		return;
+
+	case NCM_NOTIFY_CONNECT:
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		if (mbim->is_open)
+			event->wValue = cpu_to_le16(1);
+		else
+			event->wValue = cpu_to_le16(0);
+		event->wLength = 0;
+		req->length = sizeof *event;
+
+		pr_info("notify connect %s\n",
+			mbim->is_open ? "true" : "false");
+		mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+		break;
+
+	case NCM_NOTIFY_SPEED:
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(8);
+		req->length = NCM_STATUS_BYTECOUNT;
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		data = req->buf + sizeof *event;
+		data[0] = cpu_to_le32(mbim_bitrate(cdev->gadget));
+		data[1] = data[0];
+
+		pr_info("notify speed %d\n",
+			mbim_bitrate(cdev->gadget));
+		mbim->not_port.notify_state = NCM_NOTIFY_CONNECT;
+		break;
+	}
+	event->bmRequestType = 0xA1;
+	event->wIndex = cpu_to_le16(mbim->ctrl_id);
+
+	mbim->not_port.notify_req = NULL;
+	/*
+	 * In double buffering if there is a space in FIFO,
+	 * completion callback can be called right after the call,
+	 * so unlocking
+	 */
+	spin_unlock(&mbim->lock);
+	status = usb_ep_queue(mbim->not_port.notify, req, GFP_ATOMIC);
+	spin_lock(&mbim->lock);
+	if (status < 0) {
+		mbim->not_port.notify_req = req;
+		atomic_dec(&mbim->not_port.notify_count);
+		pr_err("usb_ep_queue failed, err: %d", status);
+	}
+}
+
+/*
+ * Context: mbim->lock held
+ */
+static void mbim_notify(struct f_mbim *mbim)
+{
+	/*
+	 * If mbim_notify() is called before the second (CONNECT)
+	 * notification is sent, then it will reset to send the SPEED
+	 * notificaion again (and again, and again), but it's not a problem
+	 */
+	pr_info("dev:%p\n", mbim);
+
+	mbim->not_port.notify_state = NCM_NOTIFY_SPEED;
+	mbim_do_notify(mbim);
+}
+
+static void mbim_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim			*mbim = req->context;
+	struct usb_cdc_notification	*event = req->buf;
+
+	int notif_c = 0;
+
+	pr_info("dev:%p\n", mbim);
+
+	spin_lock(&mbim->lock);
+	switch (req->status) {
+	case 0:
+		pr_info("Notification %02x sent\n",
+			event->bNotificationType);
+
+		notif_c = atomic_dec_return(&mbim->not_port.notify_count);
+
+		if (notif_c != 0) {
+			pr_info("Continue to mbim_do_notify()");
+			break;
+		} else {
+			pr_info("notify_count decreased to 0. Do not notify");
+			spin_unlock(&mbim->lock);
+			return;
+		}
+
+		break;
+
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+		atomic_set(&mbim->not_port.notify_count, 0);
+		pr_info("ESHUTDOWN/ECONNRESET, connection gone");
+		spin_unlock(&mbim->lock);
+		mbim_clear_queues(mbim);
+		mbim_reset_function_queue(mbim);
+		break;
+	default:
+		pr_err("Unknown event %02x --> %d\n",
+			event->bNotificationType, req->status);
+		break;
+	}
+
+	mbim->not_port.notify_req = req;
+	mbim_do_notify(mbim);
+
+	spin_unlock(&mbim->lock);
+
+	pr_info("dev:%p Exit\n", mbim);
+}
+
+static void mbim_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* now for SET_NTB_INPUT_SIZE only */
+	unsigned		in_size = 0;
+	struct usb_function	*f = req->context;
+	struct f_mbim		*mbim = func_to_mbim(f);
+	struct mbim_ntb_input_size *ntb = NULL;
+
+	pr_info("dev:%p\n", mbim);
+
+	req->context = NULL;
+	if (req->status || req->actual != req->length) {
+		pr_err("Bad control-OUT transfer\n");
+		goto invalid;
+	}
+
+	if (req->length == 4) {
+		in_size = get_unaligned_le32(req->buf);
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+			pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
+			goto invalid;
+		}
+	} else if (req->length == 8) {
+		ntb = (struct mbim_ntb_input_size *)req->buf;
+		in_size = get_unaligned_le32(&(ntb->ntb_input_size));
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+			pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
+			goto invalid;
+		}
+		mbim->ntb_max_datagrams =
+			get_unaligned_le16(&(ntb->ntb_max_datagrams));
+	} else {
+		pr_err("Illegal NTB length %d\n", in_size);
+		goto invalid;
+	}
+
+	pr_info("Set NTB INPUT SIZE %d\n", in_size);
+
+	mbim->ntb_input_size = in_size;
+	return;
+
+invalid:
+	usb_ep_set_halt(ep);
+
+	pr_err("dev:%p Failed\n", mbim);
+
+	return;
+}
+
+static void
+fmbim_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim		*dev = req->context;
+	struct ctrl_pkt		*cpkt = NULL;
+	int			len = req->actual;
+
+	if (!dev) {
+		pr_err("mbim dev is null\n");
+		return;
+	}
+
+	if (req->status < 0) {
+		pr_err("mbim command error %d\n", req->status);
+		return;
+	}
+
+	pr_info("dev:%p port#%d\n", dev, dev->port_num);
+
+	spin_lock(&dev->lock);
+	if (!dev->is_open) {
+		pr_err("mbim file handler %p is not open", dev);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (!cpkt) {
+		pr_err("Unable to allocate ctrl pkt\n");
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	pr_info("Add to cpkt_req_q packet with len = %d\n", len);
+	memcpy(cpkt->buf, req->buf, len);
+	list_add_tail(&cpkt->list, &dev->cpkt_req_q);
+	spin_unlock(&dev->lock);
+
+	/* wakeup read thread */
+	pr_info("Wake up read queue");
+	wake_up(&dev->read_wq);
+
+	return;
+}
+
+static int
+mbim_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_mbim			*mbim = func_to_mbim(f);
+	struct usb_composite_dev	*cdev = mbim->cdev;
+	struct usb_request		*req = cdev->req;
+	struct ctrl_pkt		*cpkt = NULL;
+	int	value = -EOPNOTSUPP;
+	u16	w_index = le16_to_cpu(ctrl->wIndex);
+	u16	w_value = le16_to_cpu(ctrl->wValue);
+	u16	w_length = le16_to_cpu(ctrl->wLength);
+
+	/*
+	 * composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+
+	if (!atomic_read(&mbim->online)) {
+		pr_info("usb cable is not connected\n");
+		return -ENOTCONN;
+	}
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_RESET_FUNCTION:
+
+		pr_info("USB_CDC_RESET_FUNCTION");
+		value = 0;
+		req->complete = fmbim_reset_cmd_complete;
+		req->context = mbim;
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+
+		pr_info("USB_CDC_SEND_ENCAPSULATED_COMMAND");
+
+		if (w_length > req->length) {
+			pr_err("w_length > req->length: %d > %d",
+			w_length, req->length);
+		}
+		value = w_length;
+		req->complete = fmbim_cmd_complete;
+		req->context = mbim;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+
+		pr_info("USB_CDC_GET_ENCAPSULATED_RESPONSE");
+
+		if (w_value) {
+			pr_err("w_length > 0: %d", w_length);
+			break;
+		}
+
+		pr_info("req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+
+		spin_lock(&mbim->lock);
+		if (list_empty(&mbim->cpkt_resp_q)) {
+			pr_err("ctrl resp queue empty\n");
+			spin_unlock(&mbim->lock);
+			break;
+		}
+
+		cpkt = list_first_entry(&mbim->cpkt_resp_q,
+					struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		spin_unlock(&mbim->lock);
+
+		value = min_t(unsigned, w_length, cpkt->len);
+		memcpy(req->buf, cpkt->buf, value);
+		mbim_free_ctrl_pkt(cpkt);
+
+		pr_info("copied encapsulated_response %d bytes",
+			value);
+
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_PARAMETERS:
+
+		pr_info("USB_CDC_GET_NTB_PARAMETERS");
+
+		if (w_length == 0 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		value = w_length > sizeof ntb_parameters ?
+			sizeof ntb_parameters : w_length;
+		memcpy(req->buf, &ntb_parameters, value);
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_INPUT_SIZE:
+
+		pr_info("USB_CDC_GET_NTB_INPUT_SIZE");
+
+		if (w_length < 4 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		put_unaligned_le32(mbim->ntb_input_size, req->buf);
+		value = 4;
+		pr_info("Reply to host INPUT SIZE %d\n",
+		     mbim->ntb_input_size);
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_INPUT_SIZE:
+
+		pr_info("USB_CDC_SET_NTB_INPUT_SIZE");
+
+		if (w_length != 4 && w_length != 8) {
+			pr_err("wrong NTB length %d", w_length);
+			break;
+		}
+
+		if (w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		req->complete = mbim_ep0out_complete;
+		req->length = w_length;
+		req->context = f;
+
+		value = req->length;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_FORMAT:
+	{
+		uint16_t format;
+
+		pr_info("USB_CDC_GET_NTB_FORMAT");
+
+		if (w_length < 2 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		format = (mbim->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+		put_unaligned_le16(format, req->buf);
+		value = 2;
+		pr_info("NTB FORMAT: sending %d\n", format);
+		break;
+	}
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_FORMAT:
+	{
+		pr_info("USB_CDC_SET_NTB_FORMAT");
+
+		if (w_length != 0 || w_index != mbim->ctrl_id)
+			break;
+		switch (w_value) {
+		case 0x0000:
+			mbim->parser_opts = &ndp16_opts;
+			pr_info("NCM16 selected\n");
+			break;
+		case 0x0001:
+			mbim->parser_opts = &ndp32_opts;
+			pr_info("NCM32 selected\n");
+			break;
+		default:
+			break;
+		}
+		value = 0;
+		break;
+	}
+
+	/* optional in mbim descriptor: */
+	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
+	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
+
+	default:
+	pr_err("invalid control req: %02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	 /* respond with data transfer or status phase? */
+	if (value >= 0) {
+		pr_info("control request: %02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (value < w_length);
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			pr_err("queueing req failed: %02x.%02x, err %d\n",
+				ctrl->bRequestType,
+			       ctrl->bRequest, value);
+		}
+	} else {
+		pr_err("ctrl req err %d: %02x.%02x v%04x i%04x l%d\n",
+			value, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_mbim		*mbim = func_to_mbim(f);
+	struct usb_composite_dev *cdev = mbim->cdev;
+	int ret = 0;
+
+	/* Control interface has only altsetting 0 */
+	if (intf == mbim->ctrl_id) {
+
+		pr_info("CONTROL_INTERFACE");
+
+		if (alt != 0)
+			goto fail;
+
+		if (mbim->not_port.notify->driver_data) {
+			pr_info("reset mbim control %d\n", intf);
+			usb_ep_disable(mbim->not_port.notify);
+		}
+
+		ret = config_ep_by_speed(cdev->gadget, f,
+					mbim->not_port.notify);
+		if (ret) {
+			mbim->not_port.notify->desc = NULL;
+			pr_err("Failed configuring notify ep %s: err %d\n",
+				mbim->not_port.notify->name, ret);
+			return ret;
+		}
+
+		ret = usb_ep_enable(mbim->not_port.notify);
+		if (ret) {
+			pr_err("usb ep#%s enable failed, err#%d\n",
+				mbim->not_port.notify->name, ret);
+			return ret;
+		}
+		mbim->not_port.notify->driver_data = mbim;
+
+	/* Data interface has two altsettings, 0 and 1 */
+	} else if (intf == mbim->data_id) {
+
+		pr_info("DATA_INTERFACE");
+
+		if (alt > 1)
+			goto fail;
+
+		if (mbim->bam_port.in->driver_data) {
+			pr_info("reset mbim\n");
+			mbim_reset_values(mbim);
+			mbim_bam_disconnect(mbim);
+		}
+
+		/*
+		 * CDC Network only sends data in non-default altsettings.
+		 * Changing altsettings resets filters, statistics, etc.
+		 */
+		if (alt == 1) {
+			pr_info("Alt set 1, initialize ports");
+
+			if (!mbim->bam_port.in->desc) {
+
+				pr_info("Choose endpoints");
+
+				ret = config_ep_by_speed(cdev->gadget, f,
+							mbim->bam_port.in);
+				if (ret) {
+					mbim->bam_port.in->desc = NULL;
+					pr_err("IN ep %s failed: %d\n",
+					mbim->bam_port.in->name, ret);
+					return ret;
+				}
+
+				pr_info("Set mbim port in_desc = 0x%p",
+					mbim->bam_port.in->desc);
+
+				ret = config_ep_by_speed(cdev->gadget, f,
+							mbim->bam_port.out);
+				if (ret) {
+					mbim->bam_port.out->desc = NULL;
+					pr_err("OUT ep %s failed: %d\n",
+					mbim->bam_port.out->name, ret);
+					return ret;
+				}
+
+				pr_info("Set mbim port out_desc = 0x%p",
+					mbim->bam_port.out->desc);
+			} else {
+				pr_info("PORTS already SET");
+			}
+
+			pr_info("Activate mbim\n");
+			mbim_bam_connect(mbim);
+		}
+
+		spin_lock(&mbim->lock);
+		mbim_notify(mbim);
+		spin_unlock(&mbim->lock);
+	} else {
+		goto fail;
+	}
+
+	atomic_set(&mbim->online, 1);
+
+	pr_info("SET DEVICE ONLINE");
+
+	/* wakeup file threads */
+	wake_up(&mbim->read_wq);
+	wake_up(&mbim->write_wq);
+
+	return 0;
+
+fail:
+	pr_err("ERROR: Illegal Interface");
+	return -EINVAL;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * this MBIM function *MUST* implement a get_alt() method.
+ */
+static int mbim_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	if (intf == mbim->ctrl_id)
+		return 0;
+	return mbim->bam_port.in->driver_data ? 1 : 0;
+}
+
+static void mbim_disable(struct usb_function *f)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	pr_info("SET DEVICE OFFLINE");
+	atomic_set(&mbim->online, 0);
+
+	mbim_clear_queues(mbim);
+	mbim_reset_function_queue(mbim);
+
+	mbim_bam_disconnect(mbim);
+
+	if (mbim->not_port.notify->driver_data) {
+		usb_ep_disable(mbim->not_port.notify);
+		mbim->not_port.notify->driver_data = NULL;
+	}
+
+	pr_info("mbim deactivated\n");
+}
+
+/*---------------------- function driver setup/binding ---------------------*/
+
+static int
+mbim_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_mbim		*mbim = func_to_mbim(f);
+	int			status;
+	struct usb_ep		*ep;
+
+	pr_info("Enter");
+
+	mbim->cdev = cdev;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	mbim->ctrl_id = status;
+	mbim_iad_desc.bFirstInterface = status;
+
+	mbim_control_intf.bInterfaceNumber = status;
+	mbim_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	mbim->data_id = status;
+
+	mbim_data_nop_intf.bInterfaceNumber = status;
+	mbim_data_intf.bInterfaceNumber = status;
+	mbim_union_desc.bSlaveInterface0 = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_in_desc);
+	if (!ep) {
+		pr_err("usb epin autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb epin autoconfig succeeded\n");
+	ep->driver_data = cdev;	/* claim */
+	mbim->bam_port.in = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_out_desc);
+	if (!ep) {
+		pr_err("usb epout autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb epout autoconfig succeeded\n");
+	ep->driver_data = cdev;	/* claim */
+	mbim->bam_port.out = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_notify_desc);
+	if (!ep) {
+		pr_err("usb notify ep autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb notify ep autoconfig succeeded\n");
+	mbim->not_port.notify = ep;
+	ep->driver_data = cdev;	/* claim */
+
+	status = -ENOMEM;
+
+	/* allocate notification request and buffer */
+	mbim->not_port.notify_req = mbim_alloc_req(ep, NCM_STATUS_BYTECOUNT);
+	if (!mbim->not_port.notify_req) {
+		pr_info("failed to allocate notify request\n");
+		goto fail;
+	}
+	pr_info("allocated notify ep request & request buffer\n");
+
+	mbim->not_port.notify_req->context = mbim;
+	mbim->not_port.notify_req->complete = mbim_notify_complete;
+
+	/* copy descriptors, and track endpoint copies */
+	f->descriptors = usb_copy_descriptors(mbim_fs_function);
+	if (!f->descriptors)
+		goto fail;
+
+	/*
+	 * support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_mbim_in_desc.bEndpointAddress =
+				fs_mbim_in_desc.bEndpointAddress;
+		hs_mbim_out_desc.bEndpointAddress =
+				fs_mbim_out_desc.bEndpointAddress;
+		hs_mbim_notify_desc.bEndpointAddress =
+				fs_mbim_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(mbim_hs_function);
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	pr_info("mbim(%d): %s speed IN/%s OUT/%s NOTIFY/%s\n",
+			mbim->port_num,
+			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+			mbim->bam_port.in->name, mbim->bam_port.out->name,
+			mbim->not_port.notify->name);
+
+	return 0;
+
+fail:
+	pr_err("%s failed to bind, err %d\n", f->name, status);
+
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+
+	if (mbim->not_port.notify_req) {
+		kfree(mbim->not_port.notify_req->buf);
+		usb_ep_free_request(mbim->not_port.notify,
+				    mbim->not_port.notify_req);
+	}
+
+	/* we might as well release our claims on endpoints */
+	if (mbim->not_port.notify)
+		mbim->not_port.notify->driver_data = NULL;
+	if (mbim->bam_port.out)
+		mbim->bam_port.out->driver_data = NULL;
+	if (mbim->bam_port.in)
+		mbim->bam_port.in->driver_data = NULL;
+
+	return status;
+}
+
+static void mbim_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+	kfree(mbim->not_port.notify_req->buf);
+	usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);
+}
+
+/**
+ * mbim_bind_config - add MBIM link to a configuration
+ * @c: the configuration to support the network link
+ * Context: single threaded during gadget setup
+ * Returns zero on success, else negative errno.
+ */
+int mbim_bind_config(struct usb_configuration *c, unsigned portno)
+{
+	struct f_mbim	*mbim = NULL;
+	int status = 0;
+
+	pr_info("port number %u", portno);
+
+	if (portno >= nr_mbim_ports) {
+		pr_err("Can not add port %u. Max ports = %d",
+		       portno, nr_mbim_ports);
+		return -ENODEV;
+	}
+
+	status = mbim_bam_setup(nr_mbim_ports);
+	if (status) {
+		pr_err("bam setup failed");
+		return status;
+	}
+
+	/* maybe allocate device-global string IDs */
+	if (mbim_string_defs[0].id == 0) {
+
+		/* control interface label */
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		mbim_string_defs[STRING_CTRL_IDX].id = status;
+		mbim_control_intf.iInterface = status;
+
+		/* data interface label */
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		mbim_string_defs[STRING_DATA_IDX].id = status;
+		mbim_data_nop_intf.iInterface = status;
+		mbim_data_intf.iInterface = status;
+	}
+
+	/* allocate and initialize one new instance */
+	mbim = mbim_ports[0].port;
+	if (!mbim) {
+		pr_info("mbim struct not allocated");
+		return -ENOMEM;
+	}
+
+	mbim->cdev = c->cdev;
+
+	spin_lock_init(&mbim->lock);
+
+	mbim_reset_values(mbim);
+
+	mbim->function.name = "usb_mbim";
+	mbim->function.strings = mbim_strings;
+	mbim->function.bind = mbim_bind;
+	mbim->function.unbind = mbim_unbind;
+	mbim->function.set_alt = mbim_set_alt;
+	mbim->function.get_alt = mbim_get_alt;
+	mbim->function.setup = mbim_setup;
+	mbim->function.disable = mbim_disable;
+
+	INIT_LIST_HEAD(&mbim->cpkt_req_q);
+	INIT_LIST_HEAD(&mbim->cpkt_resp_q);
+
+	status = usb_add_function(c, &mbim->function);
+
+	pr_info("Exit status %d", status);
+
+	return status;
+}
+
+/* ------------ MBIM DRIVER File Operations API for USER SPACE ------------ */
+
+static ssize_t
+mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
+{
+	struct f_mbim *dev = fp->private_data;
+	struct ctrl_pkt *cpkt = NULL;
+	int ret = 0;
+
+	pr_debug("Enter(%d)\n", count);
+
+	if (!dev) {
+		pr_err("Received NULL mbim pointer\n");
+		return -ENODEV;
+	}
+
+	if (count > MBIM_BULK_BUFFER_SIZE) {
+		pr_err("Buffer size is too big %d, should be at most %d\n",
+			count, MBIM_BULK_BUFFER_SIZE);
+		return -EINVAL;
+	}
+
+	if (mbim_lock(&dev->read_excl)) {
+		pr_err("Previous reading is not finished yet\n");
+		return -EBUSY;
+	}
+
+	/* block until mbim online */
+	while (!(atomic_read(&dev->online) || atomic_read(&dev->error))) {
+		pr_err("USB cable not connected. Wait.\n");
+		ret = wait_event_interruptible(dev->read_wq,
+			(atomic_read(&dev->online) ||
+			atomic_read(&dev->error)));
+		if (ret < 0) {
+			mbim_unlock(&dev->read_excl);
+			return 0;
+		}
+	}
+
+	if (atomic_read(&dev->error)) {
+		mbim_unlock(&dev->read_excl);
+		return -EIO;
+	}
+
+	while (list_empty(&dev->cpkt_req_q)) {
+		pr_err("Requests list is empty. Wait.\n");
+		ret = wait_event_interruptible(dev->read_wq,
+			!list_empty(&dev->cpkt_req_q));
+		if (ret < 0) {
+			pr_err("Waiting failed\n");
+			mbim_unlock(&dev->read_excl);
+			return 0;
+		}
+		pr_debug("Received request packet\n");
+	}
+
+	cpkt = list_first_entry(&dev->cpkt_req_q, struct ctrl_pkt,
+							list);
+	if (cpkt->len > count) {
+		mbim_unlock(&dev->read_excl);
+		pr_err("cpkt size too big:%d > buf size:%d\n",
+				cpkt->len, count);
+		return -ENOMEM;
+	}
+
+	pr_debug("cpkt size:%d\n", cpkt->len);
+
+	list_del(&cpkt->list);
+	mbim_unlock(&dev->read_excl);
+
+	ret = copy_to_user(buf, cpkt->buf, cpkt->len);
+	if (ret) {
+		pr_err("copy_to_user failed: err %d\n", ret);
+		ret = 0;
+	} else {
+		pr_debug("copied %d bytes to user\n", cpkt->len);
+		ret = cpkt->len;
+	}
+
+	mbim_free_ctrl_pkt(cpkt);
+
+	return ret;
+}
+
+static ssize_t
+mbim_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos)
+{
+	struct f_mbim *dev = fp->private_data;
+	struct ctrl_pkt *cpkt = NULL;
+	int ret = 0;
+
+	pr_debug("Enter(%d)", count);
+
+	if (!dev) {
+		pr_err("Received NULL mbim pointer\n");
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("zero length ctrl pkt\n");
+		return -ENODEV;
+	}
+
+	if (count > MAX_CTRL_PKT_SIZE) {
+		pr_err("given pkt size too big:%d > max_pkt_size:%d\n",
+				count, MAX_CTRL_PKT_SIZE);
+		return -ENOMEM;
+	}
+
+	if (mbim_lock(&dev->write_excl)) {
+		pr_err("Previous writing not finished yet\n");
+		return -EBUSY;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("USB cable not connected\n");
+		mbim_unlock(&dev->write_excl);
+		return -EPIPE;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(count, GFP_KERNEL);
+	if (!cpkt) {
+		pr_err("failed to allocate ctrl pkt\n");
+		mbim_unlock(&dev->write_excl);
+		return -ENOMEM;
+	}
+
+	ret = copy_from_user(cpkt->buf, buf, count);
+	if (ret) {
+		pr_err("copy_from_user failed err:%d\n", ret);
+		mbim_free_ctrl_pkt(cpkt);
+		mbim_unlock(&dev->write_excl);
+		return 0;
+	}
+
+	fmbim_send_cpkt_response(dev, cpkt);
+
+	mbim_unlock(&dev->write_excl);
+
+	pr_debug("Exit(%d)", count);
+
+	return count;
+}
+
+static int mbim_open(struct inode *ip, struct file *fp)
+{
+	pr_info("Open mbim driver\n");
+
+	while (!_mbim_dev) {
+		pr_err("mbim_dev not created yet\n");
+		return -ENODEV;
+	}
+
+	if (mbim_lock(&_mbim_dev->open_excl)) {
+		pr_err("Already opened\n");
+		return -EBUSY;
+	}
+
+	pr_info("Lock mbim_dev->open_excl for open\n");
+
+	if (!atomic_read(&_mbim_dev->online))
+		pr_err("USB cable not connected\n");
+
+	fp->private_data = _mbim_dev;
+
+	atomic_set(&_mbim_dev->error, 0);
+
+	spin_lock(&_mbim_dev->lock);
+	_mbim_dev->is_open = true;
+	mbim_notify(_mbim_dev);
+	spin_unlock(&_mbim_dev->lock);
+
+	pr_info("Exit, mbim file opened\n");
+
+	return 0;
+}
+
+static int mbim_release(struct inode *ip, struct file *fp)
+{
+	struct f_mbim *mbim = fp->private_data;
+
+	pr_info("Close mbim file");
+
+	spin_lock(&mbim->lock);
+	mbim->is_open = false;
+	mbim_notify(mbim);
+	spin_unlock(&mbim->lock);
+
+	mbim_unlock(&_mbim_dev->open_excl);
+
+	return 0;
+}
+
+static long mbim_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
+{
+	struct f_mbim *mbim = fp->private_data;
+	int ret = 0;
+
+	pr_info("Received command %d", cmd);
+
+	if (mbim_lock(&mbim->ioctl_excl))
+		return -EBUSY;
+
+	switch (cmd) {
+	case MBIM_GET_NTB_SIZE:
+		ret = copy_to_user((void __user *)arg,
+			&mbim->ntb_input_size, sizeof(mbim->ntb_input_size));
+		if (ret) {
+			pr_err("copying to user space failed");
+			ret = -EFAULT;
+		}
+		pr_info("Sent NTB size %d", mbim->ntb_input_size);
+		break;
+	case MBIM_GET_DATAGRAM_COUNT:
+		ret = copy_to_user((void __user *)arg,
+			&mbim->ntb_max_datagrams,
+			sizeof(mbim->ntb_max_datagrams));
+		if (ret) {
+			pr_err("copying to user space failed");
+			ret = -EFAULT;
+		}
+		pr_info("Sent NTB datagrams count %d",
+			mbim->ntb_max_datagrams);
+		break;
+	default:
+		pr_err("wrong parameter");
+		ret = -EINVAL;
+	}
+
+	mbim_unlock(&mbim->ioctl_excl);
+
+	return ret;
+}
+
+/* file operations for MBIM device /dev/android_mbim */
+static const struct file_operations mbim_fops = {
+	.owner = THIS_MODULE,
+	.open = mbim_open,
+	.release = mbim_release,
+	.read = mbim_read,
+	.write = mbim_write,
+	.unlocked_ioctl	= mbim_ioctl,
+};
+
+static struct miscdevice mbim_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "android_mbim",
+	.fops = &mbim_fops,
+};
+
+static int mbim_init(int instances)
+{
+	int i;
+	struct f_mbim *dev = NULL;
+	int ret;
+
+	pr_info("initialize %d instances\n", instances);
+
+	if (instances > NR_MBIM_PORTS) {
+		pr_err("Max-%d instances supported\n", NR_MBIM_PORTS);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < instances; i++) {
+		dev = kzalloc(sizeof(struct f_mbim), GFP_KERNEL);
+		if (!dev) {
+			pr_err("Failed to allocate mbim dev\n");
+			ret = -ENOMEM;
+			goto fail_probe;
+		}
+
+		dev->port_num = i;
+		spin_lock_init(&dev->lock);
+		INIT_LIST_HEAD(&dev->cpkt_req_q);
+		INIT_LIST_HEAD(&dev->cpkt_resp_q);
+
+		mbim_ports[i].port = dev;
+		mbim_ports[i].port_num = i;
+
+		init_waitqueue_head(&dev->read_wq);
+		init_waitqueue_head(&dev->write_wq);
+
+		atomic_set(&dev->open_excl, 0);
+		atomic_set(&dev->ioctl_excl, 0);
+		atomic_set(&dev->read_excl, 0);
+		atomic_set(&dev->write_excl, 0);
+
+		nr_mbim_ports++;
+
+	}
+
+	_mbim_dev = dev;
+	ret = misc_register(&mbim_device);
+	if (ret) {
+		pr_err("mbim driver failed to register");
+		goto fail_probe;
+	}
+
+	pr_info("Initialized %d ports\n", nr_mbim_ports);
+
+	return ret;
+
+fail_probe:
+	pr_err("Failed");
+	for (i = 0; i < nr_mbim_ports; i++) {
+		kfree(mbim_ports[i].port);
+		mbim_ports[i].port = NULL;
+	}
+
+	return ret;
+}
+
+static void fmbim_cleanup(void)
+{
+	int i = 0;
+
+	pr_info("Enter");
+
+	for (i = 0; i < nr_mbim_ports; i++) {
+		kfree(mbim_ports[i].port);
+		mbim_ports[i].port = NULL;
+	}
+	nr_mbim_ports = 0;
+
+	misc_deregister(&mbim_device);
+
+	_mbim_dev = NULL;
+}
+
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 1638977..0394b0b 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -410,15 +410,6 @@
 	ep->driver_data = dev;		/* claim the endpoint */
 	dev->ep_out = ep;
 
-	ep = usb_ep_autoconfig(cdev->gadget, out_desc);
-	if (!ep) {
-		DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
-		return -ENODEV;
-	}
-	DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
-	ep->driver_data = dev;		/* claim the endpoint */
-	dev->ep_out = ep;
-
 	ep = usb_ep_autoconfig(cdev->gadget, intr_desc);
 	if (!ep) {
 		DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n");
@@ -504,7 +495,17 @@
 	}
 
 	/* wait for a request to complete */
-	ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+	ret = wait_event_interruptible(dev->read_wq,
+				dev->rx_done || dev->state != STATE_BUSY);
+	if (dev->state == STATE_CANCELED) {
+		r = -ECANCELED;
+		if (!dev->rx_done)
+			usb_ep_dequeue(dev->ep_out, req);
+		spin_lock_irq(&dev->lock);
+		dev->state = STATE_CANCELED;
+		spin_unlock_irq(&dev->lock);
+		goto done;
+	}
 	if (ret < 0) {
 		r = ret;
 		usb_ep_dequeue(dev->ep_out, req);
@@ -709,7 +710,8 @@
 		ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
 		if (ret < 0) {
 			DBG(cdev, "send_file_work: xfer error %d\n", ret);
-			dev->state = STATE_ERROR;
+			if (dev->state != STATE_OFFLINE)
+				dev->state = STATE_ERROR;
 			r = -EIO;
 			break;
 		}
@@ -762,7 +764,8 @@
 			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
 			if (ret < 0) {
 				r = -EIO;
-				dev->state = STATE_ERROR;
+				if (dev->state != STATE_OFFLINE)
+					dev->state = STATE_ERROR;
 				break;
 			}
 		}
@@ -774,7 +777,8 @@
 			DBG(cdev, "vfs_write %d\n", ret);
 			if (ret != write_req->actual) {
 				r = -EIO;
-				dev->state = STATE_ERROR;
+				if (dev->state != STATE_OFFLINE)
+					dev->state = STATE_ERROR;
 				break;
 			}
 			write_req = NULL;
@@ -1141,27 +1145,35 @@
 	DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt);
 
 	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
-	if (ret)
-		return ret;
-
-	ret = usb_ep_enable(dev->ep_in);
-	if (ret)
-		return ret;
-
-	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
-	if (ret)
-		return ret;
-
-	ret = usb_ep_enable(dev->ep_out);
 	if (ret) {
-		usb_ep_disable(dev->ep_in);
+		dev->ep_in->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_in->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->ep_in);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_in->name, ret);
 		return ret;
 	}
 
-	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr);
-	if (ret)
+	ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
+	if (ret) {
+		dev->ep_out->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
 		return ret;
-
+	}
+	ret = usb_ep_enable(dev->ep_out);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			dev->ep_out->name, ret);
+		usb_ep_disable(dev->ep_in);
+		return ret;
+	}
+	dev->ep_intr->desc = &mtp_intr_desc;
 	ret = usb_ep_enable(dev->ep_intr);
 	if (ret) {
 		usb_ep_disable(dev->ep_out);
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
new file mode 100644
index 0000000..414a7b9
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/usb/android_composite.h>
+#include <linux/spinlock.h>
+
+#include <mach/usb_gadget_xport.h>
+
+#include "u_rmnet.h"
+#include "gadget_chips.h"
+
+#define RMNET_NOTIFY_INTERVAL	5
+#define RMNET_MAX_NOTIFY_SIZE	sizeof(struct usb_cdc_notification)
+
+
+#define ACM_CTRL_DTR	(1 << 0)
+
+/* TODO: use separate structures for data and
+ * control paths
+ */
+struct f_rmnet {
+	struct grmnet			port;
+	int				ifc_id;
+	u8				port_num;
+	atomic_t			online;
+	atomic_t			ctrl_online;
+	struct usb_composite_dev	*cdev;
+
+	spinlock_t			lock;
+
+	/* usb eps*/
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+
+	/* control info */
+	struct list_head		cpkt_resp_q;
+	atomic_t			notify_count;
+	unsigned long			cpkts_len;
+};
+
+#define NR_RMNET_PORTS	3
+static unsigned int nr_rmnet_ports;
+static unsigned int no_ctrl_smd_ports;
+static unsigned int no_ctrl_hsic_ports;
+static unsigned int no_ctrl_hsuart_ports;
+static unsigned int no_data_bam_ports;
+static unsigned int no_data_bam2bam_ports;
+static unsigned int no_data_hsic_ports;
+static unsigned int no_data_hsuart_ports;
+static struct rmnet_ports {
+	enum transport_type		data_xport;
+	enum transport_type		ctrl_xport;
+	unsigned			data_xport_num;
+	unsigned			ctrl_xport_num;
+	unsigned			port_num;
+	struct f_rmnet			*port;
+} rmnet_ports[NR_RMNET_PORTS];
+
+static struct usb_interface_descriptor rmnet_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
+	.bInterval =		1 << RMNET_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_fs_in_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
+	.bInterval =		RMNET_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_string_defs[] = {
+	[0].s = "RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rmnet_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_strings[] = {
+	&rmnet_string_table,
+	NULL,
+};
+
+static void frmnet_ctrl_response_available(struct f_rmnet *dev);
+
+/* ------- misc functions --------------------*/
+
+static inline struct f_rmnet *func_to_rmnet(struct usb_function *f)
+{
+	return container_of(f, struct f_rmnet, port.func);
+}
+
+static inline struct f_rmnet *port_to_rmnet(struct grmnet *r)
+{
+	return container_of(r, struct f_rmnet, port);
+}
+
+static struct usb_request *
+frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	req->length = len;
+
+	return req;
+}
+
+void frmnet_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static struct rmnet_ctrl_pkt *rmnet_alloc_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct rmnet_ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
+{
+	kfree(pkt->buf);
+	kfree(pkt);
+}
+
+/* -------------------------------------------*/
+
+static int rmnet_gport_setup(void)
+{
+	int	ret;
+	int	port_idx;
+	int	i;
+
+	pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u data hsuart ports: %u"
+		" smd ports: %u ctrl hsic ports: %u ctrl hsuart ports: %u"
+	" nr_rmnet_ports: %u\n",
+		__func__, no_data_bam_ports, no_data_bam2bam_ports,
+		no_data_hsic_ports, no_data_hsuart_ports, no_ctrl_smd_ports,
+		no_ctrl_hsic_ports, no_ctrl_hsuart_ports, nr_rmnet_ports);
+
+	if (no_data_bam_ports || no_data_bam2bam_ports) {
+		ret = gbam_setup(no_data_bam_ports,
+						 no_data_bam2bam_ports);
+		if (ret)
+			return ret;
+	}
+
+	if (no_ctrl_smd_ports) {
+		ret = gsmd_ctrl_setup(no_ctrl_smd_ports);
+		if (ret)
+			return ret;
+	}
+
+	if (no_data_hsic_ports) {
+		port_idx = ghsic_data_setup(no_data_hsic_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].data_xport ==
+					USB_GADGET_XPORT_HSIC) {
+				rmnet_ports[i].data_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	if (no_ctrl_hsic_ports) {
+		port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].ctrl_xport ==
+					USB_GADGET_XPORT_HSIC) {
+				rmnet_ports[i].ctrl_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	if (no_data_hsuart_ports) {
+		port_idx = ghsuart_data_setup(no_data_hsuart_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].data_xport ==
+					USB_GADGET_XPORT_HSUART) {
+				rmnet_ports[i].data_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	if (no_ctrl_hsuart_ports) {
+		port_idx = ghsuart_ctrl_setup(no_ctrl_hsuart_ports,
+				USB_GADGET_RMNET);
+		if (port_idx < 0)
+			return port_idx;
+		for (i = 0; i < nr_rmnet_ports; i++) {
+			if (rmnet_ports[i].ctrl_xport ==
+					USB_GADGET_XPORT_HSUART) {
+				rmnet_ports[i].ctrl_xport_num = port_idx;
+				port_idx++;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int gport_rmnet_connect(struct f_rmnet *dev)
+{
+	int			ret;
+	unsigned		port_num;
+	enum transport_type	cxport = rmnet_ports[dev->port_num].ctrl_xport;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
+			__func__, xport_to_str(cxport), xport_to_str(dxport),
+			dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+	switch (cxport) {
+	case USB_GADGET_XPORT_SMD:
+		ret = gsmd_ctrl_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ret = ghsic_ctrl_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ret = ghsuart_ctrl_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsuart_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(cxport));
+		return -ENODEV;
+	}
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+	case USB_GADGET_XPORT_BAM2BAM:
+		ret = gbam_connect(&dev->port, port_num,
+						   dxport, port_num);
+		if (ret) {
+			pr_err("%s: gbam_connect failed: err:%d\n",
+					__func__, ret);
+			gsmd_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ret = ghsic_data_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_data_connect failed: err:%d\n",
+					__func__, ret);
+			ghsic_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ret = ghsuart_data_connect(&dev->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsuart_data_connect failed: err:%d\n",
+					__func__, ret);
+			ghsuart_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_NONE:
+		 break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int gport_rmnet_disconnect(struct f_rmnet *dev)
+{
+	unsigned		port_num;
+	enum transport_type	cxport = rmnet_ports[dev->port_num].ctrl_xport;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
+			__func__, xport_to_str(cxport), xport_to_str(dxport),
+			dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+	switch (cxport) {
+	case USB_GADGET_XPORT_SMD:
+		gsmd_ctrl_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ghsic_ctrl_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ghsuart_ctrl_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(cxport));
+		return -ENODEV;
+	}
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_disconnect(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ghsic_data_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ghsuart_data_disconnect(&dev->port, port_num);
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+
+	pr_debug("%s: portno:%d\n", __func__, dev->port_num);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+	frmnet_free_req(dev->notify, dev->notify_req);
+
+	kfree(f->name);
+}
+
+static void frmnet_suspend(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned		port_num;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+		__func__, xport_to_str(dxport),
+		dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_suspend(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+	}
+}
+
+static void frmnet_resume(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned		port_num;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+		__func__, xport_to_str(dxport),
+		dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_resume(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+	}
+}
+
+static void frmnet_disable(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned long flags;
+	struct rmnet_ctrl_pkt *cpkt;
+
+	pr_debug("%s: port#%d\n", __func__, dev->port_num);
+
+	usb_ep_disable(dev->notify);
+	dev->notify->driver_data = NULL;
+
+	atomic_set(&dev->online, 0);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->cpkt_resp_q)) {
+		cpkt = list_first_entry(&dev->cpkt_resp_q,
+				struct rmnet_ctrl_pkt, list);
+
+		list_del(&cpkt->list);
+		rmnet_free_ctrl_pkt(cpkt);
+	}
+	atomic_set(&dev->notify_count, 0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	gport_rmnet_disconnect(dev);
+}
+
+static int
+frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_rmnet			*dev = func_to_rmnet(f);
+	struct usb_composite_dev	*cdev = dev->cdev;
+	int				ret;
+	struct list_head *cpkt;
+
+	pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	if (dev->notify->driver_data) {
+		pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
+		usb_ep_disable(dev->notify);
+	}
+
+	ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
+	if (ret) {
+		dev->notify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+					dev->notify->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->notify);
+
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, dev->notify->name, ret);
+		return ret;
+	}
+	dev->notify->driver_data = dev;
+
+	if (!dev->port.in->desc || !dev->port.out->desc) {
+		if (config_ep_by_speed(cdev->gadget, f, dev->port.in) ||
+			config_ep_by_speed(cdev->gadget, f, dev->port.out)) {
+				dev->port.in->desc = NULL;
+				dev->port.out->desc = NULL;
+				return -EINVAL;
+		}
+		ret = gport_rmnet_connect(dev);
+	}
+
+	atomic_set(&dev->online, 1);
+
+	/* In case notifications were aborted, but there are pending control
+	   packets in the response queue, re-add the notifications */
+	list_for_each(cpkt, &dev->cpkt_resp_q)
+		frmnet_ctrl_response_available(dev);
+
+	return ret;
+}
+
+static void frmnet_ctrl_response_available(struct f_rmnet *dev)
+{
+	struct usb_request		*req = dev->notify_req;
+	struct usb_cdc_notification	*event;
+	unsigned long			flags;
+	int				ret;
+
+	pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (!atomic_read(&dev->online) || !req || !req->buf) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (atomic_inc_return(&dev->notify_count) != 1) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	event = req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
+	if (ret) {
+		atomic_dec(&dev->notify_count);
+		pr_debug("ep enqueue error %d\n", ret);
+	}
+}
+
+static void frmnet_connect(struct grmnet *gr)
+{
+	struct f_rmnet			*dev;
+
+	if (!gr) {
+		pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
+		return;
+	}
+
+	dev = port_to_rmnet(gr);
+
+	atomic_set(&dev->ctrl_online, 1);
+}
+
+static void frmnet_disconnect(struct grmnet *gr)
+{
+	struct f_rmnet			*dev;
+	unsigned long			flags;
+	struct usb_cdc_notification	*event;
+	int				status;
+	struct rmnet_ctrl_pkt		*cpkt;
+
+	if (!gr) {
+		pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
+		return;
+	}
+
+	dev = port_to_rmnet(gr);
+
+	atomic_set(&dev->ctrl_online, 0);
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: nothing to do\n", __func__);
+		return;
+	}
+
+	usb_ep_fifo_flush(dev->notify);
+
+	event = dev->notify_req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+
+	status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		if (!atomic_read(&dev->online))
+			return;
+		pr_err("%s: rmnet notify ep enqueue error %d\n",
+				__func__, status);
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->cpkt_resp_q)) {
+		cpkt = list_first_entry(&dev->cpkt_resp_q,
+				struct rmnet_ctrl_pkt, list);
+
+		list_del(&cpkt->list);
+		rmnet_free_ctrl_pkt(cpkt);
+	}
+	atomic_set(&dev->notify_count, 0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+}
+
+static int
+frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
+{
+	struct f_rmnet		*dev;
+	struct rmnet_ctrl_pkt	*cpkt;
+	unsigned long		flags;
+
+	if (!gr || !buf) {
+		pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
+				__func__, gr, buf);
+		return -ENODEV;
+	}
+	cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (IS_ERR(cpkt)) {
+		pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
+		return -ENOMEM;
+	}
+	memcpy(cpkt->buf, buf, len);
+	cpkt->len = len;
+
+	dev = port_to_rmnet(gr);
+
+	pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
+		rmnet_free_ctrl_pkt(cpkt);
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	frmnet_ctrl_response_available(dev);
+
+	return 0;
+}
+
+static void
+frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_rmnet			*dev = req->context;
+	struct usb_composite_dev	*cdev;
+	unsigned			port_num;
+
+	if (!dev) {
+		pr_err("%s: rmnet dev is null\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	cdev = dev->cdev;
+
+	if (dev->port.send_encap_cmd) {
+		port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+		dev->port.send_encap_cmd(port_num, req->buf, req->actual);
+	}
+}
+
+static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_rmnet *dev = req->context;
+	int status = req->status;
+
+	pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		pr_err("rmnet notify ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		if (!atomic_read(&dev->ctrl_online))
+			break;
+
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			pr_debug("ep enqueue error %d\n", status);
+		}
+		break;
+	}
+}
+
+static int
+frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_rmnet			*dev = func_to_rmnet(f);
+	struct usb_composite_dev	*cdev = dev->cdev;
+	struct usb_request		*req = cdev->req;
+	unsigned			port_num;
+	u16				w_index = le16_to_cpu(ctrl->wIndex);
+	u16				w_value = le16_to_cpu(ctrl->wValue);
+	u16				w_length = le16_to_cpu(ctrl->wLength);
+	int				ret = -EOPNOTSUPP;
+
+	pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: usb cable is not connected\n", __func__);
+		return -ENOTCONN;
+	}
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = frmnet_cmd_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			unsigned len;
+			struct rmnet_ctrl_pkt *cpkt;
+
+			spin_lock(&dev->lock);
+			if (list_empty(&dev->cpkt_resp_q)) {
+				pr_err("ctrl resp queue empty "
+					" req%02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+			}
+
+			cpkt = list_first_entry(&dev->cpkt_resp_q,
+					struct rmnet_ctrl_pkt, list);
+			list_del(&cpkt->list);
+			spin_unlock(&dev->lock);
+
+			len = min_t(unsigned, w_length, cpkt->len);
+			memcpy(req->buf, cpkt->buf, len);
+			ret = len;
+
+			rmnet_free_ctrl_pkt(cpkt);
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		if (dev->port.notify_modem) {
+			port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+			dev->port.notify_modem(&dev->port, port_num, w_value);
+		}
+		ret = 0;
+
+		break;
+	default:
+
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (ret < w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_rmnet			*dev = func_to_rmnet(f);
+	struct usb_ep			*ep;
+	struct usb_composite_dev	*cdev = c->cdev;
+	int				ret = -ENODEV;
+
+	dev->ifc_id = usb_interface_id(c, f);
+	if (dev->ifc_id < 0) {
+		pr_err("%s: unable to allocate ifc id, err:%d",
+				__func__, dev->ifc_id);
+		return dev->ifc_id;
+	}
+	rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
+	if (!ep) {
+		pr_err("%s: usb epin autoconfig failed\n", __func__);
+		return -ENODEV;
+	}
+	dev->port.in = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
+	if (!ep) {
+		pr_err("%s: usb epout autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_out_fail;
+	}
+	dev->port.out = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
+	if (!ep) {
+		pr_err("%s: usb epnotify autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_notify_fail;
+	}
+	dev->notify = ep;
+	ep->driver_data = cdev;
+
+	dev->notify_req = frmnet_alloc_req(ep,
+				sizeof(struct usb_cdc_notification),
+				GFP_KERNEL);
+	if (IS_ERR(dev->notify_req)) {
+		pr_err("%s: unable to allocate memory for notify req\n",
+				__func__);
+		ret = -ENOMEM;
+		goto ep_notify_alloc_fail;
+	}
+
+	dev->notify_req->complete = frmnet_notify_complete;
+	dev->notify_req->context = dev;
+
+	f->descriptors = usb_copy_descriptors(rmnet_fs_function);
+
+	if (!f->descriptors)
+		goto fail;
+
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		rmnet_hs_in_desc.bEndpointAddress =
+				rmnet_fs_in_desc.bEndpointAddress;
+		rmnet_hs_out_desc.bEndpointAddress =
+				rmnet_fs_out_desc.bEndpointAddress;
+		rmnet_hs_notify_desc.bEndpointAddress =
+				rmnet_fs_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
+
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
+			__func__, dev->port_num,
+			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
+			dev->port.in->name, dev->port.out->name);
+
+	return 0;
+
+fail:
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+ep_notify_alloc_fail:
+	dev->notify->driver_data = NULL;
+	dev->notify = NULL;
+ep_auto_notify_fail:
+	dev->port.out->driver_data = NULL;
+	dev->port.out = NULL;
+ep_auto_out_fail:
+	dev->port.in->driver_data = NULL;
+	dev->port.in = NULL;
+
+	return ret;
+}
+
+static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
+{
+	int			status;
+	struct f_rmnet		*dev;
+	struct usb_function	*f;
+	unsigned long		flags;
+
+	pr_debug("%s: usb config:%p\n", __func__, c);
+
+	if (portno >= nr_rmnet_ports) {
+		pr_err("%s: supporting ports#%u port_id:%u", __func__,
+				nr_rmnet_ports, portno);
+		return -ENODEV;
+	}
+
+	if (rmnet_string_defs[0].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0) {
+			pr_err("%s: failed to get string id, err:%d\n",
+					__func__, status);
+			return status;
+		}
+		rmnet_string_defs[0].id = status;
+	}
+
+	dev = rmnet_ports[portno].port;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->cdev = c->cdev;
+	f = &dev->port.func;
+	f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	if (!f->name) {
+		pr_err("%s: cannot allocate memory for name\n", __func__);
+		return -ENOMEM;
+	}
+
+	f->strings = rmnet_strings;
+	f->bind = frmnet_bind;
+	f->unbind = frmnet_unbind;
+	f->disable = frmnet_disable;
+	f->set_alt = frmnet_set_alt;
+	f->setup = frmnet_setup;
+	f->suspend = frmnet_suspend;
+	f->resume = frmnet_resume;
+	dev->port.send_cpkt_response = frmnet_send_cpkt_response;
+	dev->port.disconnect = frmnet_disconnect;
+	dev->port.connect = frmnet_connect;
+
+	status = usb_add_function(c, f);
+	if (status) {
+		pr_err("%s: usb add function failed: %d\n",
+				__func__, status);
+		kfree(f->name);
+		return status;
+	}
+
+	pr_debug("%s: complete\n", __func__);
+
+	return status;
+}
+
+static void frmnet_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < nr_rmnet_ports; i++)
+		kfree(rmnet_ports[i].port);
+
+	nr_rmnet_ports = 0;
+	no_ctrl_smd_ports = 0;
+	no_data_bam_ports = 0;
+	no_data_bam2bam_ports = 0;
+	no_ctrl_hsic_ports = 0;
+	no_data_hsic_ports = 0;
+	no_ctrl_hsuart_ports = 0;
+	no_data_hsuart_ports = 0;
+}
+
+static int frmnet_init_port(const char *ctrl_name, const char *data_name)
+{
+	struct f_rmnet			*dev;
+	struct rmnet_ports		*rmnet_port;
+	int				ret;
+	int				i;
+
+	if (nr_rmnet_ports >= NR_RMNET_PORTS) {
+		pr_err("%s: Max-%d instances supported\n",
+				__func__, NR_RMNET_PORTS);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
+		__func__, nr_rmnet_ports, ctrl_name, data_name);
+
+	dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
+	if (!dev) {
+		pr_err("%s: Unable to allocate rmnet device\n", __func__);
+		return -ENOMEM;
+	}
+
+	dev->port_num = nr_rmnet_ports;
+	spin_lock_init(&dev->lock);
+	INIT_LIST_HEAD(&dev->cpkt_resp_q);
+
+	rmnet_port = &rmnet_ports[nr_rmnet_ports];
+	rmnet_port->port = dev;
+	rmnet_port->port_num = nr_rmnet_ports;
+	rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
+	rmnet_port->data_xport = str_to_xport(data_name);
+
+	switch (rmnet_port->ctrl_xport) {
+	case USB_GADGET_XPORT_SMD:
+		rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
+		no_ctrl_smd_ports++;
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
+		no_ctrl_hsic_ports++;
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		rmnet_port->ctrl_xport_num = no_ctrl_hsuart_ports;
+		no_ctrl_hsuart_ports++;
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %u\n", __func__,
+				rmnet_port->ctrl_xport);
+		ret = -ENODEV;
+		goto fail_probe;
+	}
+
+	switch (rmnet_port->data_xport) {
+	case USB_GADGET_XPORT_BAM:
+		rmnet_port->data_xport_num = no_data_bam_ports;
+		no_data_bam_ports++;
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		rmnet_port->data_xport_num = no_data_bam2bam_ports;
+		no_data_bam2bam_ports++;
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		rmnet_port->data_xport_num = no_data_hsic_ports;
+		no_data_hsic_ports++;
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		rmnet_port->data_xport_num = no_data_hsuart_ports;
+		no_data_hsuart_ports++;
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %u\n", __func__,
+				rmnet_port->data_xport);
+		ret = -ENODEV;
+		goto fail_probe;
+	}
+	nr_rmnet_ports++;
+
+	return 0;
+
+fail_probe:
+	for (i = 0; i < nr_rmnet_ports; i++)
+		kfree(rmnet_ports[i].port);
+
+	nr_rmnet_ports = 0;
+	no_ctrl_smd_ports = 0;
+	no_data_bam_ports = 0;
+	no_ctrl_hsic_ports = 0;
+	no_data_hsic_ports = 0;
+	no_ctrl_hsuart_ports = 0;
+	no_data_hsuart_ports = 0;
+
+	return ret;
+}
diff --git a/drivers/usb/gadget/f_rmnet.h b/drivers/usb/gadget/f_rmnet.h
new file mode 100644
index 0000000..2d816c6
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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.
+ *
+ */
+
+#ifndef __F_RMNET_H
+#define __F_RMNET_H
+
+int rmnet_function_add(struct usb_configuration *c);
+
+#endif /* __F_RMNET_H */
diff --git a/drivers/usb/gadget/f_rmnet_sdio.c b/drivers/usb/gadget/f_rmnet_sdio.c
new file mode 100644
index 0000000..8019356
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet_sdio.c
@@ -0,0 +1,1576 @@
+/*
+ * f_rmnet_sdio.c -- RmNet SDIO function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/sdio_cmux.h>
+#include <mach/sdio_dmux.h>
+
+#ifdef CONFIG_RMNET_SDIO_CTL_CHANNEL
+static uint32_t rmnet_sdio_ctl_ch = CONFIG_RMNET_SDIO_CTL_CHANNEL;
+#else
+static uint32_t rmnet_sdio_ctl_ch;
+#endif
+module_param(rmnet_sdio_ctl_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_sdio_ctl_ch, "RmNet control SDIO channel ID");
+
+#ifdef CONFIG_RMNET_SDIO_DATA_CHANNEL
+static uint32_t rmnet_sdio_data_ch = CONFIG_RMNET_SDIO_DATA_CHANNEL;
+#else
+static uint32_t rmnet_sdio_data_ch;
+#endif
+module_param(rmnet_sdio_data_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_sdio_data_ch, "RmNet data SDIO channel ID");
+
+#define ACM_CTRL_DTR				(1 << 0)
+
+#define SDIO_MUX_HDR				8
+#define RMNET_SDIO_NOTIFY_INTERVAL		5
+#define RMNET_SDIO_MAX_NFY_SZE  sizeof(struct usb_cdc_notification)
+
+#define RMNET_SDIO_RX_REQ_MAX			16
+#define RMNET_SDIO_RX_REQ_SIZE			2048
+#define RMNET_SDIO_TX_REQ_MAX			200
+
+#define TX_PKT_DROP_THRESHOLD			1000
+#define RX_PKT_FLOW_CTRL_EN_THRESHOLD		1000
+#define RX_PKT_FLOW_CTRL_DISABLE		500
+
+unsigned int sdio_tx_pkt_drop_thld = TX_PKT_DROP_THRESHOLD;
+module_param(sdio_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int sdio_rx_fctrl_en_thld = RX_PKT_FLOW_CTRL_EN_THRESHOLD;
+module_param(sdio_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int sdio_rx_fctrl_dis_thld = RX_PKT_FLOW_CTRL_DISABLE;
+module_param(sdio_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+/* QMI requests & responses buffer*/
+struct rmnet_sdio_qmi_buf {
+	void *buf;
+	int len;
+	struct list_head list;
+};
+
+struct rmnet_sdio_dev {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	struct usb_ep           *epout;
+	struct usb_ep           *epin;
+	struct usb_ep           *epnotify;
+	struct usb_request      *notify_req;
+
+	u8                      ifc_id;
+	/* QMI lists */
+	struct list_head        qmi_req_q;
+	unsigned int		qreq_q_len;
+	struct list_head        qmi_resp_q;
+	unsigned int		qresp_q_len;
+	/* Tx/Rx lists */
+	struct list_head        tx_idle;
+	unsigned int		tx_idle_len;
+	struct sk_buff_head	tx_skb_queue;
+	struct list_head        rx_idle;
+	unsigned int		rx_idle_len;
+	struct sk_buff_head	rx_skb_queue;
+
+	spinlock_t              lock;
+	atomic_t                online;
+	atomic_t                notify_count;
+
+	struct workqueue_struct *wq;
+	struct work_struct disconnect_work;
+
+	struct work_struct ctl_rx_work;
+	struct work_struct data_rx_work;
+
+	struct delayed_work sdio_open_work;
+	struct work_struct sdio_close_work;
+#define RMNET_SDIO_CH_OPEN	1
+	unsigned long	data_ch_status;
+	unsigned long	ctrl_ch_status;
+
+	unsigned int dpkts_pending_atdmux;
+	int cbits_to_modem;
+	struct work_struct set_modem_ctl_bits_work;
+
+	/* pkt logging dpkt - data pkt; cpkt - control pkt*/
+	struct dentry *dent;
+	unsigned long dpkt_tolaptop;
+	unsigned long dpkt_tomodem;
+	unsigned long tx_drp_cnt;
+	unsigned long cpkt_tolaptop;
+	unsigned long cpkt_tomodem;
+};
+
+static struct usb_interface_descriptor rmnet_sdio_interface_desc = {
+	.bLength =              USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =      USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =        3,
+	.bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =   USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =   USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_sdio_fs_notify_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_SDIO_MAX_NFY_SZE),
+	.bInterval =            1 << RMNET_SDIO_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_fs_in_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_fs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_sdio_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_sdio_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_sdio_hs_notify_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_SDIO_MAX_NFY_SZE),
+	.bInterval =            RMNET_SDIO_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_hs_in_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_sdio_hs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_sdio_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_sdio_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_sdio_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_sdio_string_defs[] = {
+	[0].s = "QMI RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_sdio_string_table = {
+	.language =             0x0409, /* en-us */
+	.strings =              rmnet_sdio_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_sdio_strings[] = {
+	&rmnet_sdio_string_table,
+	NULL,
+};
+
+static struct rmnet_sdio_qmi_buf *
+rmnet_sdio_alloc_qmi(unsigned len, gfp_t kmalloc_flags)
+
+{
+	struct rmnet_sdio_qmi_buf *qmi;
+
+	qmi = kmalloc(sizeof(struct rmnet_sdio_qmi_buf), kmalloc_flags);
+	if (qmi != NULL) {
+		qmi->buf = kmalloc(len, kmalloc_flags);
+		if (qmi->buf == NULL) {
+			kfree(qmi);
+			qmi = NULL;
+		}
+	}
+
+	return qmi ? qmi : ERR_PTR(-ENOMEM);
+}
+
+static void rmnet_sdio_free_qmi(struct rmnet_sdio_qmi_buf *qmi)
+{
+	kfree(qmi->buf);
+	kfree(qmi);
+}
+/*
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or a pointer with an error code if there is an error.
+ */
+static struct usb_request *
+rmnet_sdio_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (len && req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free a usb_request and its buffer.
+ */
+static void rmnet_sdio_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void rmnet_sdio_notify_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		ERROR(cdev, "rmnet notifyep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+
+		if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+			return;
+
+		/* handle multiple pending QMI_RESPONSE_AVAILABLE
+		 * notifications by resending until we're done
+		 */
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			ERROR(cdev, "rmnet notify ep enq error %d\n", status);
+		}
+		break;
+	}
+}
+
+static void rmnet_sdio_qmi_resp_available(struct rmnet_sdio_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_cdc_notification     *event;
+	int status;
+	unsigned long flags;
+
+	/* Response will be sent later */
+	if (atomic_inc_return(&dev->notify_count) != 1)
+		return;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	event = dev->notify_req->buf;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		if (atomic_read(&dev->online))
+			atomic_dec(&dev->notify_count);
+		ERROR(cdev, "rmnet notify ep enqueue error %d\n", status);
+	}
+}
+
+#define SDIO_MAX_CTRL_PKT_SIZE	4096
+static void rmnet_sdio_ctl_receive_cb(void *data, int size, void *priv)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_sdio_qmi_buf *qmi_resp;
+	unsigned long flags;
+
+	if (!data) {
+		pr_info("%s: cmux_ch close event\n", __func__);
+		if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status) &&
+		    test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+			queue_work(dev->wq, &dev->sdio_close_work);
+		}
+		return;
+	}
+
+	if (!size || !test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+		return;
+
+
+	if (size > SDIO_MAX_CTRL_PKT_SIZE) {
+		ERROR(cdev, "ctrl pkt size:%d exceeds max pkt size:%d\n",
+				size, SDIO_MAX_CTRL_PKT_SIZE);
+		return;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		DBG(cdev, "USB disconnected\n");
+		return;
+	}
+
+	qmi_resp = rmnet_sdio_alloc_qmi(size, GFP_KERNEL);
+	if (IS_ERR(qmi_resp)) {
+		DBG(cdev, "unable to allocate memory for QMI resp\n");
+		return;
+	}
+	memcpy(qmi_resp->buf, data, size);
+	qmi_resp->len = size;
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add_tail(&qmi_resp->list, &dev->qmi_resp_q);
+	dev->qresp_q_len++;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	rmnet_sdio_qmi_resp_available(dev);
+}
+
+static void rmnet_sdio_ctl_write_done(void *data, int size, void *priv)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	VDBG(cdev, "rmnet control write done = %d bytes\n", size);
+}
+
+static void rmnet_sdio_sts_callback(int id, void *priv)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	DBG(cdev, "rmnet_sdio_sts_callback: id: %d\n", id);
+}
+
+static void rmnet_sdio_control_rx_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev = container_of(w, struct rmnet_sdio_dev,
+						 ctl_rx_work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_sdio_qmi_buf *qmi_req;
+	unsigned long flags;
+	int ret;
+
+	while (1) {
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&dev->qmi_req_q))
+			goto unlock;
+
+		qmi_req = list_first_entry(&dev->qmi_req_q,
+					struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi_req->list);
+		dev->qreq_q_len--;
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		ret = sdio_cmux_write(rmnet_sdio_ctl_ch, qmi_req->buf,
+					qmi_req->len);
+		if (ret != qmi_req->len) {
+			ERROR(cdev, "rmnet control SDIO write failed\n");
+			return;
+		}
+
+		dev->cpkt_tomodem++;
+
+		/*
+		 * cmux_write API copies the buffer and gives it to sdio_al.
+		 * Hence freeing the memory before write is completed.
+		 */
+		rmnet_sdio_free_qmi(qmi_req);
+	}
+unlock:
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_response_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		return;
+	default:
+		INFO(cdev, "rmnet %s response error %d, %d/%d\n",
+			ep->name, req->status,
+			req->actual, req->length);
+	}
+}
+
+static void rmnet_sdio_command_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_sdio_qmi_buf *qmi_req;
+	int len = req->actual;
+
+	if (req->status < 0) {
+		ERROR(cdev, "rmnet command error %d\n", req->status);
+		return;
+	}
+
+	/* discard the packet if sdio is not available */
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+		return;
+
+	qmi_req = rmnet_sdio_alloc_qmi(len, GFP_ATOMIC);
+	if (IS_ERR(qmi_req)) {
+		ERROR(cdev, "unable to allocate memory for QMI req\n");
+		return;
+	}
+	memcpy(qmi_req->buf, req->buf, len);
+	qmi_req->len = len;
+	spin_lock(&dev->lock);
+	list_add_tail(&qmi_req->list, &dev->qmi_req_q);
+	dev->qreq_q_len++;
+	spin_unlock(&dev->lock);
+	queue_work(dev->wq, &dev->ctl_rx_work);
+}
+
+static int
+rmnet_sdio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+							 function);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int                     ret = -EOPNOTSUPP;
+	u16                     w_index = le16_to_cpu(ctrl->wIndex);
+	u16                     w_value = le16_to_cpu(ctrl->wValue);
+	u16                     w_length = le16_to_cpu(ctrl->wLength);
+	struct rmnet_sdio_qmi_buf *resp;
+
+	if (!atomic_read(&dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = rmnet_sdio_command_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			unsigned len;
+
+			spin_lock(&dev->lock);
+
+			if (list_empty(&dev->qmi_resp_q)) {
+				INFO(cdev, "qmi resp empty "
+					" req%02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+			}
+
+			resp = list_first_entry(&dev->qmi_resp_q,
+				struct rmnet_sdio_qmi_buf, list);
+			list_del(&resp->list);
+			dev->qresp_q_len--;
+			spin_unlock(&dev->lock);
+
+			len = min_t(unsigned, w_length, resp->len);
+			memcpy(req->buf, resp->buf, len);
+			ret = len;
+			req->context = dev;
+			req->complete = rmnet_sdio_response_complete;
+			rmnet_sdio_free_qmi(resp);
+
+			/* check if its the right place to add */
+			dev->cpkt_tolaptop++;
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		/* This is a workaround for RmNet and is borrowed from the
+		 * CDC/ACM standard. The host driver will issue the above ACM
+		 * standard request to the RmNet interface in the following
+		 * scenario: Once the network adapter is disabled from device
+		 * manager, the above request will be sent from the qcusbnet
+		 * host driver, with DTR being '0'. Once network adapter is
+		 * enabled from device manager (or during enumeration), the
+		 * request will be sent with DTR being '1'.
+		 */
+		if (w_value & ACM_CTRL_DTR)
+			dev->cbits_to_modem |= TIOCM_DTR;
+		else
+			dev->cbits_to_modem &= ~TIOCM_DTR;
+		queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+
+		ret = 0;
+
+		break;
+	default:
+
+invalid:
+	DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (ret < w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int
+rmnet_sdio_rx_submit(struct rmnet_sdio_dev *dev, struct usb_request *req,
+						 gfp_t gfp_flags)
+{
+	struct sk_buff *skb;
+	int retval;
+
+	skb = alloc_skb(RMNET_SDIO_RX_REQ_SIZE + SDIO_MUX_HDR, gfp_flags);
+	if (skb == NULL)
+		return -ENOMEM;
+	skb_reserve(skb, SDIO_MUX_HDR);
+
+	req->buf = skb->data;
+	req->length = RMNET_SDIO_RX_REQ_SIZE;
+	req->context = skb;
+
+	retval = usb_ep_queue(dev->epout, req, gfp_flags);
+	if (retval)
+		dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+static void rmnet_sdio_start_rx(struct rmnet_sdio_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status;
+	struct usb_request *req;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("%s: USB not connected\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->rx_idle)) {
+		req = list_first_entry(&dev->rx_idle, struct usb_request, list);
+		list_del(&req->list);
+		dev->rx_idle_len--;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = rmnet_sdio_rx_submit(dev, req, GFP_ATOMIC);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+			list_add_tail(&req->list, &dev->rx_idle);
+			dev->rx_idle_len++;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_start_tx(struct rmnet_sdio_dev *dev)
+{
+	unsigned long			flags;
+	int				status;
+	struct sk_buff			*skb;
+	struct usb_request		*req;
+	struct usb_composite_dev	*cdev = dev->cdev;
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status))
+		return;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&dev->tx_idle)) {
+		skb = __skb_dequeue(&dev->tx_skb_queue);
+		if (!skb) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return;
+		}
+
+		req = list_first_entry(&dev->tx_idle, struct usb_request, list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		list_del(&req->list);
+		dev->tx_idle_len--;
+		spin_unlock(&dev->lock);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (status) {
+			/* USB still online, queue requests back */
+			if (atomic_read(&dev->online)) {
+				ERROR(cdev, "rmnet tx data enqueue err %d\n",
+						status);
+				list_add_tail(&req->list, &dev->tx_idle);
+				dev->tx_idle_len++;
+				__skb_queue_head(&dev->tx_skb_queue, skb);
+			} else {
+				req->buf = 0;
+				rmnet_sdio_free_req(dev->epin, req);
+				dev_kfree_skb_any(skb);
+			}
+			break;
+		}
+		dev->dpkt_tolaptop++;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_data_receive_cb(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_sdio_dev *dev = priv;
+	unsigned long flags;
+
+	/* SDIO mux sends NULL SKB when link state changes */
+	if (!skb) {
+		pr_info("%s: dmux_ch close event\n", __func__);
+		if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status) &&
+		    test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+			clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+			queue_work(dev->wq, &dev->sdio_close_work);
+		}
+		return;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->tx_skb_queue.qlen > sdio_tx_pkt_drop_thld) {
+		if (printk_ratelimit())
+			pr_err("%s: tx pkt dropped: tx_drop_cnt:%lu\n",
+					__func__, dev->tx_drp_cnt);
+		dev->tx_drp_cnt++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	__skb_queue_tail(&dev->tx_skb_queue, skb);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	rmnet_sdio_start_tx(dev);
+}
+
+static void rmnet_sdio_data_write_done(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_sdio_dev *dev = priv;
+
+	/* SDIO mux sends NULL SKB when link state changes */
+	if (!skb) {
+		pr_info("%s: dmux_ch open event\n", __func__);
+		queue_delayed_work(dev->wq, &dev->sdio_open_work, 0);
+		return;
+	}
+
+	dev_kfree_skb_any(skb);
+	/* this function is called from
+	 * sdio mux from spin_lock_irqsave
+	 */
+	spin_lock(&dev->lock);
+	dev->dpkts_pending_atdmux--;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) ||
+			dev->dpkts_pending_atdmux >= sdio_rx_fctrl_dis_thld) {
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	rmnet_sdio_start_rx(dev);
+}
+
+static void rmnet_sdio_data_rx_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev = container_of(w, struct rmnet_sdio_dev,
+							data_rx_work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct sk_buff *skb;
+	int ret;
+	unsigned long flags;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		pr_info("%s: sdio data ch not open\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while ((skb = __skb_dequeue(&dev->rx_skb_queue))) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = msm_sdio_dmux_write(rmnet_sdio_data_ch, skb);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret < 0) {
+			ERROR(cdev, "rmnet SDIO data write failed\n");
+			dev_kfree_skb_any(skb);
+			break;
+		} else {
+			dev->dpkt_tomodem++;
+			dev->dpkts_pending_atdmux++;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_complete_epout(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = ep->driver_data;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct sk_buff *skb = req->context;
+	int status = req->status;
+	int queue = 0;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_sdio_free_req(ep, req);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		pr_info("%s: sdio data ch not open\n", __func__);
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_sdio_free_req(ep, req);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	if (queue) {
+		__skb_queue_tail(&dev->rx_skb_queue, skb);
+		queue_work(dev->wq, &dev->data_rx_work);
+	}
+
+	if (dev->dpkts_pending_atdmux >= sdio_rx_fctrl_en_thld) {
+		list_add_tail(&req->list, &dev->rx_idle);
+		dev->rx_idle_len++;
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	status = rmnet_sdio_rx_submit(dev, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+		list_add_tail(&req->list, &dev->rx_idle);
+		dev->rx_idle_len++;
+	}
+}
+
+static void rmnet_sdio_complete_epin(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_sdio_dev *dev = ep->driver_data;
+	struct sk_buff  *skb = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		break;
+	default:
+		ERROR(cdev, "rmnet data tx ep error %d\n", status);
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	list_add_tail(&req->list, &dev->tx_idle);
+	dev->tx_idle_len++;
+	spin_unlock(&dev->lock);
+	dev_kfree_skb_any(skb);
+
+	rmnet_sdio_start_tx(dev);
+}
+
+static void rmnet_sdio_free_buf(struct rmnet_sdio_dev *dev)
+{
+	struct rmnet_sdio_qmi_buf *qmi;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	dev->dpkt_tolaptop = 0;
+	dev->dpkt_tomodem = 0;
+	dev->cpkt_tolaptop = 0;
+	dev->cpkt_tomodem = 0;
+	dev->dpkts_pending_atdmux = 0;
+	dev->tx_drp_cnt = 0;
+
+	/* free all usb requests in tx pool */
+	list_for_each_safe(act, tmp, &dev->tx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		dev->tx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epout, req);
+	}
+
+	/* free all usb requests in rx pool */
+	list_for_each_safe(act, tmp, &dev->rx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		dev->rx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epin, req);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_req_q) {
+		qmi = list_entry(act, struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qreq_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_resp_q) {
+		qmi = list_entry(act, struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qresp_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+
+	while ((skb = __skb_dequeue(&dev->tx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&dev->rx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	rmnet_sdio_free_req(dev->epnotify, dev->notify_req);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_sdio_set_modem_cbits_w(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev;
+
+	dev = container_of(w, struct rmnet_sdio_dev, set_modem_ctl_bits_work);
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+		return;
+
+	pr_debug("%s: cbits_to_modem:%d\n",
+			__func__, dev->cbits_to_modem);
+
+	sdio_cmux_tiocmset(rmnet_sdio_ctl_ch,
+			dev->cbits_to_modem,
+			~dev->cbits_to_modem);
+}
+
+static void rmnet_sdio_disconnect_work(struct work_struct *w)
+{
+	/* REVISIT: Push all the data to sdio if anythign is pending */
+}
+static void rmnet_sdio_suspend(struct usb_function *f)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+
+	if (!atomic_read(&dev->online))
+		return;
+	/* This is a workaround for Windows Host bug during suspend.
+	 * Windows 7/xp Hosts are suppose to drop DTR, when Host suspended.
+	 * Since it is not beind done, Hence exclusively dropping the DTR
+	 * from function driver suspend.
+	 */
+	dev->cbits_to_modem &= ~TIOCM_DTR;
+	queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+}
+static void rmnet_sdio_disable(struct usb_function *f)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								 function);
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	usb_ep_disable(dev->epnotify);
+	usb_ep_disable(dev->epout);
+	usb_ep_disable(dev->epin);
+
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->notify_count, 0);
+	rmnet_sdio_free_buf(dev);
+
+	/* cleanup work */
+	queue_work(dev->wq, &dev->disconnect_work);
+	dev->cbits_to_modem = 0;
+	queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+}
+
+static void rmnet_close_sdio_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev		*dev;
+	unsigned long			flags;
+	struct usb_cdc_notification     *event;
+	int				status;
+	struct rmnet_sdio_qmi_buf	*qmi;
+	struct usb_request		*req;
+	struct sk_buff			*skb;
+
+	pr_debug("%s:\n", __func__);
+
+	dev = container_of(w, struct rmnet_sdio_dev, sdio_close_work);
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	usb_ep_fifo_flush(dev->epnotify);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	event = dev->notify_req->buf;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_KERNEL);
+	if (status < 0) {
+		if (!atomic_read(&dev->online))
+			return;
+		pr_err("%s: rmnet notify ep enqueue error %d\n",
+				__func__, status);
+	}
+
+	usb_ep_fifo_flush(dev->epout);
+	usb_ep_fifo_flush(dev->epin);
+	cancel_work_sync(&dev->data_rx_work);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	/* free all usb requests in tx pool */
+	while (!list_empty(&dev->tx_idle)) {
+		req = list_first_entry(&dev->tx_idle, struct usb_request, list);
+		list_del(&req->list);
+		dev->tx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epout, req);
+	}
+
+	/* free all usb requests in rx pool */
+	while (!list_empty(&dev->rx_idle)) {
+		req = list_first_entry(&dev->rx_idle, struct usb_request, list);
+		list_del(&req->list);
+		dev->rx_idle_len--;
+		req->buf = NULL;
+		rmnet_sdio_free_req(dev->epin, req);
+	}
+
+	/* free all buffers in qmi request pool */
+	while (!list_empty(&dev->qmi_req_q)) {
+		qmi = list_first_entry(&dev->qmi_req_q,
+				struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qreq_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+
+	/* free all buffers in qmi response pool */
+	while (!list_empty(&dev->qmi_resp_q)) {
+		qmi = list_first_entry(&dev->qmi_resp_q,
+				struct rmnet_sdio_qmi_buf, list);
+		list_del(&qmi->list);
+		dev->qresp_q_len--;
+		rmnet_sdio_free_qmi(qmi);
+	}
+	atomic_set(&dev->notify_count, 0);
+
+	pr_info("%s: setting notify count to zero\n", __func__);
+
+
+	while ((skb = __skb_dequeue(&dev->tx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&dev->rx_skb_queue)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int rmnet_sdio_start_io(struct rmnet_sdio_dev *dev)
+{
+	struct usb_request *req;
+	int ret, i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (!atomic_read(&dev->online)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) ||
+			!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+
+	for (i = 0; i < RMNET_SDIO_RX_REQ_MAX; i++) {
+		req = rmnet_sdio_alloc_req(dev->epout, 0, GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			goto free_buf;
+		}
+		req->complete = rmnet_sdio_complete_epout;
+		list_add_tail(&req->list, &dev->rx_idle);
+		dev->rx_idle_len++;
+	}
+	for (i = 0; i < RMNET_SDIO_TX_REQ_MAX; i++) {
+		req = rmnet_sdio_alloc_req(dev->epin, 0, GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			goto free_buf;
+		}
+		req->complete = rmnet_sdio_complete_epin;
+		list_add_tail(&req->list, &dev->tx_idle);
+		dev->tx_idle_len++;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* Queue Rx data requests */
+	rmnet_sdio_start_rx(dev);
+
+	return 0;
+
+free_buf:
+	rmnet_sdio_free_buf(dev);
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+	return ret;
+}
+
+
+#define RMNET_SDIO_OPEN_RETRY_DELAY	msecs_to_jiffies(2000)
+#define SDIO_SDIO_OPEN_MAX_RETRY	90
+static void rmnet_open_sdio_work(struct work_struct *w)
+{
+	struct rmnet_sdio_dev *dev =
+			container_of(w, struct rmnet_sdio_dev,
+						sdio_open_work.work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret;
+	static int retry_cnt;
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		/* Control channel for QMI messages */
+		ret = sdio_cmux_open(rmnet_sdio_ctl_ch,
+				rmnet_sdio_ctl_receive_cb,
+				rmnet_sdio_ctl_write_done,
+				rmnet_sdio_sts_callback, dev);
+		if (!ret)
+			set_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+	}
+
+	if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		/* Data channel for network packets */
+		ret = msm_sdio_dmux_open(rmnet_sdio_data_ch, dev,
+				rmnet_sdio_data_receive_cb,
+				rmnet_sdio_data_write_done);
+		if (!ret)
+			set_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+	}
+
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) &&
+			test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+
+		rmnet_sdio_start_io(dev);
+
+		/* if usb cable is connected, update DTR status to modem */
+		if (atomic_read(&dev->online))
+			queue_work(dev->wq, &dev->set_modem_ctl_bits_work);
+
+		pr_info("%s: usb rmnet sdio channels are open retry_cnt:%d\n",
+				__func__, retry_cnt);
+		retry_cnt = 0;
+		return;
+	}
+
+	retry_cnt++;
+	pr_debug("%s: usb rmnet sdio open retry_cnt:%d\n",
+			__func__, retry_cnt);
+
+	if (retry_cnt > SDIO_SDIO_OPEN_MAX_RETRY) {
+		if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status))
+			ERROR(cdev, "Unable to open control SDIO channel\n");
+
+		if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status))
+			ERROR(cdev, "Unable to open DATA SDIO channel\n");
+
+	} else {
+		queue_delayed_work(dev->wq, &dev->sdio_open_work,
+				RMNET_SDIO_OPEN_RETRY_DELAY);
+	}
+}
+
+static int rmnet_sdio_set_alt(struct usb_function *f,
+			unsigned intf, unsigned alt)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* Enable epin */
+	dev->epin->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epin);
+	if (ret) {
+		dev->epin->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epin->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epin);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epin->name, ret);
+		return ret;
+	}
+
+	/* Enable epout */
+	dev->epout->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epout);
+	if (ret) {
+		dev->epout->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epout);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+
+	/* Enable epnotify */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epnotify);
+	if (ret) {
+		dev->epnotify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epnotify);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+
+	/* allocate notification */
+	dev->notify_req = rmnet_sdio_alloc_req(dev->epnotify,
+				RMNET_SDIO_MAX_NFY_SZE, GFP_ATOMIC);
+
+	if (IS_ERR(dev->notify_req)) {
+		ret = PTR_ERR(dev->notify_req);
+		pr_err("%s: unable to allocate memory for notify ep\n",
+				__func__);
+		return ret;
+	}
+	dev->notify_req->complete = rmnet_sdio_notify_complete;
+	dev->notify_req->context = dev;
+	dev->notify_req->length = RMNET_SDIO_MAX_NFY_SZE;
+
+	atomic_set(&dev->online, 1);
+
+	ret = rmnet_sdio_start_io(dev);
+
+	return ret;
+
+}
+
+static int rmnet_sdio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+	int id;
+	struct usb_ep *ep;
+
+	dev->cdev = cdev;
+
+	/* allocate interface ID */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	dev->ifc_id = id;
+	rmnet_sdio_interface_desc.bInterfaceNumber = id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_in_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epin = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_out_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epout = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_notify_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epnotify = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		rmnet_sdio_hs_in_desc.bEndpointAddress =
+			rmnet_sdio_fs_in_desc.bEndpointAddress;
+		rmnet_sdio_hs_out_desc.bEndpointAddress =
+			rmnet_sdio_fs_out_desc.bEndpointAddress;
+		rmnet_sdio_hs_notify_desc.bEndpointAddress =
+			rmnet_sdio_fs_notify_desc.bEndpointAddress;
+	}
+
+	queue_delayed_work(dev->wq, &dev->sdio_open_work, 0);
+
+	return 0;
+
+out:
+	if (dev->epnotify)
+		dev->epnotify->driver_data = NULL;
+	if (dev->epout)
+		dev->epout->driver_data = NULL;
+	if (dev->epin)
+		dev->epin->driver_data = NULL;
+
+	return -ENODEV;
+}
+
+static void
+rmnet_sdio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
+								function);
+
+	cancel_delayed_work_sync(&dev->sdio_open_work);
+	destroy_workqueue(dev->wq);
+
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		msm_sdio_dmux_close(rmnet_sdio_data_ch);
+		clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+	}
+
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		sdio_cmux_close(rmnet_sdio_ctl_ch);
+		clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+	}
+
+	debugfs_remove_recursive(dev->dent);
+
+	kfree(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t rmnet_sdio_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_sdio_dev *dev = file->private_data;
+	char *buf;
+	unsigned long flags;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 1024, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = scnprintf(buf, PAGE_SIZE,
+			"-*-DATA-*-\n"
+			"dpkts_tohost:%lu epInPool:%u tx_size:%u drp_cnt:%lu\n"
+			"dpkts_tomodem:%lu epOutPool:%u rx_size:%u pending:%u\n"
+			"-*-QMI-*-\n"
+			"cpkts_tomodem:%lu  qmi_req_q:%u cbits:%d\n"
+			"cpkts_tolaptop:%lu qmi_resp_q:%u notify_cnt:%d\n"
+			"-*-MISC-*-\n"
+			"data_ch_status: %lu ctrl_ch_status: %lu\n",
+			/* data */
+			dev->dpkt_tolaptop, dev->tx_idle_len,
+			dev->tx_skb_queue.qlen, dev->tx_drp_cnt,
+			dev->dpkt_tomodem, dev->rx_idle_len,
+			dev->rx_skb_queue.qlen, dev->dpkts_pending_atdmux,
+			/* qmi */
+			dev->cpkt_tomodem, dev->qreq_q_len,
+			dev->cbits_to_modem,
+			dev->cpkt_tolaptop, dev->qresp_q_len,
+			atomic_read(&dev->notify_count),
+			/* misc */
+			dev->data_ch_status, dev->ctrl_ch_status);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_sdio_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct rmnet_sdio_dev *dev = file->private_data;
+
+	dev->dpkt_tolaptop = 0;
+	dev->dpkt_tomodem = 0;
+	dev->cpkt_tolaptop = 0;
+	dev->cpkt_tomodem = 0;
+	dev->dpkts_pending_atdmux = 0;
+	dev->tx_drp_cnt = 0;
+
+	/* TBD: How do we reset skb qlen
+	 * it might have side effects
+	 */
+
+	return count;
+}
+
+static int debug_rmnet_sdio_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+const struct file_operations debug_rmnet_sdio_stats_ops = {
+	.open  = debug_rmnet_sdio_open,
+	.read  = rmnet_sdio_read_stats,
+	.write = rmnet_sdio_reset_stats,
+};
+
+static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev)
+{
+	dev->dent = debugfs_create_dir("usb_rmnet_sdio", 0);
+	if (IS_ERR(dev->dent))
+		return;
+
+	debugfs_create_file("status", 0444, dev->dent, dev,
+					&debug_rmnet_sdio_stats_ops);
+}
+#else
+static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev)
+{
+	return;
+}
+#endif
+
+int rmnet_sdio_function_add(struct usb_configuration *c)
+{
+	struct rmnet_sdio_dev *dev;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->wq = create_singlethread_workqueue("k_rmnet_work");
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->notify_count, 0);
+	atomic_set(&dev->online, 0);
+
+	INIT_WORK(&dev->disconnect_work, rmnet_sdio_disconnect_work);
+	INIT_WORK(&dev->set_modem_ctl_bits_work, rmnet_sdio_set_modem_cbits_w);
+
+	INIT_WORK(&dev->ctl_rx_work, rmnet_sdio_control_rx_work);
+	INIT_WORK(&dev->data_rx_work, rmnet_sdio_data_rx_work);
+
+	INIT_DELAYED_WORK(&dev->sdio_open_work, rmnet_open_sdio_work);
+	INIT_WORK(&dev->sdio_close_work, rmnet_close_sdio_work);
+
+	INIT_LIST_HEAD(&dev->qmi_req_q);
+	INIT_LIST_HEAD(&dev->qmi_resp_q);
+
+	INIT_LIST_HEAD(&dev->rx_idle);
+	INIT_LIST_HEAD(&dev->tx_idle);
+	skb_queue_head_init(&dev->tx_skb_queue);
+	skb_queue_head_init(&dev->rx_skb_queue);
+
+	dev->function.name = "rmnet_sdio";
+	dev->function.strings = rmnet_sdio_strings;
+	dev->function.descriptors = rmnet_sdio_fs_function;
+	dev->function.hs_descriptors = rmnet_sdio_hs_function;
+	dev->function.bind = rmnet_sdio_bind;
+	dev->function.unbind = rmnet_sdio_unbind;
+	dev->function.setup = rmnet_sdio_setup;
+	dev->function.set_alt = rmnet_sdio_set_alt;
+	dev->function.disable = rmnet_sdio_disable;
+	dev->function.suspend = rmnet_sdio_suspend;
+
+	ret = usb_add_function(c, &dev->function);
+	if (ret)
+		goto free_wq;
+
+	rmnet_sdio_debugfs_init(dev);
+
+       return 0;
+
+free_wq:
+       destroy_workqueue(dev->wq);
+free_dev:
+       kfree(dev);
+
+       return ret;
+}
diff --git a/drivers/usb/gadget/f_rmnet_smd.c b/drivers/usb/gadget/f_rmnet_smd.c
new file mode 100644
index 0000000..b71f646
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet_smd.c
@@ -0,0 +1,1391 @@
+/*
+ * f_rmnet.c -- RmNet function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/msm_smd.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/ch9.h>
+
+#include "gadget_chips.h"
+
+#ifndef CONFIG_MSM_SMD
+#define CONFIG_RMNET_SMD_CTL_CHANNEL	""
+#define CONFIG_RMNET_SMD_DATA_CHANNEL	""
+#endif
+
+static char *rmnet_ctl_ch = CONFIG_RMNET_SMD_CTL_CHANNEL;
+module_param(rmnet_ctl_ch, charp, S_IRUGO);
+MODULE_PARM_DESC(rmnet_ctl_ch, "RmNet control SMD channel");
+
+static char *rmnet_data_ch = CONFIG_RMNET_SMD_DATA_CHANNEL;
+module_param(rmnet_data_ch, charp, S_IRUGO);
+MODULE_PARM_DESC(rmnet_data_ch, "RmNet data SMD channel");
+
+#define RMNET_SMD_ACM_CTRL_DTR	(1 << 0)
+
+#define RMNET_SMD_NOTIFY_INTERVAL	5
+#define RMNET_SMD_MAX_NOTIFY_SIZE	sizeof(struct usb_cdc_notification)
+
+#define QMI_REQ_MAX			4
+#define QMI_REQ_SIZE			2048
+#define QMI_RESP_MAX			8
+#define QMI_RESP_SIZE			2048
+
+#define RMNET_RX_REQ_MAX		8
+#define RMNET_RX_REQ_SIZE		2048
+#define RMNET_TX_REQ_MAX		8
+#define RMNET_TX_REQ_SIZE		2048
+
+#define RMNET_TXN_MAX	 		2048
+
+/* QMI requests & responses buffer*/
+struct qmi_buf {
+	void *buf;
+	int len;
+	struct list_head list;
+};
+
+/* Control & data SMD channel private data */
+struct rmnet_smd_ch_info {
+	struct smd_channel 	*ch;
+	struct tasklet_struct	tx_tlet;
+	struct tasklet_struct	rx_tlet;
+#define CH_OPENED	0
+	unsigned long		flags;
+	/* pending rx packet length */
+	atomic_t		rx_pkt;
+	/* wait for smd open event*/
+	wait_queue_head_t	wait;
+};
+
+struct rmnet_smd_dev {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	struct usb_ep		*epout;
+	struct usb_ep		*epin;
+	struct usb_ep		*epnotify;
+	struct usb_request 	*notify_req;
+
+	u8			ifc_id;
+	/* QMI lists */
+	struct list_head	qmi_req_pool;
+	struct list_head	qmi_resp_pool;
+	struct list_head	qmi_req_q;
+	struct list_head	qmi_resp_q;
+	/* Tx/Rx lists */
+	struct list_head 	tx_idle;
+	struct list_head 	rx_idle;
+	struct list_head	rx_queue;
+
+	spinlock_t		lock;
+	atomic_t		online;
+	atomic_t		notify_count;
+
+	struct platform_driver		pdrv;
+	u8				is_pdrv_used;
+	struct rmnet_smd_ch_info	smd_ctl;
+	struct rmnet_smd_ch_info	smd_data;
+
+	struct workqueue_struct *wq;
+	struct work_struct connect_work;
+	struct work_struct disconnect_work;
+
+	unsigned long	dpkts_to_host;
+	unsigned long	dpkts_from_modem;
+	unsigned long	dpkts_from_host;
+	unsigned long	dpkts_to_modem;
+
+	unsigned long	cpkts_to_host;
+	unsigned long	cpkts_from_modem;
+	unsigned long	cpkts_from_host;
+	unsigned long	cpkts_to_modem;
+};
+
+static struct rmnet_smd_dev *rmnet_smd;
+
+static struct usb_interface_descriptor rmnet_smd_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_smd_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_SMD_MAX_NOTIFY_SIZE),
+	.bInterval =		1 << RMNET_SMD_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_fs_in_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_smd_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_smd_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_smd_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_SMD_MAX_NOTIFY_SIZE),
+	.bInterval =		RMNET_SMD_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_smd_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_smd_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_smd_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_smd_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_smd_string_defs[] = {
+	[0].s = "QMI RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_smd_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rmnet_smd_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_smd_strings[] = {
+	&rmnet_smd_string_table,
+	NULL,
+};
+
+static struct qmi_buf *
+rmnet_smd_alloc_qmi(unsigned len, gfp_t kmalloc_flags)
+{
+	struct qmi_buf *qmi;
+
+	qmi = kmalloc(sizeof(struct qmi_buf), kmalloc_flags);
+	if (qmi != NULL) {
+		qmi->buf = kmalloc(len, kmalloc_flags);
+		if (qmi->buf == NULL) {
+			kfree(qmi);
+			qmi = NULL;
+		}
+	}
+
+	return qmi ? qmi : ERR_PTR(-ENOMEM);
+}
+
+static void rmnet_smd_free_qmi(struct qmi_buf *qmi)
+{
+	kfree(qmi->buf);
+	kfree(qmi);
+}
+/*
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or a error code if there is an error.
+ */
+static struct usb_request *
+rmnet_smd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free a usb_request and its buffer.
+ */
+static void rmnet_smd_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void rmnet_smd_notify_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		ERROR(cdev, "rmnet notify ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		if (ep != dev->epnotify)
+			break;
+
+		/* handle multiple pending QMI_RESPONSE_AVAILABLE
+		 * notifications by resending until we're done
+		 */
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			ERROR(cdev, "rmnet notify ep enqueue error %d\n",
+					status);
+		}
+		break;
+	}
+}
+
+static void qmi_smd_response_available(struct rmnet_smd_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request		*req = dev->notify_req;
+	struct usb_cdc_notification	*event = req->buf;
+	int status;
+
+	/* Response will be sent later */
+	if (atomic_inc_return(&dev->notify_count) != 1)
+		return;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		atomic_dec(&dev->notify_count);
+		ERROR(cdev, "rmnet notify ep enqueue error %d\n", status);
+	}
+}
+
+/* TODO
+ * handle modem restart events
+ */
+static void rmnet_smd_event_notify(void *priv, unsigned event)
+{
+	struct rmnet_smd_ch_info *smd_info = priv;
+	int len = atomic_read(&smd_info->rx_pkt);
+	struct rmnet_smd_dev *dev =
+				(struct rmnet_smd_dev *) smd_info->tx_tlet.data;
+
+	switch (event) {
+	case SMD_EVENT_DATA: {
+		if (!atomic_read(&dev->online))
+			break;
+		if (len && (smd_write_avail(smd_info->ch) >= len))
+			tasklet_schedule(&smd_info->rx_tlet);
+
+		if (smd_read_avail(smd_info->ch))
+			tasklet_schedule(&smd_info->tx_tlet);
+
+		break;
+	}
+	case SMD_EVENT_OPEN:
+		/* usb endpoints are not enabled untill smd channels
+		 * are opened. wake up worker thread to continue
+		 * connection processing
+		 */
+		set_bit(CH_OPENED, &smd_info->flags);
+		wake_up(&smd_info->wait);
+		break;
+	case SMD_EVENT_CLOSE:
+		/* We will never come here.
+		 * reset flags after closing smd channel
+		 * */
+		clear_bit(CH_OPENED, &smd_info->flags);
+		break;
+	}
+}
+
+static void rmnet_control_tx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct qmi_buf *qmi_resp;
+	int sz;
+	unsigned long flags;
+
+	while (1) {
+		sz = smd_cur_packet_size(dev->smd_ctl.ch);
+		if (sz == 0)
+			break;
+		if (smd_read_avail(dev->smd_ctl.ch) < sz)
+			break;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&dev->qmi_resp_pool)) {
+			ERROR(cdev, "rmnet QMI Tx buffers full\n");
+			spin_unlock_irqrestore(&dev->lock, flags);
+			break;
+		}
+		qmi_resp = list_first_entry(&dev->qmi_resp_pool,
+				struct qmi_buf, list);
+		list_del(&qmi_resp->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		qmi_resp->len = smd_read(dev->smd_ctl.ch, qmi_resp->buf, sz);
+
+		spin_lock_irqsave(&dev->lock, flags);
+		dev->cpkts_from_modem++;
+		list_add_tail(&qmi_resp->list, &dev->qmi_resp_q);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		qmi_smd_response_available(dev);
+	}
+
+}
+
+static void rmnet_control_rx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct qmi_buf *qmi_req;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (1) {
+
+		if (list_empty(&dev->qmi_req_q)) {
+			atomic_set(&dev->smd_ctl.rx_pkt, 0);
+			break;
+		}
+		qmi_req = list_first_entry(&dev->qmi_req_q,
+				struct qmi_buf, list);
+		if (smd_write_avail(dev->smd_ctl.ch) < qmi_req->len) {
+			atomic_set(&dev->smd_ctl.rx_pkt, qmi_req->len);
+			DBG(cdev, "rmnet control smd channel full\n");
+			break;
+		}
+
+		list_del(&qmi_req->list);
+		dev->cpkts_from_host++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = smd_write(dev->smd_ctl.ch, qmi_req->buf, qmi_req->len);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret != qmi_req->len) {
+			ERROR(cdev, "rmnet control smd write failed\n");
+			break;
+		}
+		dev->cpkts_to_modem++;
+		list_add_tail(&qmi_req->list, &dev->qmi_req_pool);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_smd_command_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct qmi_buf *qmi_req;
+	int ret;
+
+	if (req->status < 0) {
+		ERROR(cdev, "rmnet command error %d\n", req->status);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	dev->cpkts_from_host++;
+	/* no pending control rx packet */
+	if (!atomic_read(&dev->smd_ctl.rx_pkt)) {
+		if (smd_write_avail(dev->smd_ctl.ch) < req->actual) {
+			atomic_set(&dev->smd_ctl.rx_pkt, req->actual);
+			goto queue_req;
+		}
+		spin_unlock(&dev->lock);
+		ret = smd_write(dev->smd_ctl.ch, req->buf, req->actual);
+		/* This should never happen */
+		if (ret != req->actual)
+			ERROR(cdev, "rmnet control smd write failed\n");
+		spin_lock(&dev->lock);
+		dev->cpkts_to_modem++;
+		spin_unlock(&dev->lock);
+		return;
+	}
+queue_req:
+	if (list_empty(&dev->qmi_req_pool)) {
+		spin_unlock(&dev->lock);
+		ERROR(cdev, "rmnet QMI pool is empty\n");
+		return;
+	}
+
+	qmi_req = list_first_entry(&dev->qmi_req_pool, struct qmi_buf, list);
+	list_del(&qmi_req->list);
+	spin_unlock(&dev->lock);
+	memcpy(qmi_req->buf, req->buf, req->actual);
+	qmi_req->len = req->actual;
+	spin_lock(&dev->lock);
+	list_add_tail(&qmi_req->list, &dev->qmi_req_q);
+	spin_unlock(&dev->lock);
+}
+static void rmnet_txcommand_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+
+	spin_lock(&dev->lock);
+	dev->cpkts_to_host++;
+	spin_unlock(&dev->lock);
+}
+
+static int
+rmnet_smd_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			ret = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+	struct qmi_buf *resp;
+	int schedule = 0;
+
+	if (!atomic_read(&dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = rmnet_smd_command_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			spin_lock(&dev->lock);
+			if (list_empty(&dev->qmi_resp_q)) {
+				INFO(cdev, "qmi resp empty "
+					" req%02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+			}
+			resp = list_first_entry(&dev->qmi_resp_q,
+					struct qmi_buf, list);
+			list_del(&resp->list);
+			spin_unlock(&dev->lock);
+			memcpy(req->buf, resp->buf, resp->len);
+			ret = resp->len;
+			spin_lock(&dev->lock);
+
+			if (list_empty(&dev->qmi_resp_pool))
+				schedule = 1;
+			list_add_tail(&resp->list, &dev->qmi_resp_pool);
+
+			if (schedule)
+				tasklet_schedule(&dev->smd_ctl.tx_tlet);
+			spin_unlock(&dev->lock);
+			req->complete = rmnet_txcommand_complete;
+			req->context = dev;
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		/* This is a workaround for RmNet and is borrowed from the
+		 * CDC/ACM standard. The host driver will issue the above ACM
+		 * standard request to the RmNet interface in the following
+		 * scenario: Once the network adapter is disabled from device
+		 * manager, the above request will be sent from the qcusbnet
+		 * host driver, with DTR being '0'. Once network adapter is
+		 * enabled from device manager (or during enumeration), the
+		 * request will be sent with DTR being '1'.
+		 */
+		if (w_value & RMNET_SMD_ACM_CTRL_DTR)
+			ret = smd_tiocmset(dev->smd_ctl.ch, TIOCM_DTR, 0);
+		else
+			ret = smd_tiocmset(dev->smd_ctl.ch, 0, TIOCM_DTR);
+
+		break;
+	default:
+
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void rmnet_smd_start_rx(struct rmnet_smd_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status;
+	struct usb_request *req;
+	struct list_head *pool = &dev->rx_idle;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(pool)) {
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = usb_ep_queue(dev->epout, req, GFP_ATOMIC);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+			list_add_tail(&req->list, pool);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_data_tx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int status;
+	int sz;
+	unsigned long flags;
+
+	while (1) {
+
+		sz = smd_cur_packet_size(dev->smd_data.ch);
+		if (sz == 0)
+			break;
+		if (smd_read_avail(dev->smd_data.ch) < sz)
+			break;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&dev->tx_idle)) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			DBG(cdev, "rmnet data Tx buffers full\n");
+			break;
+		}
+		req = list_first_entry(&dev->tx_idle, struct usb_request, list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		req->length = smd_read(dev->smd_data.ch, req->buf, sz);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		if (status) {
+			ERROR(cdev, "rmnet tx data enqueue err %d\n", status);
+			spin_lock_irqsave(&dev->lock, flags);
+			list_add_tail(&req->list, &dev->tx_idle);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			break;
+		}
+		spin_lock_irqsave(&dev->lock, flags);
+		dev->dpkts_from_modem++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+
+}
+
+static void rmnet_data_rx_tlet(unsigned long arg)
+{
+	struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (1) {
+		if (list_empty(&dev->rx_queue)) {
+			atomic_set(&dev->smd_data.rx_pkt, 0);
+			break;
+		}
+		req = list_first_entry(&dev->rx_queue,
+			struct usb_request, list);
+		if (smd_write_avail(dev->smd_data.ch) < req->actual) {
+			atomic_set(&dev->smd_data.rx_pkt, req->actual);
+			DBG(cdev, "rmnet SMD data channel full\n");
+			break;
+		}
+
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = smd_write(dev->smd_data.ch, req->buf, req->actual);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret != req->actual) {
+			ERROR(cdev, "rmnet SMD data write failed\n");
+			break;
+		}
+		dev->dpkts_to_modem++;
+		list_add_tail(&req->list, &dev->rx_idle);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* We have free rx data requests. */
+	rmnet_smd_start_rx(dev);
+}
+
+/* If SMD has enough room to accommodate a data rx packet,
+ * write into SMD directly. Otherwise enqueue to rx_queue.
+ * We will not write into SMD directly untill rx_queue is
+ * empty to strictly follow the ordering requests.
+ */
+static void rmnet_smd_complete_epout(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int ret;
+
+	switch (status) {
+	case 0:
+		/* normal completion */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	dev->dpkts_from_host++;
+	if (!atomic_read(&dev->smd_data.rx_pkt)) {
+		if (smd_write_avail(dev->smd_data.ch) < req->actual) {
+			atomic_set(&dev->smd_data.rx_pkt, req->actual);
+			goto queue_req;
+		}
+		spin_unlock(&dev->lock);
+		ret = smd_write(dev->smd_data.ch, req->buf, req->actual);
+		/* This should never happen */
+		if (ret != req->actual)
+			ERROR(cdev, "rmnet data smd write failed\n");
+		/* Restart Rx */
+		spin_lock(&dev->lock);
+		dev->dpkts_to_modem++;
+		list_add_tail(&req->list, &dev->rx_idle);
+		spin_unlock(&dev->lock);
+		rmnet_smd_start_rx(dev);
+		return;
+	}
+queue_req:
+	list_add_tail(&req->list, &dev->rx_queue);
+	spin_unlock(&dev->lock);
+}
+
+static void rmnet_smd_complete_epin(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_smd_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int schedule = 0;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &dev->tx_idle);
+		spin_unlock(&dev->lock);
+		break;
+	default:
+		ERROR(cdev, "rmnet data tx ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		spin_lock(&dev->lock);
+		if (list_empty(&dev->tx_idle))
+			schedule = 1;
+		list_add_tail(&req->list, &dev->tx_idle);
+		dev->dpkts_to_host++;
+		if (schedule)
+			tasklet_schedule(&dev->smd_data.tx_tlet);
+		spin_unlock(&dev->lock);
+		break;
+	}
+
+}
+
+static void rmnet_smd_disconnect_work(struct work_struct *w)
+{
+	struct qmi_buf *qmi;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+	struct rmnet_smd_dev *dev = container_of(w, struct rmnet_smd_dev,
+					disconnect_work);
+
+	tasklet_kill(&dev->smd_ctl.rx_tlet);
+	tasklet_kill(&dev->smd_ctl.tx_tlet);
+	tasklet_kill(&dev->smd_data.rx_tlet);
+	tasklet_kill(&dev->smd_data.tx_tlet);
+
+	smd_close(dev->smd_ctl.ch);
+	dev->smd_ctl.flags = 0;
+
+	smd_close(dev->smd_data.ch);
+	dev->smd_data.flags = 0;
+
+	atomic_set(&dev->notify_count, 0);
+
+	list_for_each_safe(act, tmp, &dev->rx_queue) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		list_add_tail(&req->list, &dev->rx_idle);
+	}
+
+	list_for_each_safe(act, tmp, &dev->qmi_req_q) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		list_add_tail(&qmi->list, &dev->qmi_req_pool);
+	}
+
+	list_for_each_safe(act, tmp, &dev->qmi_resp_q) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		list_add_tail(&qmi->list, &dev->qmi_resp_pool);
+	}
+
+	if (dev->is_pdrv_used) {
+		platform_driver_unregister(&dev->pdrv);
+		dev->is_pdrv_used = 0;
+	}
+}
+
+/* SMD close may sleep
+ * schedule a work to close smd channels
+ */
+static void rmnet_smd_disable(struct usb_function *f)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	atomic_set(&dev->online, 0);
+
+	usb_ep_fifo_flush(dev->epnotify);
+	usb_ep_disable(dev->epnotify);
+	usb_ep_fifo_flush(dev->epout);
+	usb_ep_disable(dev->epout);
+
+	usb_ep_fifo_flush(dev->epin);
+	usb_ep_disable(dev->epin);
+
+	/* cleanup work */
+	queue_work(dev->wq, &dev->disconnect_work);
+}
+
+static void rmnet_smd_connect_work(struct work_struct *w)
+{
+	struct rmnet_smd_dev *dev = container_of(w, struct rmnet_smd_dev,
+								connect_work);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* Control channel for QMI messages */
+	ret = smd_open(rmnet_ctl_ch, &dev->smd_ctl.ch,
+			&dev->smd_ctl, rmnet_smd_event_notify);
+	if (ret) {
+		ERROR(cdev, "Unable to open control smd channel: %d\n", ret);
+		/*
+		 * Register platform driver to be notified in case SMD channels
+		 * later becomes ready to be opened.
+		 */
+		ret = platform_driver_register(&dev->pdrv);
+		if (ret)
+			ERROR(cdev, "Platform driver %s register failed %d\n",
+					dev->pdrv.driver.name, ret);
+		else
+			dev->is_pdrv_used = 1;
+
+		return;
+	}
+	wait_event(dev->smd_ctl.wait, test_bit(CH_OPENED,
+				&dev->smd_ctl.flags));
+
+	/* Data channel for network packets */
+	ret = smd_open(rmnet_data_ch, &dev->smd_data.ch,
+			&dev->smd_data, rmnet_smd_event_notify);
+	if (ret) {
+		ERROR(cdev, "Unable to open data smd channel\n");
+		smd_close(dev->smd_ctl.ch);
+		return;
+	}
+	wait_event(dev->smd_data.wait, test_bit(CH_OPENED,
+				&dev->smd_data.flags));
+
+	atomic_set(&dev->online, 1);
+	/* Queue Rx data requests */
+	rmnet_smd_start_rx(dev);
+}
+
+static int rmnet_smd_ch_probe(struct platform_device *pdev)
+{
+	DBG(rmnet_smd->cdev, "Probe called for device: %s\n", pdev->name);
+
+	queue_work(rmnet_smd->wq, &rmnet_smd->connect_work);
+
+	return 0;
+}
+
+/* SMD open may sleep.
+ * Schedule a work to open smd channels and enable
+ * endpoints if smd channels are opened successfully.
+ */
+static int rmnet_smd_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* Enable epin endpoint */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epin);
+	if (ret) {
+		dev->epin->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failed for ep %s, result %d\n",
+			dev->epin->name, ret);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epin);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					dev->epin->name, ret);
+		return ret;
+	}
+
+	/* Enable epout endpoint */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epout);
+	if (ret) {
+		dev->epout->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failed for ep %s, result %d\n",
+					dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epout);
+
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+
+	/* Enable epnotify endpoint */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epnotify);
+	if (ret) {
+		dev->epnotify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failed for ep %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epnotify);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+
+	queue_work(dev->wq, &dev->connect_work);
+	return 0;
+}
+
+static void rmnet_smd_free_buf(struct rmnet_smd_dev *dev)
+{
+	struct qmi_buf *qmi;
+	struct usb_request *req;
+	struct list_head *act, *tmp;
+
+	dev->dpkts_to_host = 0;
+	dev->dpkts_from_modem = 0;
+	dev->dpkts_from_host = 0;
+	dev->dpkts_to_modem = 0;
+
+	dev->cpkts_to_host = 0;
+	dev->cpkts_from_modem = 0;
+	dev->cpkts_from_host = 0;
+	dev->cpkts_to_modem = 0;
+	/* free all usb requests in tx pool */
+	list_for_each_safe(act, tmp, &dev->tx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_smd_free_req(dev->epout, req);
+	}
+
+	/* free all usb requests in rx pool */
+	list_for_each_safe(act, tmp, &dev->rx_idle) {
+		req = list_entry(act, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_smd_free_req(dev->epin, req);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_req_pool) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		rmnet_smd_free_qmi(qmi);
+	}
+
+	/* free all buffers in qmi request pool */
+	list_for_each_safe(act, tmp, &dev->qmi_resp_pool) {
+		qmi = list_entry(act, struct qmi_buf, list);
+		list_del(&qmi->list);
+		rmnet_smd_free_qmi(qmi);
+	}
+
+	rmnet_smd_free_req(dev->epnotify, dev->notify_req);
+}
+static int rmnet_smd_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+	int i, id, ret;
+	struct qmi_buf *qmi;
+	struct usb_request *req;
+	struct usb_ep *ep;
+
+	dev->cdev = cdev;
+
+	/* allocate interface ID */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	dev->ifc_id = id;
+	rmnet_smd_interface_desc.bInterfaceNumber = id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_in_desc);
+	if (!ep)
+		return -ENODEV;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epin = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_out_desc);
+	if (!ep)
+		return -ENODEV;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epout = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_notify_desc);
+	if (!ep)
+		return -ENODEV;
+	ep->driver_data = cdev; /* clain endpoint */
+	dev->epnotify = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		rmnet_smd_hs_in_desc.bEndpointAddress =
+				rmnet_smd_fs_in_desc.bEndpointAddress;
+		rmnet_smd_hs_out_desc.bEndpointAddress =
+				rmnet_smd_fs_out_desc.bEndpointAddress;
+		rmnet_smd_hs_notify_desc.bEndpointAddress =
+				rmnet_smd_fs_notify_desc.bEndpointAddress;
+
+	}
+
+	/* allocate notification */
+	dev->notify_req = rmnet_smd_alloc_req(dev->epnotify,
+					RMNET_SMD_MAX_NOTIFY_SIZE, GFP_KERNEL);
+	if (IS_ERR(dev->notify_req))
+		return PTR_ERR(dev->notify_req);
+
+	dev->notify_req->complete = rmnet_smd_notify_complete;
+	dev->notify_req->context = dev;
+	dev->notify_req->length = RMNET_SMD_MAX_NOTIFY_SIZE;
+
+	/* Allocate the qmi request and response buffers */
+	for (i = 0; i < QMI_REQ_MAX; i++) {
+		qmi = rmnet_smd_alloc_qmi(QMI_REQ_SIZE, GFP_KERNEL);
+		if (IS_ERR(qmi)) {
+			ret = PTR_ERR(qmi);
+			goto free_buf;
+		}
+		list_add_tail(&qmi->list, &dev->qmi_req_pool);
+	}
+
+	for (i = 0; i < QMI_RESP_MAX; i++) {
+		qmi = rmnet_smd_alloc_qmi(QMI_RESP_SIZE, GFP_KERNEL);
+		if (IS_ERR(qmi)) {
+			ret = PTR_ERR(qmi);
+			goto free_buf;
+		}
+		list_add_tail(&qmi->list, &dev->qmi_resp_pool);
+	}
+
+	/* Allocate bulk in/out requests for data transfer */
+	for (i = 0; i < RMNET_RX_REQ_MAX; i++) {
+		req = rmnet_smd_alloc_req(dev->epout, RMNET_RX_REQ_SIZE,
+								 GFP_KERNEL);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			goto free_buf;
+		}
+		req->length = RMNET_TXN_MAX;
+		req->context = dev;
+		req->complete = rmnet_smd_complete_epout;
+		list_add_tail(&req->list, &dev->rx_idle);
+	}
+
+	for (i = 0; i < RMNET_TX_REQ_MAX; i++) {
+		req = rmnet_smd_alloc_req(dev->epin, RMNET_TX_REQ_SIZE,
+							GFP_KERNEL);
+		if (IS_ERR(req)) {
+			ret = PTR_ERR(req);
+			goto free_buf;
+		}
+		req->context = dev;
+		req->complete = rmnet_smd_complete_epin;
+		list_add_tail(&req->list, &dev->tx_idle);
+	}
+
+	return 0;
+
+free_buf:
+	rmnet_smd_free_buf(dev);
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t rmnet_smd_debug_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_smd_dev *dev = file->private_data;
+	struct rmnet_smd_ch_info smd_ctl_info = dev->smd_ctl;
+	struct rmnet_smd_ch_info smd_data_info = dev->smd_data;
+	char *buf;
+	unsigned long flags;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = scnprintf(buf, 512,
+			"smd_control_ch_opened: %lu\n"
+			"smd_data_ch_opened: %lu\n"
+			"usb online : %d\n"
+			"dpkts_from_modem: %lu\n"
+			"dpkts_to_host: %lu\n"
+			"pending_dpkts_to_host: %lu\n"
+			"dpkts_from_host: %lu\n"
+			"dpkts_to_modem: %lu\n"
+			"pending_dpkts_to_modem: %lu\n"
+			"cpkts_from_modem: %lu\n"
+			"cpkts_to_host: %lu\n"
+			"pending_cpkts_to_host: %lu\n"
+			"cpkts_from_host: %lu\n"
+			"cpkts_to_modem: %lu\n"
+			"pending_cpkts_to_modem: %lu\n"
+			"smd_read_avail_ctrl: %d\n"
+			"smd_write_avail_ctrl: %d\n"
+			"smd_read_avail_data: %d\n"
+			"smd_write_avail_data: %d\n",
+			smd_ctl_info.flags, smd_data_info.flags,
+			atomic_read(&dev->online),
+			dev->dpkts_from_modem, dev->dpkts_to_host,
+			(dev->dpkts_from_modem - dev->dpkts_to_host),
+			dev->dpkts_from_host, dev->dpkts_to_modem,
+			(dev->dpkts_from_host - dev->dpkts_to_modem),
+			dev->cpkts_from_modem, dev->cpkts_to_host,
+			(dev->cpkts_from_modem - dev->cpkts_to_host),
+			dev->cpkts_from_host, dev->cpkts_to_modem,
+			(dev->cpkts_from_host - dev->cpkts_to_modem),
+			smd_read_avail(dev->smd_ctl.ch),
+			smd_write_avail(dev->smd_ctl.ch),
+			smd_read_avail(dev->smd_data.ch),
+			smd_write_avail(dev->smd_data.ch));
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_smd_debug_reset_stats(struct file *file,
+					const char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	struct rmnet_smd_dev *dev = file->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	dev->dpkts_to_host = 0;
+	dev->dpkts_from_modem = 0;
+	dev->dpkts_from_host = 0;
+	dev->dpkts_to_modem = 0;
+
+	dev->cpkts_to_host = 0;
+	dev->cpkts_from_modem = 0;
+	dev->cpkts_from_host = 0;
+	dev->cpkts_to_modem = 0;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return count;
+}
+
+static int rmnet_smd_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+const struct file_operations rmnet_smd_debug_stats_ops = {
+	.open = rmnet_smd_debug_open,
+	.read = rmnet_smd_debug_read_stats,
+	.write = rmnet_smd_debug_reset_stats,
+};
+
+struct dentry *dent_smd;
+struct dentry *dent_smd_status;
+
+static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev)
+{
+
+	dent_smd = debugfs_create_dir("usb_rmnet_smd", 0);
+	if (IS_ERR(dent_smd))
+		return;
+
+	dent_smd_status = debugfs_create_file("status", 0444, dent_smd, dev,
+			&rmnet_smd_debug_stats_ops);
+
+	if (!dent_smd_status) {
+		debugfs_remove(dent_smd);
+		dent_smd = NULL;
+		return;
+	}
+
+	return;
+}
+#else
+static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev) {}
+#endif
+
+static void
+rmnet_smd_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
+								function);
+
+	tasklet_kill(&dev->smd_ctl.rx_tlet);
+	tasklet_kill(&dev->smd_ctl.tx_tlet);
+	tasklet_kill(&dev->smd_data.rx_tlet);
+	tasklet_kill(&dev->smd_data.tx_tlet);
+
+	flush_workqueue(dev->wq);
+	rmnet_smd_free_buf(dev);
+	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
+
+	destroy_workqueue(dev->wq);
+	debugfs_remove_recursive(dent_smd);
+	kfree(dev);
+
+}
+
+int rmnet_smd_bind_config(struct usb_configuration *c)
+{
+	struct rmnet_smd_dev *dev;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	rmnet_smd = dev;
+
+	dev->wq = create_singlethread_workqueue("k_rmnet_work");
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->notify_count, 0);
+	atomic_set(&dev->online, 0);
+	atomic_set(&dev->smd_ctl.rx_pkt, 0);
+	atomic_set(&dev->smd_data.rx_pkt, 0);
+
+	INIT_WORK(&dev->connect_work, rmnet_smd_connect_work);
+	INIT_WORK(&dev->disconnect_work, rmnet_smd_disconnect_work);
+
+	tasklet_init(&dev->smd_ctl.rx_tlet, rmnet_control_rx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&dev->smd_ctl.tx_tlet, rmnet_control_tx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&dev->smd_data.rx_tlet, rmnet_data_rx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&dev->smd_data.tx_tlet, rmnet_data_tx_tlet,
+					(unsigned long) dev);
+
+	init_waitqueue_head(&dev->smd_ctl.wait);
+	init_waitqueue_head(&dev->smd_data.wait);
+
+	dev->pdrv.probe = rmnet_smd_ch_probe;
+	dev->pdrv.driver.name = CONFIG_RMNET_SMD_CTL_CHANNEL;
+	dev->pdrv.driver.owner = THIS_MODULE;
+
+	INIT_LIST_HEAD(&dev->qmi_req_pool);
+	INIT_LIST_HEAD(&dev->qmi_req_q);
+	INIT_LIST_HEAD(&dev->qmi_resp_pool);
+	INIT_LIST_HEAD(&dev->qmi_resp_q);
+	INIT_LIST_HEAD(&dev->rx_idle);
+	INIT_LIST_HEAD(&dev->rx_queue);
+	INIT_LIST_HEAD(&dev->tx_idle);
+
+	dev->function.name = "rmnet";
+	dev->function.strings = rmnet_smd_strings;
+	dev->function.descriptors = rmnet_smd_fs_function;
+	dev->function.hs_descriptors = rmnet_smd_hs_function;
+	dev->function.bind = rmnet_smd_bind;
+	dev->function.unbind = rmnet_smd_unbind;
+	dev->function.setup = rmnet_smd_setup;
+	dev->function.set_alt = rmnet_smd_set_alt;
+	dev->function.disable = rmnet_smd_disable;
+
+	ret = usb_add_function(c, &dev->function);
+	if (ret)
+		goto free_wq;
+
+	rmnet_smd_debugfs_init(dev);
+
+	return 0;
+
+free_wq:
+	destroy_workqueue(dev->wq);
+free_dev:
+	kfree(dev);
+
+	return ret;
+}
diff --git a/drivers/usb/gadget/f_rmnet_smd_sdio.c b/drivers/usb/gadget/f_rmnet_smd_sdio.c
new file mode 100644
index 0000000..175afe3
--- /dev/null
+++ b/drivers/usb/gadget/f_rmnet_smd_sdio.c
@@ -0,0 +1,2045 @@
+/*
+ * f_rmnet_smd_sdio.c -- RmNet SMD & SDIO function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) 2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ratelimit.h>
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <asm/ioctls.h>
+
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/msm_smd.h>
+#include <mach/sdio_cmux.h>
+#include <mach/sdio_dmux.h>
+#include <mach/usb_gadget_xport.h>
+
+#ifdef CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL
+static uint32_t rmnet_mux_sdio_ctl_ch = CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL;
+#else
+static uint32_t rmnet_mux_sdio_ctl_ch;
+#endif
+module_param(rmnet_mux_sdio_ctl_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_mux_sdio_ctl_ch, "RmNetMUX control SDIO channel ID");
+
+#ifdef CONFIG_RMNET_SMD_SDIO_DATA_CHANNEL
+static uint32_t rmnet_mux_sdio_data_ch = CONFIG_RMNET_SMD_SDIO_DATA_CHANNEL;
+#else
+static uint32_t rmnet_mux_sdio_data_ch;
+#endif
+module_param(rmnet_mux_sdio_data_ch, uint, S_IRUGO);
+MODULE_PARM_DESC(rmnet_mux_sdio_data_ch, "RmNetMUX data SDIO channel ID");
+
+#ifdef CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL
+static char *rmnet_mux_smd_data_ch = CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL;
+#else
+static char *rmnet_mux_smd_data_ch;
+#endif
+module_param(rmnet_mux_smd_data_ch, charp, S_IRUGO);
+MODULE_PARM_DESC(rmnet_mux_smd_data_ch, "RmNetMUX data SMD channel");
+
+#define RMNET_MUX_ACM_CTRL_DTR			(1 << 0)
+
+#define RMNET_MUX_SDIO_HDR			8
+#define RMNET_MUX_SDIO_NOTIFY_INTERVAL		5
+#define RMNET_MUX_SDIO_MAX_NFY_SZE	sizeof(struct usb_cdc_notification)
+
+#define RMNET_MUX_SDIO_RX_REQ_MAX		16
+#define RMNET_MUX_SDIO_RX_REQ_SIZE		2048
+#define RMNET_MUX_SDIO_TX_REQ_MAX		100
+
+#define RMNET_MUX_SDIO_TX_LIMIT			1000
+#define RMNET_MUX_SDIO_RX_ENABLE_LIMIT		1000
+#define RMNET_MUX_SDIO_RX_DISABLE_LIMIT		500
+
+static uint32_t mux_sdio_tx_pkt_drop_thld = RMNET_MUX_SDIO_TX_LIMIT;
+module_param(mux_sdio_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
+
+static uint32_t mux_sdio_rx_fctrl_en_thld =
+		RMNET_MUX_SDIO_RX_ENABLE_LIMIT;
+module_param(mux_sdio_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static uint32_t mux_sdio_rx_fctrl_dis_thld = RMNET_MUX_SDIO_RX_DISABLE_LIMIT;
+module_param(mux_sdio_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+
+#define RMNET_MUX_SMD_RX_REQ_MAX		8
+#define RMNET_MUX_SMD_RX_REQ_SIZE		2048
+#define RMNET_MUX_SMD_TX_REQ_MAX		8
+#define RMNET_MUX_SMD_TX_REQ_SIZE		2048
+#define RMNET_MUX_SMD_TXN_MAX			2048
+
+struct rmnet_mux_ctrl_pkt {
+	void *buf;
+	int len;
+	struct list_head list;
+};
+
+struct rmnet_mux_ctrl_dev {
+	struct list_head tx_q;
+	wait_queue_head_t tx_wait_q;
+	unsigned long tx_len;
+
+	struct list_head rx_q;
+	unsigned long rx_len;
+
+	unsigned long cbits_to_modem;
+
+	unsigned	opened;
+};
+
+struct rmnet_mux_sdio_dev {
+	/* Tx/Rx lists */
+	struct list_head tx_idle;
+	struct sk_buff_head    tx_skb_queue;
+	struct list_head rx_idle;
+	struct sk_buff_head    rx_skb_queue;
+
+
+
+	struct work_struct data_rx_work;
+
+	struct delayed_work open_work;
+	atomic_t sdio_open;
+
+	unsigned int dpkts_pending_atdmux;
+};
+
+/* Data SMD channel */
+struct rmnet_mux_smd_info {
+	struct smd_channel *ch;
+	struct tasklet_struct tx_tlet;
+	struct tasklet_struct rx_tlet;
+#define RMNET_MUX_CH_OPENED 0
+	unsigned long flags;
+	/* pending rx packet length */
+	atomic_t rx_pkt;
+	/* wait for smd open event*/
+	wait_queue_head_t wait;
+};
+
+struct rmnet_mux_smd_dev {
+	/* Tx/Rx lists */
+	struct list_head tx_idle;
+	struct list_head rx_idle;
+	struct list_head rx_queue;
+
+	struct rmnet_mux_smd_info smd_data;
+};
+
+struct rmnet_mux_dev {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	struct usb_ep *epout;
+	struct usb_ep *epin;
+	struct usb_ep *epnotify;
+	struct usb_request *notify_req;
+
+	struct rmnet_mux_smd_dev smd_dev;
+	struct rmnet_mux_sdio_dev sdio_dev;
+	struct rmnet_mux_ctrl_dev ctrl_dev;
+
+	u8 ifc_id;
+	enum transport_type xport;
+	spinlock_t lock;
+	atomic_t online;
+	atomic_t notify_count;
+	struct workqueue_struct *wq;
+	struct work_struct disconnect_work;
+
+	/* pkt counters */
+	unsigned long dpkts_tomsm;
+	unsigned long dpkts_tomdm;
+	unsigned long dpkts_tolaptop;
+	unsigned long tx_drp_cnt;
+	unsigned long cpkts_tolaptop;
+	unsigned long cpkts_tomdm;
+	unsigned long cpkts_drp_cnt;
+};
+
+static struct rmnet_mux_dev *rmux_dev;
+
+static struct usb_interface_descriptor rmnet_mux_interface_desc = {
+	.bLength =              USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =      USB_DT_INTERFACE,
+	.bNumEndpoints =        3,
+	.bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =   USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =   USB_CLASS_VENDOR_SPEC,
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_mux_fs_notify_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_MUX_SDIO_MAX_NFY_SZE),
+	.bInterval =            1 << RMNET_MUX_SDIO_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_fs_in_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_fs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_mux_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_mux_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_mux_hs_notify_desc  = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(
+						RMNET_MUX_SDIO_MAX_NFY_SZE),
+	.bInterval =            RMNET_MUX_SDIO_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_hs_in_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_IN,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_mux_hs_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bEndpointAddress =     USB_DIR_OUT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_mux_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_mux_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_mux_hs_out_desc,
+	NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string rmnet_mux_string_defs[] = {
+	[0].s = "RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_mux_string_table = {
+	.language =             0x0409, /* en-us */
+	.strings =              rmnet_mux_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_mux_strings[] = {
+	&rmnet_mux_string_table,
+	NULL,
+};
+
+static struct rmnet_mux_ctrl_pkt *rmnet_mux_alloc_ctrl_pkt(unsigned len,
+							   gfp_t flags)
+{
+	struct rmnet_mux_ctrl_pkt *cpkt;
+
+	cpkt = kzalloc(sizeof(struct rmnet_mux_ctrl_pkt), flags);
+	if (!cpkt)
+		return 0;
+
+	cpkt->buf = kzalloc(len, flags);
+	if (!cpkt->buf) {
+		kfree(cpkt);
+		return 0;
+	}
+
+	cpkt->len = len;
+
+	return cpkt;
+
+}
+
+static void rmnet_mux_free_ctrl_pkt(struct rmnet_mux_ctrl_pkt *cpkt)
+{
+	kfree(cpkt->buf);
+	kfree(cpkt);
+}
+
+/*
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or a pointer with an error code if there is an error.
+ */
+static struct usb_request *
+rmnet_mux_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (len && req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Free a usb_request and its buffer.
+ */
+static void rmnet_mux_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static int rmnet_mux_sdio_rx_submit(struct rmnet_mux_dev *dev,
+				    struct usb_request *req, gfp_t gfp_flags)
+{
+	struct sk_buff *skb;
+	int retval;
+
+	skb = alloc_skb(RMNET_MUX_SDIO_RX_REQ_SIZE + RMNET_MUX_SDIO_HDR,
+								gfp_flags);
+	if (skb == NULL)
+		return -ENOMEM;
+	skb_reserve(skb, RMNET_MUX_SDIO_HDR);
+
+	req->buf = skb->data;
+	req->length = RMNET_MUX_SDIO_RX_REQ_SIZE;
+	req->context = skb;
+
+	retval = usb_ep_queue(dev->epout, req, gfp_flags);
+	if (retval)
+		dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+static void rmnet_mux_sdio_start_rx(struct rmnet_mux_dev *dev)
+{
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status;
+	struct usb_request *req;
+	struct list_head *pool;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: USB not connected\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	pool = &sdio_dev->rx_idle;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = rmnet_mux_sdio_rx_submit(dev, req, GFP_KERNEL);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet_mux data rx enqueue err %d\n",
+								status);
+			list_add_tail(&req->list, &sdio_dev->rx_idle);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_sdio_start_tx(struct rmnet_mux_dev *dev)
+{
+	unsigned long			flags;
+	int				status;
+	struct sk_buff			*skb;
+	struct usb_request		*req;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev	*cdev = dev->cdev;
+
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(&sdio_dev->tx_idle)) {
+		skb = __skb_dequeue(&sdio_dev->tx_skb_queue);
+		if (!skb) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return;
+		}
+
+		req = list_first_entry(&sdio_dev->tx_idle,
+				struct usb_request, list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		list_del(&req->list);
+		spin_unlock(&dev->lock);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (status) {
+			/* USB still online, queue requests back */
+			if (atomic_read(&dev->online)) {
+				ERROR(cdev, "rmnet tx data enqueue err %d\n",
+						status);
+				list_add_tail(&req->list, &sdio_dev->tx_idle);
+				__skb_queue_head(&sdio_dev->tx_skb_queue, skb);
+			} else {
+				req->buf = 0;
+				rmnet_mux_free_req(dev->epin, req);
+				dev_kfree_skb_any(skb);
+			}
+			break;
+		}
+		dev->dpkts_tolaptop++;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_sdio_data_receive_cb(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_mux_dev *dev = priv;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	unsigned long flags;
+
+	if (!skb)
+		return;
+	if (!atomic_read(&dev->online)) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+	if (sdio_dev->tx_skb_queue.qlen > mux_sdio_tx_pkt_drop_thld) {
+		pr_err_ratelimited("%s: tx pkt dropped: tx_drop_cnt:%lu\n",
+			__func__, dev->tx_drp_cnt);
+		dev->tx_drp_cnt++;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	__skb_queue_tail(&sdio_dev->tx_skb_queue, skb);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	rmnet_mux_sdio_start_tx(dev);
+}
+
+static void rmnet_mux_sdio_data_write_done(void *priv, struct sk_buff *skb)
+{
+	struct rmnet_mux_dev *dev = priv;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+
+	if (!skb)
+		return;
+
+	dev_kfree_skb_any(skb);
+	/* this function is called from
+	 * sdio mux from spin_lock_irqsave
+	 */
+	spin_lock(&dev->lock);
+	sdio_dev->dpkts_pending_atdmux--;
+
+	if (sdio_dev->dpkts_pending_atdmux >= mux_sdio_rx_fctrl_dis_thld) {
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	rmnet_mux_sdio_start_rx(dev);
+}
+
+static void rmnet_mux_sdio_data_rx_work(struct work_struct *w)
+{
+	struct rmnet_mux_dev *dev = container_of(w, struct rmnet_mux_dev,
+			sdio_dev.data_rx_work);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	struct sk_buff *skb;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while ((skb = __skb_dequeue(&sdio_dev->rx_skb_queue))) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = msm_sdio_dmux_write(rmnet_mux_sdio_data_ch, skb);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret < 0) {
+			ERROR(cdev, "rmnet_mux SDIO data write failed\n");
+			dev_kfree_skb_any(skb);
+		} else {
+			dev->dpkts_tomdm++;
+			sdio_dev->dpkts_pending_atdmux++;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void
+rmnet_mux_sdio_complete_epout(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = ep->driver_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct sk_buff *skb = req->context;
+	int status = req->status;
+	int queue = 0;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_mux_free_req(ep, req);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET_MUX %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	if (queue) {
+		__skb_queue_tail(&sdio_dev->rx_skb_queue, skb);
+		queue_work(dev->wq, &sdio_dev->data_rx_work);
+	}
+
+	if (sdio_dev->dpkts_pending_atdmux >= mux_sdio_rx_fctrl_en_thld) {
+		list_add_tail(&req->list, &sdio_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	}
+	spin_unlock(&dev->lock);
+
+	status = rmnet_mux_sdio_rx_submit(dev, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(cdev, "rmnet_mux data rx enqueue err %d\n", status);
+		list_add_tail(&req->list, &sdio_dev->rx_idle);
+	}
+}
+
+static void
+rmnet_mux_sdio_complete_epin(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = ep->driver_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct sk_buff  *skb = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		break;
+	default:
+		ERROR(cdev, "rmnet_mux data tx ep error %d\n", status);
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	list_add_tail(&req->list, &sdio_dev->tx_idle);
+	spin_unlock(&dev->lock);
+	dev_kfree_skb_any(skb);
+
+	rmnet_mux_sdio_start_tx(dev);
+}
+
+static int rmnet_mux_sdio_enable(struct rmnet_mux_dev *dev)
+{
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	int i;
+	struct usb_request *req;
+
+	/*
+	 * If the memory allocation fails, all the allocated
+	 * requests will be freed upon cable disconnect.
+	 */
+	for (i = 0; i < RMNET_MUX_SDIO_RX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epout, 0, GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->complete = rmnet_mux_sdio_complete_epout;
+		list_add_tail(&req->list, &sdio_dev->rx_idle);
+	}
+	for (i = 0; i < RMNET_MUX_SDIO_TX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epin, 0, GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->complete = rmnet_mux_sdio_complete_epin;
+		list_add_tail(&req->list, &sdio_dev->tx_idle);
+	}
+
+	rmnet_mux_sdio_start_rx(dev);
+	return 0;
+}
+
+static void rmnet_mux_smd_start_rx(struct rmnet_mux_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	int status;
+	struct usb_request *req;
+	struct list_head *pool = &smd_dev->rx_idle;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (!list_empty(pool)) {
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		status = usb_ep_queue(dev->epout, req, GFP_ATOMIC);
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (status) {
+			ERROR(cdev, "rmnet data rx enqueue err %d\n", status);
+			list_add_tail(&req->list, pool);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_smd_data_tx_tlet(unsigned long arg)
+{
+	struct rmnet_mux_dev *dev = (struct rmnet_mux_dev *) arg;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int status;
+	int sz;
+	unsigned long flags;
+
+	while (1) {
+		if (!atomic_read(&dev->online))
+			break;
+		sz = smd_cur_packet_size(smd_dev->smd_data.ch);
+		if (sz == 0)
+			break;
+		if (smd_read_avail(smd_dev->smd_data.ch) < sz)
+			break;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (list_empty(&smd_dev->tx_idle)) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			DBG(cdev, "rmnet_mux data Tx buffers full\n");
+			break;
+		}
+		req = list_first_entry(&smd_dev->tx_idle,
+				struct usb_request, list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		req->length = smd_read(smd_dev->smd_data.ch, req->buf, sz);
+		status = usb_ep_queue(dev->epin, req, GFP_ATOMIC);
+		if (status) {
+			ERROR(cdev, "rmnet tx data enqueue err %d\n", status);
+			spin_lock_irqsave(&dev->lock, flags);
+			list_add_tail(&req->list, &smd_dev->tx_idle);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			break;
+		}
+		dev->dpkts_tolaptop++;
+	}
+
+}
+
+static void rmnet_mux_smd_data_rx_tlet(unsigned long arg)
+{
+	struct rmnet_mux_dev *dev = (struct rmnet_mux_dev *) arg;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request *req;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	while (1) {
+		if (!atomic_read(&dev->online))
+			break;
+		if (list_empty(&smd_dev->rx_queue)) {
+			atomic_set(&smd_dev->smd_data.rx_pkt, 0);
+			break;
+		}
+		req = list_first_entry(&smd_dev->rx_queue,
+			struct usb_request, list);
+		if (smd_write_avail(smd_dev->smd_data.ch) < req->actual) {
+			atomic_set(&smd_dev->smd_data.rx_pkt, req->actual);
+			DBG(cdev, "rmnet_mux SMD data channel full\n");
+			break;
+		}
+
+		list_del(&req->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		ret = smd_write(smd_dev->smd_data.ch, req->buf, req->actual);
+		spin_lock_irqsave(&dev->lock, flags);
+		if (ret != req->actual) {
+			ERROR(cdev, "rmnet_mux SMD data write failed\n");
+			break;
+		}
+		dev->dpkts_tomsm++;
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* We have free rx data requests. */
+	rmnet_mux_smd_start_rx(dev);
+}
+
+/* If SMD has enough room to accommodate a data rx packet,
+ * write into SMD directly. Otherwise enqueue to rx_queue.
+ * We will not write into SMD directly untill rx_queue is
+ * empty to strictly follow the ordering requests.
+ */
+static void
+rmnet_mux_smd_complete_epout(struct usb_ep *ep, struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int ret;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* normal completion */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	default:
+		/* unexpected failure */
+		ERROR(cdev, "RMNET_MUX %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	if (!atomic_read(&smd_dev->smd_data.rx_pkt)) {
+		if (smd_write_avail(smd_dev->smd_data.ch) < req->actual) {
+			atomic_set(&smd_dev->smd_data.rx_pkt, req->actual);
+			goto queue_req;
+		}
+		spin_unlock(&dev->lock);
+		ret = smd_write(smd_dev->smd_data.ch, req->buf, req->actual);
+		/* This should never happen */
+		if (ret != req->actual)
+			ERROR(cdev, "rmnet_mux data smd write failed\n");
+		/* Restart Rx */
+		dev->dpkts_tomsm++;
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+		spin_unlock(&dev->lock);
+		rmnet_mux_smd_start_rx(dev);
+		return;
+	}
+queue_req:
+	list_add_tail(&req->list, &smd_dev->rx_queue);
+	spin_unlock(&dev->lock);
+}
+
+static void rmnet_mux_smd_complete_epin(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+	int schedule = 0;
+
+	if (dev->xport == USB_GADGET_XPORT_UNDEF) {
+		rmnet_mux_free_req(ep, req);
+		return;
+	}
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		spin_lock(&dev->lock);
+		list_add_tail(&req->list, &smd_dev->tx_idle);
+		spin_unlock(&dev->lock);
+		break;
+	default:
+		ERROR(cdev, "rmnet_mux data tx ep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+		spin_lock(&dev->lock);
+		if (list_empty(&smd_dev->tx_idle))
+			schedule = 1;
+		list_add_tail(&req->list, &smd_dev->tx_idle);
+
+		if (schedule)
+			tasklet_schedule(&smd_dev->smd_data.tx_tlet);
+		spin_unlock(&dev->lock);
+		break;
+	}
+
+}
+
+
+static void rmnet_mux_smd_notify(void *priv, unsigned event)
+{
+	struct rmnet_mux_dev *dev = priv;
+	struct rmnet_mux_smd_info *smd_info = &dev->smd_dev.smd_data;
+	int len = atomic_read(&smd_info->rx_pkt);
+
+	switch (event) {
+	case SMD_EVENT_DATA: {
+		if (!atomic_read(&dev->online))
+			break;
+		if (len && (smd_write_avail(smd_info->ch) >= len))
+			tasklet_schedule(&smd_info->rx_tlet);
+
+		if (smd_read_avail(smd_info->ch))
+			tasklet_schedule(&smd_info->tx_tlet);
+
+		break;
+	}
+	case SMD_EVENT_OPEN:
+		/* usb endpoints are not enabled untill smd channels
+		 * are opened. wake up worker thread to continue
+		 * connection processing
+		 */
+		set_bit(RMNET_MUX_CH_OPENED, &smd_info->flags);
+		wake_up(&smd_info->wait);
+		break;
+	case SMD_EVENT_CLOSE:
+		/* We will never come here.
+		 * reset flags after closing smd channel
+		 * */
+		clear_bit(RMNET_MUX_CH_OPENED, &smd_info->flags);
+		break;
+	}
+}
+
+static int rmnet_mux_smd_enable(struct rmnet_mux_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	int i, ret;
+	struct usb_request *req;
+
+	if (test_bit(RMNET_MUX_CH_OPENED, &smd_dev->smd_data.flags))
+		goto smd_alloc_req;
+
+	ret = smd_open(rmnet_mux_smd_data_ch, &smd_dev->smd_data.ch,
+			dev, rmnet_mux_smd_notify);
+	if (ret) {
+		ERROR(cdev, "Unable to open data smd channel\n");
+		return ret;
+	}
+
+	wait_event(smd_dev->smd_data.wait, test_bit(RMNET_MUX_CH_OPENED,
+				&smd_dev->smd_data.flags));
+
+	/* Allocate bulk in/out requests for data transfer.
+	 * If the memory allocation fails, all the allocated
+	 * requests will be freed upon cable disconnect.
+	 */
+smd_alloc_req:
+	for (i = 0; i < RMNET_MUX_SMD_RX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epout, RMNET_MUX_SMD_RX_REQ_SIZE,
+				GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->length = RMNET_MUX_SMD_TXN_MAX;
+		req->context = dev;
+		req->complete = rmnet_mux_smd_complete_epout;
+		list_add_tail(&req->list, &smd_dev->rx_idle);
+	}
+
+	for (i = 0; i < RMNET_MUX_SMD_TX_REQ_MAX; i++) {
+		req = rmnet_mux_alloc_req(dev->epin, RMNET_MUX_SMD_TX_REQ_SIZE,
+				GFP_KERNEL);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+		req->context = dev;
+		req->complete = rmnet_mux_smd_complete_epin;
+		list_add_tail(&req->list, &smd_dev->tx_idle);
+	}
+
+	rmnet_mux_smd_start_rx(dev);
+	return 0;
+}
+
+static void rmnet_mux_notify_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&dev->notify_count, 0);
+		break;
+	default:
+		ERROR(cdev, "rmnet_mux notifyep error %d\n", status);
+		/* FALLTHROUGH */
+	case 0:
+
+		if (atomic_dec_and_test(&dev->notify_count))
+			break;
+
+		status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&dev->notify_count);
+			ERROR(cdev, "rmnet notify ep enq error %d\n", status);
+		}
+		break;
+	}
+}
+
+static void ctrl_response_available(struct rmnet_mux_dev *dev)
+{
+	struct usb_composite_dev *cdev = dev->cdev;
+	struct usb_request              *req = dev->notify_req;
+	struct usb_cdc_notification     *event = req->buf;
+	int status;
+
+	/* Response will be sent later */
+	if (atomic_inc_return(&dev->notify_count) != 1)
+		return;
+
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ifc_id);
+	event->wLength = cpu_to_le16(0);
+
+	status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC);
+	if (status < 0) {
+		atomic_dec(&dev->notify_count);
+		ERROR(cdev, "rmnet_mux notify ep enqueue error %d\n", status);
+	}
+}
+
+#define MAX_CTRL_PKT_SIZE	4096
+
+static void rmnet_mux_response_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_mux_dev *dev = req->context;
+	struct usb_composite_dev *cdev = dev->cdev;
+
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		return;
+	default:
+		INFO(cdev, "rmnet_mux %s response error %d, %d/%d\n",
+			ep->name, req->status,
+			req->actual, req->length);
+	}
+}
+
+static void rmnet_mux_command_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct rmnet_mux_dev		*dev = req->context;
+	struct usb_composite_dev	*cdev = dev->cdev;
+	struct rmnet_mux_ctrl_dev		*ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_ctrl_pkt		*cpkt;
+	int				len = req->actual;
+
+	if (req->status < 0) {
+		ERROR(cdev, "rmnet_mux command error %d\n", req->status);
+		return;
+	}
+
+	cpkt = rmnet_mux_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (!cpkt) {
+		ERROR(cdev, "unable to allocate memory for ctrl req\n");
+		return;
+	}
+
+	spin_lock(&dev->lock);
+	if (!ctrl_dev->opened) {
+		spin_unlock(&dev->lock);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		dev->cpkts_drp_cnt++;
+		pr_err_ratelimited(
+			"%s: ctrl pkts dropped: cpkts_drp_cnt: %lu\n",
+			__func__, dev->cpkts_drp_cnt);
+		return;
+	}
+
+	memcpy(cpkt->buf, req->buf, len);
+
+	list_add_tail(&cpkt->list, &ctrl_dev->tx_q);
+	ctrl_dev->tx_len++;
+	spin_unlock(&dev->lock);
+
+	/* wakeup read thread */
+	wake_up(&ctrl_dev->tx_wait_q);
+}
+
+static int
+rmnet_mux_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								 function);
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int                     ret = -EOPNOTSUPP;
+	u16                     w_index = le16_to_cpu(ctrl->wIndex);
+	u16                     w_value = le16_to_cpu(ctrl->wValue);
+	u16                     w_length = le16_to_cpu(ctrl->wLength);
+	struct rmnet_mux_ctrl_pkt	*cpkt;
+
+	if (!atomic_read(&dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		ret = w_length;
+		req->complete = rmnet_mux_command_complete;
+		req->context = dev;
+		break;
+
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value)
+			goto invalid;
+		else {
+			unsigned len;
+
+			spin_lock(&dev->lock);
+			if (list_empty(&ctrl_dev->rx_q)) {
+				DBG(cdev, "ctrl resp queue empty"
+					" %02x.%02x v%04x i%04x l%d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					w_value, w_index, w_length);
+				spin_unlock(&dev->lock);
+				goto invalid;
+
+			}
+			cpkt = list_first_entry(&ctrl_dev->rx_q,
+					struct rmnet_mux_ctrl_pkt, list);
+			list_del(&cpkt->list);
+			ctrl_dev->rx_len--;
+			spin_unlock(&dev->lock);
+
+			len = min_t(unsigned, w_length, cpkt->len);
+			memcpy(req->buf, cpkt->buf, len);
+			ret = len;
+			req->complete = rmnet_mux_response_complete;
+			req->context = dev;
+			rmnet_mux_free_ctrl_pkt(cpkt);
+
+			dev->cpkts_tolaptop++;
+		}
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		/* This is a workaround for RmNet and is borrowed from the
+		 * CDC/ACM standard. The host driver will issue the above ACM
+		 * standard request to the RmNet interface in the following
+		 * scenario: Once the network adapter is disabled from device
+		 * manager, the above request will be sent from the qcusbnet
+		 * host driver, with DTR being '0'. Once network adapter is
+		 * enabled from device manager (or during enumeration), the
+		 * request will be sent with DTR being '1'.
+		 */
+		if (w_value & RMNET_MUX_ACM_CTRL_DTR)
+			ctrl_dev->cbits_to_modem |= TIOCM_DTR;
+		else
+			ctrl_dev->cbits_to_modem &= ~TIOCM_DTR;
+
+		ret = 0;
+
+		break;
+	default:
+
+invalid:
+	DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		VDBG(cdev, "rmnet_mux req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (ret < w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			ERROR(cdev, "rmnet_mux ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev)
+{
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct rmnet_mux_ctrl_pkt *cpkt;
+	struct usb_request *req;
+	struct list_head *pool;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	/* free all usb requests in SDIO tx pool */
+	pool = &sdio_dev->tx_idle;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		req->buf = NULL;
+		rmnet_mux_free_req(dev->epout, req);
+	}
+
+	pool = &sdio_dev->rx_idle;
+	/* free all usb requests in SDIO rx pool */
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		req->buf = NULL;
+		rmnet_mux_free_req(dev->epin, req);
+	}
+
+	while ((skb = __skb_dequeue(&sdio_dev->tx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&sdio_dev->rx_skb_queue)))
+		dev_kfree_skb_any(skb);
+
+	/* free all usb requests in SMD tx pool */
+	pool = &smd_dev->tx_idle;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_mux_free_req(dev->epout, req);
+	}
+
+	pool = &smd_dev->rx_idle;
+	/* free all usb requests in SMD rx pool */
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_mux_free_req(dev->epin, req);
+	}
+
+	/* free all usb requests in SMD rx queue */
+	pool = &smd_dev->rx_queue;
+	while (!list_empty(pool)) {
+		req = list_first_entry(pool, struct usb_request, list);
+		list_del(&req->list);
+		rmnet_mux_free_req(dev->epin, req);
+	}
+
+	pool = &ctrl_dev->tx_q;
+	while (!list_empty(pool)) {
+		cpkt = list_first_entry(pool, struct rmnet_mux_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		ctrl_dev->tx_len--;
+	}
+
+	pool = &ctrl_dev->rx_q;
+	while (!list_empty(pool)) {
+		cpkt = list_first_entry(pool, struct rmnet_mux_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		ctrl_dev->rx_len--;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void rmnet_mux_disconnect_work(struct work_struct *w)
+{
+	struct rmnet_mux_dev *dev = container_of(w, struct rmnet_mux_dev,
+			disconnect_work);
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	if (dev->xport == USB_GADGET_XPORT_SMD) {
+		tasklet_kill(&smd_dev->smd_data.rx_tlet);
+		tasklet_kill(&smd_dev->smd_data.tx_tlet);
+	}
+
+	rmnet_mux_free_buf(dev);
+	dev->xport = 0;
+
+	/* wakeup read thread */
+	wake_up(&ctrl_dev->tx_wait_q);
+}
+
+static void rmnet_mux_suspend(struct usb_function *f)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	if (!atomic_read(&dev->online))
+		return;
+	/* This is a workaround for Windows Host bug during suspend.
+	 * Windows 7/xp Hosts are suppose to drop DTR, when Host suspended.
+	 * Since it is not being done, Hence exclusively dropping the DTR
+	 * from function driver suspend.
+	 */
+	ctrl_dev->cbits_to_modem &= ~TIOCM_DTR;
+}
+
+static void rmnet_mux_disable(struct usb_function *f)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	if (!atomic_read(&dev->online))
+		return;
+
+	atomic_set(&dev->online, 0);
+
+	usb_ep_fifo_flush(dev->epnotify);
+	usb_ep_disable(dev->epnotify);
+	rmnet_mux_free_req(dev->epnotify, dev->notify_req);
+
+	usb_ep_fifo_flush(dev->epout);
+	usb_ep_disable(dev->epout);
+
+	usb_ep_fifo_flush(dev->epin);
+	usb_ep_disable(dev->epin);
+
+	/* cleanup work */
+	ctrl_dev->cbits_to_modem = 0;
+	queue_work(dev->wq, &dev->disconnect_work);
+}
+
+#define SDIO_OPEN_RETRY_DELAY	msecs_to_jiffies(2000)
+#define SDIO_OPEN_MAX_RETRY	90
+static void rmnet_mux_open_sdio_work(struct work_struct *w)
+{
+	struct rmnet_mux_dev *dev =
+		container_of(w, struct rmnet_mux_dev, sdio_dev.open_work.work);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret;
+	static int retry_cnt;
+
+	/* Data channel for network packets */
+	ret = msm_sdio_dmux_open(rmnet_mux_sdio_data_ch, dev,
+				rmnet_mux_sdio_data_receive_cb,
+				rmnet_mux_sdio_data_write_done);
+	if (ret) {
+		if (retry_cnt > SDIO_OPEN_MAX_RETRY) {
+			ERROR(cdev, "Unable to open SDIO DATA channel\n");
+			return;
+		}
+		retry_cnt++;
+		queue_delayed_work(dev->wq, &sdio_dev->open_work,
+					SDIO_OPEN_RETRY_DELAY);
+		return;
+	}
+
+
+	atomic_set(&sdio_dev->sdio_open, 1);
+	pr_info("%s: usb rmnet_mux sdio channels are open retry_cnt:%d\n",
+				__func__, retry_cnt);
+	retry_cnt = 0;
+	return;
+}
+
+static int rmnet_mux_set_alt(struct usb_function *f,
+			unsigned intf, unsigned alt)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct usb_composite_dev *cdev = dev->cdev;
+	int ret = 0;
+
+	/* allocate notification */
+	dev->notify_req = rmnet_mux_alloc_req(dev->epnotify,
+				RMNET_MUX_SDIO_MAX_NFY_SZE, GFP_ATOMIC);
+
+	if (IS_ERR(dev->notify_req))
+		return PTR_ERR(dev->notify_req);
+
+	dev->notify_req->complete = rmnet_mux_notify_complete;
+	dev->notify_req->context = dev;
+	dev->notify_req->length = RMNET_MUX_SDIO_MAX_NFY_SZE;
+
+	/* Enable epin */
+	dev->epin->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epin);
+	if (ret) {
+			dev->epin->desc = NULL;
+			ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->epin->name, ret);
+			return ret;
+	}
+	ret = usb_ep_enable(dev->epin);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+		dev->epin->name, ret);
+		return ret;
+	}
+
+	/* Enable epout */
+	dev->epout->driver_data = dev;
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epout);
+	if (ret) {
+		dev->epout->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epout);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epout->name, ret);
+		usb_ep_disable(dev->epin);
+		return ret;
+	}
+
+	/* Enable epnotify */
+	ret = config_ep_by_speed(cdev->gadget, f, dev->epnotify);
+	if (ret) {
+		dev->epnotify->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+	ret = usb_ep_enable(dev->epnotify);
+	if (ret) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+			dev->epnotify->name, ret);
+		usb_ep_disable(dev->epin);
+		usb_ep_disable(dev->epout);
+		return ret;
+	}
+
+	dev->dpkts_tolaptop = 0;
+	dev->cpkts_tolaptop = 0;
+	dev->cpkts_tomdm = 0;
+	dev->dpkts_tomdm = 0;
+	dev->dpkts_tomsm = 0;
+	dev->tx_drp_cnt = 0;
+	dev->cpkts_drp_cnt = 0;
+	sdio_dev->dpkts_pending_atdmux = 0;
+	atomic_set(&dev->online, 1);
+
+	return 0;
+}
+
+static ssize_t transport_store(
+		struct device *device, struct device_attribute *attr,
+		const char *buf, size_t size)
+{
+	struct rmnet_mux_dev *dev =  rmux_dev;
+	int value;
+	enum transport_type given_xport;
+	enum transport_type t;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct list_head *pool;
+	struct sk_buff_head *skb_pool;
+	struct sk_buff *skb;
+	struct usb_request *req;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("%s: usb cable is not connected\n", __func__);
+		return -EINVAL;
+	}
+
+	sscanf(buf, "%d", &value);
+	if (value)
+		given_xport = USB_GADGET_XPORT_SDIO;
+	else
+		given_xport = USB_GADGET_XPORT_SMD;
+
+	if (given_xport == dev->xport) {
+		pr_err("%s: given_xport:%s cur_xport:%s doing nothing\n",
+				__func__, xport_to_str(given_xport),
+				xport_to_str(dev->xport));
+		return 0;
+	}
+
+	pr_debug("usb_rmnet_mux: TransportRequested: %s\n",
+			xport_to_str(given_xport));
+
+	/* prevent any other pkts to/from usb  */
+	t = dev->xport;
+	dev->xport = USB_GADGET_XPORT_UNDEF;
+	if (t != USB_GADGET_XPORT_UNDEF) {
+		usb_ep_fifo_flush(dev->epin);
+		usb_ep_fifo_flush(dev->epout);
+	}
+
+	switch (t) {
+	case USB_GADGET_XPORT_SDIO:
+		spin_lock_irqsave(&dev->lock, flags);
+		/* tx_idle */
+
+		sdio_dev->dpkts_pending_atdmux = 0;
+
+		pool = &sdio_dev->tx_idle;
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			req->buf = NULL;
+			rmnet_mux_free_req(dev->epout, req);
+		}
+
+		/* rx_idle */
+		pool = &sdio_dev->rx_idle;
+		/* free all usb requests in SDIO rx pool */
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			req->buf = NULL;
+			rmnet_mux_free_req(dev->epin, req);
+		}
+
+		/* tx_skb_queue */
+		skb_pool = &sdio_dev->tx_skb_queue;
+		while ((skb = __skb_dequeue(skb_pool)))
+			dev_kfree_skb_any(skb);
+		/* rx_skb_queue */
+		skb_pool = &sdio_dev->rx_skb_queue;
+		while ((skb = __skb_dequeue(skb_pool)))
+			dev_kfree_skb_any(skb);
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		/* close smd xport */
+		tasklet_kill(&smd_dev->smd_data.rx_tlet);
+		tasklet_kill(&smd_dev->smd_data.tx_tlet);
+
+		spin_lock_irqsave(&dev->lock, flags);
+		/* free all usb requests in SMD tx pool */
+		pool = &smd_dev->tx_idle;
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			rmnet_mux_free_req(dev->epout, req);
+		}
+
+		pool = &smd_dev->rx_idle;
+		/* free all usb requests in SMD rx pool */
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			rmnet_mux_free_req(dev->epin, req);
+		}
+
+		/* free all usb requests in SMD rx queue */
+		pool = &smd_dev->rx_queue;
+		while (!list_empty(pool)) {
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del(&req->list);
+			rmnet_mux_free_req(dev->epin, req);
+		}
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+		break;
+	default:
+		pr_debug("%s: undefined xport, do nothing\n", __func__);
+	}
+
+	dev->xport = given_xport;
+
+	switch (dev->xport) {
+	case USB_GADGET_XPORT_SDIO:
+		rmnet_mux_sdio_enable(dev);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		rmnet_mux_smd_enable(dev);
+		break;
+	default:
+		/* we should never come here */
+		pr_err("%s: undefined transport\n", __func__);
+	}
+
+	return size;
+}
+static DEVICE_ATTR(transport, S_IRUGO | S_IWUSR, NULL, transport_store);
+
+static int rmnet_mux_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	int id;
+	struct usb_ep *ep;
+
+	dev->cdev = cdev;
+
+	/* allocate interface ID */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	dev->ifc_id = id;
+	rmnet_mux_interface_desc.bInterfaceNumber = id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_mux_fs_in_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epin = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_mux_fs_out_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epout = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &rmnet_mux_fs_notify_desc);
+	if (!ep)
+		goto out;
+	ep->driver_data = cdev; /* claim endpoint */
+	dev->epnotify = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		rmnet_mux_hs_in_desc.bEndpointAddress =
+			rmnet_mux_fs_in_desc.bEndpointAddress;
+		rmnet_mux_hs_out_desc.bEndpointAddress =
+			rmnet_mux_fs_out_desc.bEndpointAddress;
+		rmnet_mux_hs_notify_desc.bEndpointAddress =
+			rmnet_mux_fs_notify_desc.bEndpointAddress;
+	}
+
+	queue_delayed_work(dev->wq, &sdio_dev->open_work, 0);
+
+	return 0;
+
+out:
+	if (dev->epnotify)
+		dev->epnotify->driver_data = NULL;
+	if (dev->epout)
+		dev->epout->driver_data = NULL;
+	if (dev->epin)
+		dev->epin->driver_data = NULL;
+
+	return -ENODEV;
+}
+
+static void rmnet_mux_smd_init(struct rmnet_mux_smd_dev *smd_dev)
+{
+	struct rmnet_mux_dev *dev = container_of(smd_dev,
+			struct rmnet_mux_dev, smd_dev);
+
+	atomic_set(&smd_dev->smd_data.rx_pkt, 0);
+	tasklet_init(&smd_dev->smd_data.rx_tlet, rmnet_mux_smd_data_rx_tlet,
+					(unsigned long) dev);
+	tasklet_init(&smd_dev->smd_data.tx_tlet, rmnet_mux_smd_data_tx_tlet,
+					(unsigned long) dev);
+
+	init_waitqueue_head(&smd_dev->smd_data.wait);
+
+	INIT_LIST_HEAD(&smd_dev->rx_idle);
+	INIT_LIST_HEAD(&smd_dev->rx_queue);
+	INIT_LIST_HEAD(&smd_dev->tx_idle);
+}
+
+static void rmnet_mux_sdio_init(struct rmnet_mux_sdio_dev *sdio_dev)
+{
+	INIT_WORK(&sdio_dev->data_rx_work, rmnet_mux_sdio_data_rx_work);
+
+	INIT_DELAYED_WORK(&sdio_dev->open_work, rmnet_mux_open_sdio_work);
+
+	INIT_LIST_HEAD(&sdio_dev->rx_idle);
+	INIT_LIST_HEAD(&sdio_dev->tx_idle);
+	skb_queue_head_init(&sdio_dev->tx_skb_queue);
+	skb_queue_head_init(&sdio_dev->rx_skb_queue);
+}
+
+static void
+rmnet_mux_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
+								function);
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+
+	smd_dev->smd_data.flags = 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t rmnet_mux_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = file->private_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	char *debug_buf;
+	unsigned long flags;
+	int ret;
+
+	debug_buf = kmalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!debug_buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = scnprintf(debug_buf, DEBUG_BUF_SIZE,
+			"dpkts_tomsm:  %lu\n"
+			"dpkts_tomdm: %lu\n"
+			"cpkts_tomdm: %lu\n"
+			"dpkts_tolaptop: %lu\n"
+			"cpkts_tolaptop:  %lu\n"
+			"cbits_to_modem: %lu\n"
+			"tx skb size:     %u\n"
+			"rx_skb_size:     %u\n"
+			"dpkts_pending_at_dmux: %u\n"
+			"tx drp cnt: %lu\n"
+			"cpkts_drp_cnt: %lu\n"
+			"cpkt_tx_qlen: %lu\n"
+			"cpkt_rx_qlen_to_modem: %lu\n"
+			"xport: %s\n"
+			"ctr_ch_opened:	%d\n",
+			dev->dpkts_tomsm, dev->dpkts_tomdm,
+			dev->cpkts_tomdm, dev->dpkts_tolaptop,
+			dev->cpkts_tolaptop, ctrl_dev->cbits_to_modem,
+			sdio_dev->tx_skb_queue.qlen,
+			sdio_dev->rx_skb_queue.qlen,
+			sdio_dev->dpkts_pending_atdmux, dev->tx_drp_cnt,
+			dev->cpkts_drp_cnt,
+			ctrl_dev->tx_len, ctrl_dev->rx_len,
+			xport_to_str(dev->xport), ctrl_dev->opened);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, debug_buf, ret);
+
+	kfree(debug_buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_mux_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = file->private_data;
+	struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
+
+	dev->dpkts_tolaptop = 0;
+	dev->cpkts_tolaptop = 0;
+	dev->cpkts_tomdm = 0;
+	dev->dpkts_tomdm = 0;
+	dev->dpkts_tomsm = 0;
+	sdio_dev->dpkts_pending_atdmux = 0;
+	dev->tx_drp_cnt = 0;
+	dev->cpkts_drp_cnt = 0;
+	return count;
+}
+
+static int dbg_rmnet_mux_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+const struct file_operations rmnet_mux_svlte_debug_stats_ops = {
+	.open = dbg_rmnet_mux_open,
+	.read = rmnet_mux_read_stats,
+	.write = rmnet_mux_reset_stats,
+};
+
+struct dentry *dent_rmnet_mux;
+
+static void rmnet_mux_debugfs_init(struct rmnet_mux_dev *dev)
+{
+
+	dent_rmnet_mux = debugfs_create_dir("usb_rmnet_mux", 0);
+	if (IS_ERR(dent_rmnet_mux))
+		return;
+
+	debugfs_create_file("status", 0444, dent_rmnet_mux, dev,
+			&rmnet_mux_svlte_debug_stats_ops);
+}
+#else
+static void rmnet_mux_debugfs_init(struct rmnet_mux_dev *dev) {}
+#endif
+
+int usb_rmnet_mux_ctrl_open(struct inode *inode, struct file *fp)
+{
+	struct rmnet_mux_dev *dev =  rmux_dev;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (ctrl_dev->opened) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		pr_err("%s: device is already opened\n", __func__);
+		return -EBUSY;
+	}
+
+	ctrl_dev->opened = 1;
+	fp->private_data = dev;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+
+int usb_rmnet_mux_ctrl_release(struct inode *inode, struct file *fp)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ctrl_dev->opened = 0;
+	fp->private_data = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+ssize_t usb_rmnet_mux_ctrl_read(struct file *fp,
+		      char __user *buf,
+		      size_t count,
+		      loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_ctrl_pkt *cpkt;
+	unsigned long flags;
+	int ret = 0;
+
+ctrl_read:
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (list_empty(&ctrl_dev->tx_q)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		/* Implement sleep and wakeup here */
+		ret = wait_event_interruptible(ctrl_dev->tx_wait_q,
+					!list_empty(&ctrl_dev->tx_q) ||
+					!atomic_read(&dev->online));
+		if (ret < 0)
+			return ret;
+
+		goto ctrl_read;
+	}
+
+	cpkt = list_first_entry(&ctrl_dev->tx_q, struct rmnet_mux_ctrl_pkt,
+							list);
+	if (cpkt->len > count) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		pr_err("%s: cpkt size:%d > buf size:%d\n",
+				__func__, cpkt->len, count);
+		return -ENOMEM;
+	}
+	list_del(&cpkt->list);
+	ctrl_dev->tx_len--;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	count = cpkt->len;
+
+	ret = copy_to_user(buf, cpkt->buf, count);
+	dev->cpkts_tomdm++;
+
+	rmnet_mux_free_ctrl_pkt(cpkt);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+ssize_t usb_rmnet_mux_ctrl_write(struct file *fp,
+		       const char __user *buf,
+		       size_t count,
+		       loff_t *ppos)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	struct rmnet_mux_ctrl_pkt *cpkt;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!atomic_read(&dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("%s: zero length ctrl pkt\n", __func__);
+		return -ENODEV;
+	}
+
+	if (count > MAX_CTRL_PKT_SIZE) {
+		pr_err("%s: max_pkt_size:%d given_pkt_size:%d\n",
+				__func__, MAX_CTRL_PKT_SIZE, count);
+		return -ENOMEM;
+	}
+
+	cpkt = rmnet_mux_alloc_ctrl_pkt(count, GFP_KERNEL);
+	if (!cpkt) {
+		pr_err("%s: cannot allocate rmnet_mux ctrl pkt\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = copy_from_user(cpkt->buf, buf, count);
+	if (ret) {
+		pr_err("%s: copy_from_user failed err:%d\n",
+				__func__, ret);
+		rmnet_mux_free_ctrl_pkt(cpkt);
+		return ret;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ctrl_dev->rx_len++;
+	list_add(&cpkt->list, &ctrl_dev->rx_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ctrl_response_available(dev);
+
+	return count;
+}
+
+
+#define RMNET_MUX_CTRL_GET_DTR	_IOR(0xFE, 0, int)
+static long
+usb_rmnet_mux_ctrl_ioctl(struct file *fp, unsigned c, unsigned long value)
+{
+	struct rmnet_mux_dev *dev = fp->private_data;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+	unsigned long *temp = (unsigned long *)value;
+	int ret = 0;
+
+	if (c != RMNET_MUX_CTRL_GET_DTR)
+		return -ENODEV;
+
+	ret = copy_to_user(temp,
+			&ctrl_dev->cbits_to_modem,
+			sizeof(*temp));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct file_operations rmnet_mux_ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.open		= usb_rmnet_mux_ctrl_open,
+	.release	= usb_rmnet_mux_ctrl_release,
+	.read		= usb_rmnet_mux_ctrl_read,
+	.write		= usb_rmnet_mux_ctrl_write,
+	.unlocked_ioctl	= usb_rmnet_mux_ctrl_ioctl,
+};
+
+static struct miscdevice rmnet_mux_ctrl_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "rmnet_mux_ctrl",
+	.fops = &rmnet_mux_ctrl_fops,
+};
+
+static int rmnet_mux_ctrl_device_init(struct rmnet_mux_dev *dev)
+{
+	int ret;
+	struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	INIT_LIST_HEAD(&ctrl_dev->tx_q);
+	INIT_LIST_HEAD(&ctrl_dev->rx_q);
+	init_waitqueue_head(&ctrl_dev->tx_wait_q);
+
+	ret = misc_register(&rmnet_mux_ctrl_dev);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rmnet_smd_sdio_function_add(struct usb_configuration *c)
+{
+	struct rmnet_mux_dev *dev = rmux_dev;
+
+	if (!dev)
+		return -ENODEV;
+
+	pr_debug("rmnet_smd_sdio_function_add\n");
+
+	dev->function.name = "rmnet_smd_sdio";
+	dev->function.strings = rmnet_mux_strings;
+	dev->function.descriptors = rmnet_mux_fs_function;
+	dev->function.hs_descriptors = rmnet_mux_hs_function;
+	dev->function.bind = rmnet_mux_bind;
+	dev->function.unbind = rmnet_mux_unbind;
+	dev->function.setup = rmnet_mux_setup;
+	dev->function.set_alt = rmnet_mux_set_alt;
+	dev->function.disable = rmnet_mux_disable;
+	dev->function.suspend = rmnet_mux_suspend;
+
+	return usb_add_function(c, &dev->function);
+}
+
+static int rmnet_smd_sdio_init(void)
+{
+	struct rmnet_mux_dev *dev;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	rmux_dev = dev;
+
+	dev->wq = create_singlethread_workqueue("k_rmnet_mux_work");
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->notify_count, 0);
+	atomic_set(&dev->online, 0);
+	INIT_WORK(&dev->disconnect_work, rmnet_mux_disconnect_work);
+	rmnet_mux_smd_init(&dev->smd_dev);
+	rmnet_mux_sdio_init(&dev->sdio_dev);
+
+	ret = rmnet_mux_ctrl_device_init(dev);
+	if (ret) {
+		pr_debug("%s: rmnet_mux_ctrl_device_init failed, err:%d\n",
+				__func__, ret);
+		goto free_wq;
+	}
+
+	rmnet_mux_debugfs_init(dev);
+
+	return 0;
+
+free_wq:
+	destroy_workqueue(dev->wq);
+free_dev:
+	kfree(dev);
+
+	return ret;
+}
+
+static void rmnet_smd_sdio_cleanup(void)
+{
+	struct rmnet_mux_dev *dev = rmux_dev;
+	struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
+
+	debugfs_remove_recursive(dent_rmnet_mux);
+	misc_deregister(&rmnet_mux_ctrl_dev);
+	smd_close(smd_dev->smd_data.ch);
+	destroy_workqueue(dev->wq);
+	kfree(dev);
+}
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 1413df9..6e807cb 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -869,14 +869,14 @@
 	if (!can_support_rndis(c) || !ethaddr)
 		return -EINVAL;
 
+	/* setup RNDIS itself */
+	status = rndis_init();
+	if (status < 0)
+		return status;
+
 	/* maybe allocate device-global string IDs */
 	if (rndis_string_defs[0].id == 0) {
 
-		/* ... and setup RNDIS itself */
-		status = rndis_init();
-		if (status < 0)
-			return status;
-
 		/* control interface label */
 		status = usb_string_id(c->cdev);
 		if (status < 0)
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 07197d6..7b6acc6 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <mach/usb_gadget_xport.h>
 
 #include "u_serial.h"
 #include "gadget_chips.h"
@@ -26,74 +27,199 @@
  * CDC ACM driver.  However, for many purposes it's just as functional
  * if you can arrange appropriate host side drivers.
  */
+#define GSERIAL_NO_PORTS 3
 
 struct f_gser {
 	struct gserial			port;
 	u8				data_id;
 	u8				port_num;
+
+	u8				online;
+	enum transport_type		transport;
+
+#ifdef CONFIG_MODEM_SUPPORT
+	u8				pending;
+	spinlock_t			lock;
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+
+	struct usb_cdc_line_coding	port_line_coding;
+
+	/* SetControlLineState request */
+	u16				port_handshake_bits;
+#define ACM_CTRL_RTS	(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR	(1 << 0)	/* host is ready for data r/w */
+
+	/* SerialState notification */
+	u16				serial_state;
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+#endif
 };
 
+static unsigned int no_tty_ports;
+static unsigned int no_sdio_ports;
+static unsigned int no_smd_ports;
+static unsigned int no_hsic_sports;
+static unsigned int no_hsuart_sports;
+static unsigned int nr_ports;
+
+static struct port_info {
+	enum transport_type	transport;
+	unsigned		port_num;
+	unsigned		client_port_num;
+} gserial_ports[GSERIAL_NO_PORTS];
+
+static inline bool is_transport_sdio(enum transport_type t)
+{
+	if (t == USB_GADGET_XPORT_SDIO)
+		return 1;
+	return 0;
+}
+
 static inline struct f_gser *func_to_gser(struct usb_function *f)
 {
 	return container_of(f, struct f_gser, port.func);
 }
 
+#ifdef CONFIG_MODEM_SUPPORT
+static inline struct f_gser *port_to_gser(struct gserial *p)
+{
+	return container_of(p, struct f_gser, port);
+}
+#define GS_LOG2_NOTIFY_INTERVAL		5	/* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET		10	/* notification + 2 bytes */
+#endif
 /*-------------------------------------------------------------------------*/
 
 /* interface descriptor: */
 
-static struct usb_interface_descriptor gser_interface_desc __initdata = {
+static struct usb_interface_descriptor gser_interface_desc = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	/* .bInterfaceNumber = DYNAMIC */
+#ifdef CONFIG_MODEM_SUPPORT
+	.bNumEndpoints =	3,
+#else
 	.bNumEndpoints =	2,
+#endif
 	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
 	.bInterfaceSubClass =	0,
 	.bInterfaceProtocol =	0,
 	/* .iInterface = DYNAMIC */
 };
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_cdc_header_desc gser_header_desc  = {
+	.bLength =		sizeof(gser_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+	.bcdCDC =		__constant_cpu_to_le16(0x0110),
+};
 
+static struct usb_cdc_call_mgmt_descriptor
+gser_call_mgmt_descriptor  = {
+	.bLength =		sizeof(gser_call_mgmt_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
+	.bmCapabilities =	0,
+	/* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor gser_descriptor  = {
+	.bLength =		sizeof(gser_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
+	.bmCapabilities =	USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc gser_union_desc  = {
+	.bLength =		sizeof(gser_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+#endif
 /* full speed support: */
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_endpoint_descriptor gser_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+#endif
 
-static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_fs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_fs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *gser_fs_function[] __initdata = {
+static struct usb_descriptor_header *gser_fs_function[] = {
 	(struct usb_descriptor_header *) &gser_interface_desc,
+#ifdef CONFIG_MODEM_SUPPORT
+	(struct usb_descriptor_header *) &gser_header_desc,
+	(struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &gser_descriptor,
+	(struct usb_descriptor_header *) &gser_union_desc,
+	(struct usb_descriptor_header *) &gser_fs_notify_desc,
+#endif
 	(struct usb_descriptor_header *) &gser_fs_in_desc,
 	(struct usb_descriptor_header *) &gser_fs_out_desc,
 	NULL,
 };
 
 /* high speed support: */
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_endpoint_descriptor gser_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
+};
+#endif
 
-static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_hs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	cpu_to_le16(512),
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_hs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	cpu_to_le16(512),
+	.wMaxPacketSize =	__constant_cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *gser_hs_function[] __initdata = {
+static struct usb_descriptor_header *gser_hs_function[] = {
 	(struct usb_descriptor_header *) &gser_interface_desc,
+#ifdef CONFIG_MODEM_SUPPORT
+	(struct usb_descriptor_header *) &gser_header_desc,
+	(struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &gser_descriptor,
+	(struct usb_descriptor_header *) &gser_union_desc,
+	(struct usb_descriptor_header *) &gser_hs_notify_desc,
+#endif
 	(struct usb_descriptor_header *) &gser_hs_in_desc,
 	(struct usb_descriptor_header *) &gser_hs_out_desc,
 	NULL,
@@ -144,18 +270,279 @@
 	NULL,
 };
 
+static int gport_setup(struct usb_configuration *c)
+{
+	int ret = 0;
+	int port_idx;
+	int i;
+
+	pr_debug("%s: no_tty_ports: %u no_sdio_ports: %u"
+		" no_smd_ports: %u no_hsic_sports: %u no_hsuart_ports: %u nr_ports: %u\n",
+			__func__, no_tty_ports, no_sdio_ports, no_smd_ports,
+			no_hsic_sports, no_hsuart_sports, nr_ports);
+
+	if (no_tty_ports)
+		ret = gserial_setup(c->cdev->gadget, no_tty_ports);
+	if (no_sdio_ports)
+		ret = gsdio_setup(c->cdev->gadget, no_sdio_ports);
+	if (no_smd_ports)
+		ret = gsmd_setup(c->cdev->gadget, no_smd_ports);
+	if (no_hsic_sports) {
+		port_idx = ghsic_data_setup(no_hsic_sports, USB_GADGET_SERIAL);
+		if (port_idx < 0)
+			return port_idx;
+
+		for (i = 0; i < nr_ports; i++) {
+			if (gserial_ports[i].transport ==
+					USB_GADGET_XPORT_HSIC) {
+				gserial_ports[i].client_port_num = port_idx;
+				port_idx++;
+			}
+		}
+
+		/*clinet port num is same for data setup and ctrl setup*/
+		ret = ghsic_ctrl_setup(no_hsic_sports, USB_GADGET_SERIAL);
+		if (ret < 0)
+			return ret;
+		return 0;
+	}
+	if (no_hsuart_sports) {
+		port_idx = ghsuart_data_setup(no_hsuart_sports,
+					USB_GADGET_SERIAL);
+		if (port_idx < 0)
+			return port_idx;
+
+		for (i = 0; i < nr_ports; i++) {
+			if (gserial_ports[i].transport ==
+					USB_GADGET_XPORT_HSUART) {
+				gserial_ports[i].client_port_num = port_idx;
+				port_idx++;
+			}
+		}
+
+		return 0;
+	}
+	return ret;
+}
+
+static int gport_connect(struct f_gser *gser)
+{
+	unsigned	port_num;
+	int		ret;
+
+	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
+			__func__, xport_to_str(gser->transport),
+			gser, &gser->port, gser->port_num);
+
+	port_num = gserial_ports[gser->port_num].client_port_num;
+
+	switch (gser->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_connect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_connect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_connect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ret = ghsic_ctrl_connect(&gser->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		ret = ghsic_data_connect(&gser->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsic_data_connect failed: err:%d\n",
+					__func__, ret);
+			ghsic_ctrl_disconnect(&gser->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ret = ghsuart_data_connect(&gser->port, port_num);
+		if (ret) {
+			pr_err("%s: ghsuart_data_connect failed: err:%d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(gser->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int gport_disconnect(struct f_gser *gser)
+{
+	unsigned port_num;
+
+	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
+			__func__, xport_to_str(gser->transport),
+			gser, &gser->port, gser->port_num);
+
+	port_num = gserial_ports[gser->port_num].client_port_num;
+
+	switch (gser->transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_disconnect(&gser->port);
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gsdio_disconnect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gsmd_disconnect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		ghsic_ctrl_disconnect(&gser->port, port_num);
+		ghsic_data_disconnect(&gser->port, port_num);
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		ghsuart_data_disconnect(&gser->port, port_num);
+		break;
+	default:
+		pr_err("%s: Un-supported transport:%s\n", __func__,
+				xport_to_str(gser->transport));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_MODEM_SUPPORT
+static void gser_complete_set_line_coding(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct f_gser            *gser = ep->driver_data;
+	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+	if (req->status != 0) {
+		DBG(cdev, "gser ttyGS%d completion, err %d\n",
+				gser->port_num, req->status);
+		return;
+	}
+
+	/* normal completion */
+	if (req->actual != sizeof(gser->port_line_coding)) {
+		DBG(cdev, "gser ttyGS%d short resp, len %d\n",
+				gser->port_num, req->actual);
+		usb_ep_set_halt(ep);
+	} else {
+		struct usb_cdc_line_coding	*value = req->buf;
+		gser->port_line_coding = *value;
+	}
+}
 /*-------------------------------------------------------------------------*/
 
+static int
+gser_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_gser            *gser = func_to_gser(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	 *req = cdev->req;
+	int			 value = -EOPNOTSUPP;
+	u16			 w_index = le16_to_cpu(ctrl->wIndex);
+	u16			 w_value = le16_to_cpu(ctrl->wValue);
+	u16			 w_length = le16_to_cpu(ctrl->wLength);
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	/* SET_LINE_CODING ... just read and save what the host sends */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_LINE_CODING:
+		if (w_length != sizeof(struct usb_cdc_line_coding))
+			goto invalid;
+
+		value = w_length;
+		cdev->gadget->ep0->driver_data = gser;
+		req->complete = gser_complete_set_line_coding;
+		break;
+
+	/* GET_LINE_CODING ... return what host sent, or initial value */
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_GET_LINE_CODING:
+		value = min_t(unsigned, w_length,
+				sizeof(struct usb_cdc_line_coding));
+		memcpy(req->buf, &gser->port_line_coding, value);
+		break;
+
+	/* SET_CONTROL_LINE_STATE ... save what the host sent */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+
+		value = 0;
+		gser->port_handshake_bits = w_value;
+		if (gser->port.notify_modem) {
+			unsigned port_num =
+				gserial_ports[gser->port_num].client_port_num;
+
+			gser->port.notify_modem(&gser->port,
+					port_num, w_value);
+		}
+		break;
+
+	default:
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "gser ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+			gser->port_num, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "gser response on ttyGS%d, err %d\n",
+					gser->port_num, value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+#endif
 static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct f_gser		*gser = func_to_gser(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
+	int rc = 0;
 
 	/* we know alt == 0, so this is an activation or a reset */
 
+#ifdef CONFIG_MODEM_SUPPORT
+	if (gser->notify->driver_data) {
+		DBG(cdev, "reset generic ctl ttyGS%d\n", gser->port_num);
+		usb_ep_disable(gser->notify);
+	}
+
+	if (!gser->notify->desc) {
+		if (config_ep_by_speed(cdev->gadget, f, gser->notify)) {
+			gser->notify->desc = NULL;
+			return -EINVAL;
+		}
+	}
+	rc = usb_ep_enable(gser->notify);
+
+	if (rc) {
+		ERROR(cdev, "can't enable %s, result %d\n",
+					gser->notify->name, rc);
+		return rc;
+	}
+	gser->notify->driver_data = gser;
+#endif
+
 	if (gser->port.in->driver_data) {
-		DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
-		gserial_disconnect(&gser->port);
+		DBG(cdev, "reset generic data ttyGS%d\n", gser->port_num);
+		gport_disconnect(gser);
 	}
 	if (!gser->port.in->desc || !gser->port.out->desc) {
 		DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
@@ -166,8 +553,11 @@
 			return -EINVAL;
 		}
 	}
-	gserial_connect(&gser->port, gser->port_num);
-	return 0;
+
+	gport_connect(gser);
+
+	gser->online = 1;
+	return rc;
 }
 
 static void gser_disable(struct usb_function *f)
@@ -176,14 +566,185 @@
 	struct usb_composite_dev *cdev = f->config->cdev;
 
 	DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
-	gserial_disconnect(&gser->port);
+
+	gport_disconnect(gser);
+
+#ifdef CONFIG_MODEM_SUPPORT
+	usb_ep_fifo_flush(gser->notify);
+	usb_ep_disable(gser->notify);
+#endif
+	gser->online = 0;
+}
+#ifdef CONFIG_MODEM_SUPPORT
+static int gser_notify(struct f_gser *gser, u8 type, u16 value,
+		void *data, unsigned length)
+{
+	struct usb_ep			*ep = gser->notify;
+	struct usb_request		*req;
+	struct usb_cdc_notification	*notify;
+	const unsigned			len = sizeof(*notify) + length;
+	void				*buf;
+	int				status;
+	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+	req = gser->notify_req;
+	gser->notify_req = NULL;
+	gser->pending = false;
+
+	req->length = len;
+	notify = req->buf;
+	buf = notify + 1;
+
+	notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	notify->bNotificationType = type;
+	notify->wValue = cpu_to_le16(value);
+	notify->wIndex = cpu_to_le16(gser->data_id);
+	notify->wLength = cpu_to_le16(length);
+	memcpy(buf, data, length);
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status < 0) {
+		ERROR(cdev, "gser ttyGS%d can't notify serial state, %d\n",
+				gser->port_num, status);
+		gser->notify_req = req;
+	}
+
+	return status;
 }
 
+static int gser_notify_serial_state(struct f_gser *gser)
+{
+	int			 status;
+	unsigned long flags;
+	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+	spin_lock_irqsave(&gser->lock, flags);
+	if (gser->notify_req) {
+		DBG(cdev, "gser ttyGS%d serial state %04x\n",
+				gser->port_num, gser->serial_state);
+		status = gser_notify(gser, USB_CDC_NOTIFY_SERIAL_STATE,
+				0, &gser->serial_state,
+					sizeof(gser->serial_state));
+	} else {
+		gser->pending = true;
+		status = 0;
+	}
+	spin_unlock_irqrestore(&gser->lock, flags);
+	return status;
+}
+
+static void gser_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_gser *gser = req->context;
+	u8	      doit = false;
+	unsigned long flags;
+
+	/* on this call path we do NOT hold the port spinlock,
+	 * which is why ACM needs its own spinlock
+	 */
+	spin_lock_irqsave(&gser->lock, flags);
+	if (req->status != -ESHUTDOWN)
+		doit = gser->pending;
+	gser->notify_req = req;
+	spin_unlock_irqrestore(&gser->lock, flags);
+
+	if (doit && gser->online)
+		gser_notify_serial_state(gser);
+}
+static void gser_connect(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	gser->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+	gser_notify_serial_state(gser);
+}
+
+unsigned int gser_get_dtr(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	if (gser->port_handshake_bits & ACM_CTRL_DTR)
+		return 1;
+	else
+		return 0;
+}
+
+unsigned int gser_get_rts(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	if (gser->port_handshake_bits & ACM_CTRL_RTS)
+		return 1;
+	else
+		return 0;
+}
+
+unsigned int gser_send_carrier_detect(struct gserial *port, unsigned int yes)
+{
+	struct f_gser *gser = port_to_gser(port);
+	u16			state;
+
+	state = gser->serial_state;
+	state &= ~ACM_CTRL_DCD;
+	if (yes)
+		state |= ACM_CTRL_DCD;
+
+	gser->serial_state = state;
+	return gser_notify_serial_state(gser);
+
+}
+
+unsigned int gser_send_ring_indicator(struct gserial *port, unsigned int yes)
+{
+	struct f_gser *gser = port_to_gser(port);
+	u16			state;
+
+	state = gser->serial_state;
+	state &= ~ACM_CTRL_RI;
+	if (yes)
+		state |= ACM_CTRL_RI;
+
+	gser->serial_state = state;
+	return gser_notify_serial_state(gser);
+
+}
+static void gser_disconnect(struct gserial *port)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	gser->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+	gser_notify_serial_state(gser);
+}
+
+static int gser_send_break(struct gserial *port, int duration)
+{
+	struct f_gser *gser = port_to_gser(port);
+	u16			state;
+
+	state = gser->serial_state;
+	state &= ~ACM_CTRL_BRK;
+	if (duration)
+		state |= ACM_CTRL_BRK;
+
+	gser->serial_state = state;
+	return gser_notify_serial_state(gser);
+}
+
+static int gser_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
+{
+	struct f_gser *gser = port_to_gser(port);
+
+	gser->serial_state = ctrl_bits;
+
+	return gser_notify_serial_state(gser);
+}
+#endif
 /*-------------------------------------------------------------------------*/
 
 /* serial function driver setup/binding */
 
-static int __init
+static int
 gser_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -213,9 +774,29 @@
 	gser->port.out = ep;
 	ep->driver_data = cdev;	/* claim */
 
+#ifdef CONFIG_MODEM_SUPPORT
+	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_notify_desc);
+	if (!ep)
+		goto fail;
+	gser->notify = ep;
+	ep->driver_data = cdev;	/* claim */
+	/* allocate notification */
+	gser->notify_req = gs_alloc_req(ep,
+			sizeof(struct usb_cdc_notification) + 2,
+			GFP_KERNEL);
+	if (!gser->notify_req)
+		goto fail;
+
+	gser->notify_req->complete = gser_notify_complete;
+	gser->notify_req->context = gser;
+#endif
+
 	/* copy descriptors, and track endpoint copies */
 	f->descriptors = usb_copy_descriptors(gser_fs_function);
 
+	if (!f->descriptors)
+		goto fail;
+
 	/* support all relevant hardware speeds... we expect that when
 	 * hardware is dual speed, all bulk-capable endpoints work at
 	 * both speeds
@@ -225,9 +806,17 @@
 				gser_fs_in_desc.bEndpointAddress;
 		gser_hs_out_desc.bEndpointAddress =
 				gser_fs_out_desc.bEndpointAddress;
+#ifdef CONFIG_MODEM_SUPPORT
+		gser_hs_notify_desc.bEndpointAddress =
+				gser_fs_notify_desc.bEndpointAddress;
+#endif
 
 		/* copy descriptors, and track endpoint copies */
 		f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
+
+		if (!f->hs_descriptors)
+			goto fail;
+
 	}
 	if (gadget_is_superspeed(c->cdev->gadget)) {
 		gser_ss_in_desc.bEndpointAddress =
@@ -249,6 +838,16 @@
 	return 0;
 
 fail:
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+#ifdef CONFIG_MODEM_SUPPORT
+	if (gser->notify_req)
+		gs_free_req(gser->notify, gser->notify_req);
+
+	/* we might as well release our claims on endpoints */
+	if (gser->notify)
+		gser->notify->driver_data = NULL;
+#endif
 	/* we might as well release our claims on endpoints */
 	if (gser->port.out)
 		gser->port.out->driver_data = NULL;
@@ -263,11 +862,17 @@
 static void
 gser_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+#ifdef CONFIG_MODEM_SUPPORT
+	struct f_gser *gser = func_to_gser(f);
+#endif
 	if (gadget_is_dualspeed(c->cdev->gadget))
 		usb_free_descriptors(f->hs_descriptors);
 	if (gadget_is_superspeed(c->cdev->gadget))
 		usb_free_descriptors(f->ss_descriptors);
 	usb_free_descriptors(f->descriptors);
+#ifdef CONFIG_MODEM_SUPPORT
+	gs_free_req(gser->notify, gser->notify_req);
+#endif
 	kfree(func_to_gser(f));
 }
 
@@ -283,7 +888,7 @@
  * handle all the ones it binds.  Caller is also responsible
  * for calling @gserial_cleanup() before module unload.
  */
-int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
+int gser_bind_config(struct usb_configuration *c, u8 port_num)
 {
 	struct f_gser	*gser;
 	int		status;
@@ -305,6 +910,9 @@
 	if (!gser)
 		return -ENOMEM;
 
+#ifdef CONFIG_MODEM_SUPPORT
+	spin_lock_init(&gser->lock);
+#endif
 	gser->port_num = port_num;
 
 	gser->port.func.name = "gser";
@@ -313,9 +921,77 @@
 	gser->port.func.unbind = gser_unbind;
 	gser->port.func.set_alt = gser_set_alt;
 	gser->port.func.disable = gser_disable;
+	gser->transport		= gserial_ports[port_num].transport;
+#ifdef CONFIG_MODEM_SUPPORT
+	/* We support only three ports for now */
+	if (port_num == 0)
+		gser->port.func.name = "modem";
+	else if (port_num == 1)
+		gser->port.func.name = "nmea";
+	else
+		gser->port.func.name = "modem2";
+	gser->port.func.setup = gser_setup;
+	gser->port.connect = gser_connect;
+	gser->port.get_dtr = gser_get_dtr;
+	gser->port.get_rts = gser_get_rts;
+	gser->port.send_carrier_detect = gser_send_carrier_detect;
+	gser->port.send_ring_indicator = gser_send_ring_indicator;
+	gser->port.send_modem_ctrl_bits = gser_send_modem_ctrl_bits;
+	gser->port.disconnect = gser_disconnect;
+	gser->port.send_break = gser_send_break;
+#endif
 
 	status = usb_add_function(c, &gser->port.func);
 	if (status)
 		kfree(gser);
 	return status;
 }
+
+/**
+ * gserial_init_port - bind a gserial_port to its transport
+ */
+static int gserial_init_port(int port_num, const char *name)
+{
+	enum transport_type transport;
+
+	if (port_num >= GSERIAL_NO_PORTS)
+		return -ENODEV;
+
+	transport = str_to_xport(name);
+	pr_debug("%s, port:%d, transport:%s\n", __func__,
+			port_num, xport_to_str(transport));
+
+	gserial_ports[port_num].transport = transport;
+	gserial_ports[port_num].port_num = port_num;
+
+	switch (transport) {
+	case USB_GADGET_XPORT_TTY:
+		gserial_ports[port_num].client_port_num = no_tty_ports;
+		no_tty_ports++;
+		break;
+	case USB_GADGET_XPORT_SDIO:
+		gserial_ports[port_num].client_port_num = no_sdio_ports;
+		no_sdio_ports++;
+		break;
+	case USB_GADGET_XPORT_SMD:
+		gserial_ports[port_num].client_port_num = no_smd_ports;
+		no_smd_ports++;
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		/*client port number will be updated in gport_setup*/
+		no_hsic_sports++;
+		break;
+	case USB_GADGET_XPORT_HSUART:
+		/*client port number will be updated in gport_setup*/
+		no_hsuart_sports++;
+		break;
+	default:
+		pr_err("%s: Un-supported transport transport: %u\n",
+				__func__, gserial_ports[port_num].transport);
+		return -ENODEV;
+	}
+
+	nr_ports++;
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index a8855d0..14873a0 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -29,6 +29,7 @@
 #define gadget_is_at91(g)		(!strcmp("at91_udc", (g)->name))
 #define gadget_is_atmel_usba(g)		(!strcmp("atmel_usba_udc", (g)->name))
 #define gadget_is_ci13xxx_msm(g)	(!strcmp("ci13xxx_msm", (g)->name))
+#define gadget_is_ci13xxx_msm_hsic(g)	(!strcmp("ci13xxx_msm_hsic", (g)->name))
 #define gadget_is_ci13xxx_pci(g)	(!strcmp("ci13xxx_pci", (g)->name))
 #define gadget_is_dummy(g)		(!strcmp("dummy_udc", (g)->name))
 #define gadget_is_dwc3(g)		(!strcmp("dwc3-gadget", (g)->name))
@@ -38,6 +39,7 @@
 #define gadget_is_imx(g)		(!strcmp("imx_udc", (g)->name))
 #define gadget_is_langwell(g)		(!strcmp("langwell_udc", (g)->name))
 #define gadget_is_m66592(g)		(!strcmp("m66592_udc", (g)->name))
+#define gadget_is_msm72k(g)		(!strcmp("msm72k_udc", (g)->name))
 #define gadget_is_musbhdrc(g)		(!strcmp("musb-hdrc", (g)->name))
 #define gadget_is_net2272(g)		(!strcmp("net2272", (g)->name))
 #define gadget_is_net2280(g)		(!strcmp("net2280", (g)->name))
@@ -118,6 +120,10 @@
 		return 0x31;
 	else if (gadget_is_dwc3(gadget))
 		return 0x32;
+	else if (gadget_is_msm72k(gadget))
+		return 0x33;
+	else if (gadget_is_ci13xxx_msm_hsic(gadget))
+		return 0x34;
 
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
new file mode 100644
index 0000000..a025d95
--- /dev/null
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -0,0 +1,2834 @@
+/*
+ * Driver for HighSpeed USB Client Controller in MSM7K
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *         Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/switch.h>
+#include <linux/pm_runtime.h>
+
+#include <mach/msm72k_otg.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board.h>
+#include <mach/msm_hsusb.h>
+#include <linux/device.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/clk.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+
+static const char driver_name[] = "msm72k_udc";
+
+/* #define DEBUG */
+/* #define VERBOSE */
+
+#define MSM_USB_BASE ((unsigned) ui->addr)
+
+#define	DRIVER_DESC		"MSM 72K USB Peripheral Controller"
+#define	DRIVER_NAME		"MSM72K_UDC"
+
+#define EPT_FLAG_IN        0x0001
+
+#define SETUP_BUF_SIZE     8
+
+
+static const char *const ep_name[] = {
+	"ep0out", "ep1out", "ep2out", "ep3out",
+	"ep4out", "ep5out", "ep6out", "ep7out",
+	"ep8out", "ep9out", "ep10out", "ep11out",
+	"ep12out", "ep13out", "ep14out", "ep15out",
+	"ep0in", "ep1in", "ep2in", "ep3in",
+	"ep4in", "ep5in", "ep6in", "ep7in",
+	"ep8in", "ep9in", "ep10in", "ep11in",
+	"ep12in", "ep13in", "ep14in", "ep15in"
+};
+
+/*To release the wakelock from debugfs*/
+static int release_wlocks;
+
+struct msm_request {
+	struct usb_request req;
+
+	/* saved copy of req.complete */
+	void	(*gadget_complete)(struct usb_ep *ep,
+					struct usb_request *req);
+
+
+	struct usb_info *ui;
+	struct msm_request *next;
+	struct msm_request *prev;
+
+	unsigned busy:1;
+	unsigned live:1;
+	unsigned alloced:1;
+
+	dma_addr_t dma;
+	dma_addr_t item_dma;
+
+	struct ept_queue_item *item;
+};
+
+#define to_msm_request(r) container_of(r, struct msm_request, req)
+#define to_msm_endpoint(r) container_of(r, struct msm_endpoint, ep)
+#define to_msm_otg(xceiv)  container_of(xceiv, struct msm_otg, phy)
+#define is_b_sess_vld()	((OTGSC_BSV & readl(USB_OTGSC)) ? 1 : 0)
+#define is_usb_online(ui) (ui->usb_state != USB_STATE_NOTATTACHED)
+
+struct msm_endpoint {
+	struct usb_ep ep;
+	struct usb_info *ui;
+	struct msm_request *req; /* head of pending requests */
+	struct msm_request *last;
+	unsigned flags;
+
+	/* bit number (0-31) in various status registers
+	** as well as the index into the usb_info's array
+	** of all endpoints
+	*/
+	unsigned char bit;
+	unsigned char num;
+	unsigned long dTD_update_fail_count;
+	unsigned long false_prime_fail_count;
+	unsigned actual_prime_fail_count;
+
+	unsigned wedged:1;
+	/* pointers to DMA transfer list area */
+	/* these are allocated from the usb_info dma space */
+	struct ept_queue_head *head;
+	struct timer_list prime_timer;
+};
+
+/* PHY status check timer to monitor phy stuck up on reset */
+static struct timer_list phy_status_timer;
+
+static void ept_prime_timer_func(unsigned long data);
+static void usb_do_work(struct work_struct *w);
+static void usb_do_remote_wakeup(struct work_struct *w);
+
+
+#define USB_STATE_IDLE    0
+#define USB_STATE_ONLINE  1
+#define USB_STATE_OFFLINE 2
+
+#define USB_FLAG_START          0x0001
+#define USB_FLAG_VBUS_ONLINE    0x0002
+#define USB_FLAG_VBUS_OFFLINE   0x0004
+#define USB_FLAG_RESET          0x0008
+#define USB_FLAG_SUSPEND        0x0010
+#define USB_FLAG_CONFIGURED     0x0020
+
+#define USB_CHG_DET_DELAY	msecs_to_jiffies(1000)
+#define REMOTE_WAKEUP_DELAY	msecs_to_jiffies(1000)
+#define PHY_STATUS_CHECK_DELAY	(jiffies + msecs_to_jiffies(1000))
+#define EPT_PRIME_CHECK_DELAY	(jiffies + msecs_to_jiffies(1000))
+
+struct usb_info {
+	/* lock for register/queue/device state changes */
+	spinlock_t lock;
+
+	/* single request used for handling setup transactions */
+	struct usb_request *setup_req;
+
+	struct platform_device *pdev;
+	int irq;
+	void *addr;
+
+	unsigned state;
+	unsigned flags;
+
+	atomic_t configured;
+	atomic_t running;
+
+	struct dma_pool *pool;
+
+	/* dma page to back the queue heads and items */
+	unsigned char *buf;
+	dma_addr_t dma;
+
+	struct ept_queue_head *head;
+
+	/* used for allocation */
+	unsigned next_item;
+	unsigned next_ifc_num;
+
+	/* endpoints are ordered based on their status bits,
+	** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15
+	*/
+	struct msm_endpoint ept[32];
+
+
+	/* max power requested by selected configuration */
+	unsigned b_max_pow;
+	unsigned chg_current;
+	struct delayed_work chg_det;
+	struct delayed_work chg_stop;
+	struct msm_hsusb_gadget_platform_data *pdata;
+	struct work_struct phy_status_check;
+
+	struct work_struct work;
+	unsigned phy_status;
+	unsigned phy_fail_count;
+	unsigned prime_fail_count;
+	unsigned long dTD_update_fail_count;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct switch_dev sdev;
+
+#define ep0out ept[0]
+#define ep0in  ept[16]
+
+	atomic_t ep0_dir;
+	atomic_t test_mode;
+	atomic_t offline_pending;
+	atomic_t softconnect;
+#ifdef CONFIG_USB_OTG
+	u8 hnp_avail;
+#endif
+
+	atomic_t remote_wakeup;
+	atomic_t self_powered;
+	struct delayed_work rw_work;
+
+	struct usb_phy *xceiv;
+	enum usb_device_state usb_state;
+	struct wake_lock	wlock;
+};
+
+static const struct usb_ep_ops msm72k_ep_ops;
+static struct usb_info *the_usb_info;
+
+static int msm72k_wakeup(struct usb_gadget *_gadget);
+static int msm72k_pullup_internal(struct usb_gadget *_gadget, int is_active);
+static int msm72k_set_halt(struct usb_ep *_ep, int value);
+static void flush_endpoint(struct msm_endpoint *ept);
+static void usb_reset(struct usb_info *ui);
+static int usb_ept_set_halt(struct usb_ep *_ep, int value);
+
+static void msm_hsusb_set_speed(struct usb_info *ui)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) {
+	case PORTSC_PSPD_FS:
+		dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_FULL\n");
+		ui->gadget.speed = USB_SPEED_FULL;
+		break;
+	case PORTSC_PSPD_LS:
+		dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_LOW\n");
+		ui->gadget.speed = USB_SPEED_LOW;
+		break;
+	case PORTSC_PSPD_HS:
+		dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_HIGH\n");
+		ui->gadget.speed = USB_SPEED_HIGH;
+		break;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void msm_hsusb_set_state(enum usb_device_state state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&the_usb_info->lock, flags);
+	the_usb_info->usb_state = state;
+	spin_unlock_irqrestore(&the_usb_info->lock, flags);
+}
+
+static enum usb_device_state msm_hsusb_get_state(void)
+{
+	unsigned long flags;
+	enum usb_device_state state;
+
+	spin_lock_irqsave(&the_usb_info->lock, flags);
+	state = the_usb_info->usb_state;
+	spin_unlock_irqrestore(&the_usb_info->lock, flags);
+
+	return state;
+}
+
+static ssize_t print_switch_name(struct switch_dev *sdev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", DRIVER_NAME);
+}
+
+static ssize_t print_switch_state(struct switch_dev *sdev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+		sdev->state ? "online" : "offline");
+}
+
+static inline enum chg_type usb_get_chg_type(struct usb_info *ui)
+{
+	if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
+		return USB_CHG_TYPE__WALLCHARGER;
+	else
+		return USB_CHG_TYPE__SDP;
+}
+
+#define USB_WALLCHARGER_CHG_CURRENT 1800
+static int usb_get_max_power(struct usb_info *ui)
+{
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long flags;
+	enum chg_type temp;
+	int suspended;
+	int configured;
+	unsigned bmaxpow;
+
+	if (ui->gadget.is_a_peripheral)
+		return -EINVAL;
+
+	temp = atomic_read(&otg->chg_type);
+	spin_lock_irqsave(&ui->lock, flags);
+	suspended = ui->usb_state == USB_STATE_SUSPENDED ? 1 : 0;
+	configured = atomic_read(&ui->configured);
+	bmaxpow = ui->b_max_pow;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	if (temp == USB_CHG_TYPE__INVALID)
+		return -ENODEV;
+
+	if (temp == USB_CHG_TYPE__WALLCHARGER)
+		return USB_WALLCHARGER_CHG_CURRENT;
+
+	if (suspended || !configured)
+		return 0;
+
+	return bmaxpow;
+}
+
+static int usb_phy_stuck_check(struct usb_info *ui)
+{
+	/*
+	 * write some value (0xAA) into scratch reg (0x16) and read it back,
+	 * If the read value is same as written value, means PHY is normal
+	 * otherwise, PHY seems to have stuck.
+	 */
+
+	if (usb_phy_io_write(ui->xceiv, 0xAA, 0x16) == -1) {
+		dev_dbg(&ui->pdev->dev,
+				"%s(): ulpi write timeout\n", __func__);
+		return -EIO;
+	}
+
+	if (usb_phy_io_read(ui->xceiv, 0x16) != 0xAA) {
+		dev_dbg(&ui->pdev->dev,
+				"%s(): read value is incorrect\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * This function checks the phy status by reading/writing to the
+ * phy scratch register. If the phy is stuck resets the HW
+ * */
+static void usb_phy_stuck_recover(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->gadget.speed != USB_SPEED_UNKNOWN ||
+			ui->usb_state == USB_STATE_NOTATTACHED ||
+			ui->driver == NULL) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	disable_irq(otg->irq);
+	if (usb_phy_stuck_check(ui)) {
+#ifdef CONFIG_USB_MSM_ACA
+		del_timer_sync(&otg->id_timer);
+#endif
+		ui->phy_fail_count++;
+		dev_err(&ui->pdev->dev,
+				"%s():PHY stuck, resetting HW\n", __func__);
+		/*
+		 * PHY seems to have stuck,
+		 * reset the PHY and HW link to recover the PHY
+		 */
+		usb_reset(ui);
+#ifdef CONFIG_USB_MSM_ACA
+		mod_timer(&otg->id_timer, jiffies +
+				 msecs_to_jiffies(OTG_ID_POLL_MS));
+#endif
+		msm72k_pullup_internal(&ui->gadget, 1);
+	}
+	enable_irq(otg->irq);
+}
+
+static void usb_phy_status_check_timer(unsigned long data)
+{
+	struct usb_info *ui = the_usb_info;
+
+	schedule_work(&ui->phy_status_check);
+}
+
+static void usb_chg_stop(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, chg_stop.work);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	enum chg_type temp;
+
+	temp = atomic_read(&otg->chg_type);
+
+	if (temp == USB_CHG_TYPE__SDP)
+		usb_phy_set_power(ui->xceiv, 0);
+}
+
+static void usb_chg_detect(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, chg_det.work);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	enum chg_type temp = USB_CHG_TYPE__INVALID;
+	unsigned long flags;
+	int maxpower;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->usb_state == USB_STATE_NOTATTACHED) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return;
+	}
+
+	temp = usb_get_chg_type(ui);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	atomic_set(&otg->chg_type, temp);
+	maxpower = usb_get_max_power(ui);
+	if (maxpower > 0)
+		usb_phy_set_power(ui->xceiv, maxpower);
+
+	/* USB driver prevents idle and suspend power collapse(pc)
+	 * while USB cable is connected. But when dedicated charger is
+	 * connected, driver can vote for idle and suspend pc.
+	 * OTG driver handles idle pc as part of above usb_phy_set_power call
+	 * when wallcharger is attached. To allow suspend pc, release the
+	 * wakelock which will be re-acquired for any sub-sequent usb interrupts
+	 * */
+	if (temp == USB_CHG_TYPE__WALLCHARGER) {
+		pm_runtime_put_sync(&ui->pdev->dev);
+		wake_unlock(&ui->wlock);
+	}
+}
+
+static int usb_ep_get_stall(struct msm_endpoint *ept)
+{
+	unsigned int n;
+	struct usb_info *ui = ept->ui;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+	if (ept->flags & EPT_FLAG_IN)
+		return (CTRL_TXS & n) ? 1 : 0;
+	else
+		return (CTRL_RXS & n) ? 1 : 0;
+}
+
+static void init_endpoints(struct usb_info *ui)
+{
+	unsigned n;
+
+	for (n = 0; n < 32; n++) {
+		struct msm_endpoint *ept = ui->ept + n;
+
+		ept->ui = ui;
+		ept->bit = n;
+		ept->num = n & 15;
+		ept->ep.name = ep_name[n];
+		ept->ep.ops = &msm72k_ep_ops;
+
+		if (ept->bit > 15) {
+			/* IN endpoint */
+			ept->head = ui->head + (ept->num << 1) + 1;
+			ept->flags = EPT_FLAG_IN;
+		} else {
+			/* OUT endpoint */
+			ept->head = ui->head + (ept->num << 1);
+			ept->flags = 0;
+		}
+		setup_timer(&ept->prime_timer, ept_prime_timer_func,
+			(unsigned long) ept);
+
+	}
+}
+
+static void config_ept(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT;
+
+	/* ep0 out needs interrupt-on-setup */
+	if (ept->bit == 0)
+		cfg |= CONFIG_IOS;
+
+	ept->head->config = cfg;
+	ept->head->next = TERMINATE;
+
+	if (ept->ep.maxpacket)
+		dev_dbg(&ui->pdev->dev,
+			"ept #%d %s max:%d head:%p bit:%d\n",
+		       ept->num,
+		       (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+		       ept->ep.maxpacket, ept->head, ept->bit);
+}
+
+static void configure_endpoints(struct usb_info *ui)
+{
+	unsigned n;
+
+	for (n = 0; n < 32; n++)
+		config_ept(ui->ept + n);
+}
+
+struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept,
+			unsigned bufsize, gfp_t gfp_flags)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		goto fail1;
+
+	req->item = dma_pool_alloc(ui->pool, gfp_flags, &req->item_dma);
+	if (!req->item)
+		goto fail2;
+
+	if (bufsize) {
+		req->req.buf = kmalloc(bufsize, gfp_flags);
+		if (!req->req.buf)
+			goto fail3;
+		req->alloced = 1;
+	}
+
+	return &req->req;
+
+fail3:
+	dma_pool_free(ui->pool, req->item, req->item_dma);
+fail2:
+	kfree(req);
+fail1:
+	return 0;
+}
+
+static void usb_ept_enable(struct msm_endpoint *ept, int yes,
+		unsigned char ep_type)
+{
+	struct usb_info *ui = ept->ui;
+	int in = ept->flags & EPT_FLAG_IN;
+	unsigned n;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in) {
+		if (yes) {
+			n = (n & (~CTRL_TXT_MASK)) |
+				(ep_type << CTRL_TXT_EP_TYPE_SHIFT);
+			n |= CTRL_TXE | CTRL_TXR;
+		} else
+			n &= (~CTRL_TXE);
+	} else {
+		if (yes) {
+			n = (n & (~CTRL_RXT_MASK)) |
+				(ep_type << CTRL_RXT_EP_TYPE_SHIFT);
+			n |= CTRL_RXE | CTRL_RXR;
+		} else
+			n &= ~(CTRL_RXE);
+	}
+	/* complete all the updates to ept->head before enabling endpoint*/
+	mb();
+	writel(n, USB_ENDPTCTRL(ept->num));
+
+	/* Ensure endpoint is enabled before returning */
+	mb();
+
+	dev_dbg(&ui->pdev->dev, "ept %d %s %s\n",
+	       ept->num, in ? "in" : "out", yes ? "enabled" : "disabled");
+}
+
+static void ept_prime_timer_func(unsigned long data)
+{
+	struct msm_endpoint *ept = (struct msm_endpoint *)data;
+	struct usb_info *ui = ept->ui;
+	unsigned n = 1 << ept->bit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	ept->false_prime_fail_count++;
+	if ((readl_relaxed(USB_ENDPTPRIME) & n)) {
+		/*
+		 * ---- UNLIKELY ---
+		 * May be hardware is taking long time to process the
+		 * prime request. Or could be intermittent priming and
+		 * previous dTD is not fired yet.
+		 */
+		mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY);
+		goto out;
+	}
+	if (readl_relaxed(USB_ENDPTSTAT) & n)
+		goto out;
+
+	/* clear speculative loads on item->info */
+	rmb();
+	if (ept->req && (ept->req->item->info & INFO_ACTIVE)) {
+		ui->prime_fail_count++;
+		ept->actual_prime_fail_count++;
+		pr_err("%s(): ept%d%s prime failed. ept: config: %x"
+				"active: %x next: %x info: %x\n",
+				__func__, ept->num,
+				ept->flags & EPT_FLAG_IN ? "in" : "out",
+				ept->head->config, ept->head->active,
+				ept->head->next, ept->head->info);
+		writel_relaxed(n, USB_ENDPTPRIME);
+		mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY);
+	}
+out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void usb_ept_start(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req = ept->req;
+	unsigned n = 1 << ept->bit;
+
+	BUG_ON(req->live);
+
+	while (req) {
+		req->live = 1;
+		/* prepare the transaction descriptor item for the hardware */
+		req->item->info =
+			INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
+		req->item->page0 = req->dma;
+		req->item->page1 = (req->dma + 0x1000) & 0xfffff000;
+		req->item->page2 = (req->dma + 0x2000) & 0xfffff000;
+		req->item->page3 = (req->dma + 0x3000) & 0xfffff000;
+
+		if (req->next == NULL) {
+			req->item->next = TERMINATE;
+			break;
+		}
+		req->item->next = req->next->item_dma;
+		req = req->next;
+	}
+
+	rmb();
+	/* link the hw queue head to the request's transaction item */
+	ept->head->next = ept->req->item_dma;
+	ept->head->info = 0;
+
+	/* flush buffers before priming ept */
+	mb();
+	/* during high throughput testing it is observed that
+	 * ept stat bit is not set even though all the data
+	 * structures are updated properly and ept prime bit
+	 * is set. To workaround the issue, kick a timer and
+	 * make decision on re-prime. We can do a busy loop here
+	 * but it leads to high cpu usage.
+	 */
+	writel_relaxed(n, USB_ENDPTPRIME);
+	mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY);
+}
+
+int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req)
+{
+	unsigned long flags;
+	struct msm_request *req = to_msm_request(_req);
+	struct msm_request *last;
+	struct usb_info *ui = ept->ui;
+	unsigned length = req->req.length;
+
+	if (length > 0x4000)
+		return -EMSGSIZE;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (req->busy) {
+		req->req.status = -EBUSY;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		dev_err(&ui->pdev->dev,
+			"usb_ept_queue_xfer() tried to queue busy request\n");
+		return -EBUSY;
+	}
+
+	if (!atomic_read(&ui->configured) && (ept->num != 0)) {
+		req->req.status = -ESHUTDOWN;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		if (printk_ratelimit())
+			dev_err(&ui->pdev->dev,
+				"%s: called while offline\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	if (ui->usb_state == USB_STATE_SUSPENDED) {
+		if (!atomic_read(&ui->remote_wakeup)) {
+			req->req.status = -EAGAIN;
+			spin_unlock_irqrestore(&ui->lock, flags);
+			if (printk_ratelimit())
+				dev_err(&ui->pdev->dev,
+				"%s: cannot queue as bus is suspended "
+				"ept #%d %s max:%d head:%p bit:%d\n",
+				__func__, ept->num,
+				(ept->flags & EPT_FLAG_IN) ? "in" : "out",
+				ept->ep.maxpacket, ept->head, ept->bit);
+
+			return -EAGAIN;
+		}
+
+		wake_lock(&ui->wlock);
+		usb_phy_set_suspend(ui->xceiv, 0);
+		schedule_delayed_work(&ui->rw_work, REMOTE_WAKEUP_DELAY);
+	}
+
+	req->busy = 1;
+	req->live = 0;
+	req->next = 0;
+	req->req.status = -EBUSY;
+
+	req->dma = dma_map_single(NULL, req->req.buf, length,
+				  (ept->flags & EPT_FLAG_IN) ?
+				  DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+
+	/* Add the new request to the end of the queue */
+	last = ept->last;
+	if (last) {
+		/* Already requests in the queue. add us to the
+		 * end, but let the completion interrupt actually
+		 * start things going, to avoid hw issues
+		 */
+		last->next = req;
+		req->prev = last;
+
+	} else {
+		/* queue was empty -- kick the hardware */
+		ept->req = req;
+		req->prev = NULL;
+		usb_ept_start(ept);
+	}
+	ept->last = req;
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return 0;
+}
+
+/* --- endpoint 0 handling --- */
+
+static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct msm_request *r = to_msm_request(req);
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+
+	req->complete = r->gadget_complete;
+	r->gadget_complete = 0;
+	if	(req->complete)
+		req->complete(&ui->ep0in.ep, req);
+}
+
+static void ep0_status_complete(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct usb_request *req = _req->context;
+	struct msm_request *r;
+	struct msm_endpoint *ept;
+	struct usb_info *ui;
+
+	pr_debug("%s:\n", __func__);
+	if (!req)
+		return;
+
+	r = to_msm_request(req);
+	ept = to_msm_endpoint(ep);
+	ui = ept->ui;
+	_req->context = 0;
+
+	req->complete = r->gadget_complete;
+	req->zero = 0;
+	r->gadget_complete = 0;
+	if (req->complete)
+		req->complete(&ui->ep0in.ep, req);
+
+}
+
+static void ep0_status_phase(struct usb_ep *ep, struct usb_request *req)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+
+	pr_debug("%s:\n", __func__);
+
+	req->length = 0;
+	req->complete = ep0_status_complete;
+
+	/* status phase */
+	if (atomic_read(&ui->ep0_dir) == USB_DIR_IN)
+		usb_ept_queue_xfer(&ui->ep0out, req);
+	else
+		usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0in_send_zero_leng_pkt(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct usb_request *req = ui->setup_req;
+
+	pr_debug("%s:\n", __func__);
+
+	req->length = 0;
+	req->complete = ep0_status_phase;
+	usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0_queue_ack_complete(struct usb_ep *ep,
+	struct usb_request *_req)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+	struct usb_request *req = ui->setup_req;
+
+	pr_debug("%s: _req:%p actual:%d length:%d zero:%d\n",
+			__func__, _req, _req->actual,
+			_req->length, _req->zero);
+
+	/* queue up the receive of the ACK response from the host */
+	if (_req->status == 0 && _req->actual == _req->length) {
+		req->context = _req;
+		if (atomic_read(&ui->ep0_dir) == USB_DIR_IN) {
+			if (_req->zero && _req->length &&
+					!(_req->length % ep->maxpacket)) {
+				ep0in_send_zero_leng_pkt(&ui->ep0in);
+				return;
+			}
+		}
+		ep0_status_phase(ep, req);
+	} else
+		ep0_complete(ep, _req);
+}
+
+static void ep0_setup_ack_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(ep);
+	struct usb_info *ui = ept->ui;
+	unsigned int temp;
+	int test_mode = atomic_read(&ui->test_mode);
+
+	if (!test_mode)
+		return;
+
+	switch (test_mode) {
+	case J_TEST:
+		dev_info(&ui->pdev->dev, "usb electrical test mode: (J)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC);
+		break;
+
+	case K_TEST:
+		dev_info(&ui->pdev->dev, "usb electrical test mode: (K)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_K_STATE, USB_PORTSC);
+		break;
+
+	case SE0_NAK_TEST:
+		dev_info(&ui->pdev->dev,
+			"usb electrical test mode: (SE0-NAK)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_SE0_NAK, USB_PORTSC);
+		break;
+
+	case TST_PKT_TEST:
+		dev_info(&ui->pdev->dev,
+			"usb electrical test mode: (TEST_PKT)\n");
+		temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(temp | PORTSC_PTC_TST_PKT, USB_PORTSC);
+		break;
+	}
+}
+
+static void ep0_setup_ack(struct usb_info *ui)
+{
+	struct usb_request *req = ui->setup_req;
+	req->length = 0;
+	req->complete = ep0_setup_ack_complete;
+	usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0_setup_stall(struct usb_info *ui)
+{
+	writel((1<<16) | (1<<0), USB_ENDPTCTRL(0));
+}
+
+static void ep0_setup_send(struct usb_info *ui, unsigned length)
+{
+	struct usb_request *req = ui->setup_req;
+	struct msm_request *r = to_msm_request(req);
+	struct msm_endpoint *ept = &ui->ep0in;
+
+	req->length = length;
+	req->complete = ep0_queue_ack_complete;
+	r->gadget_complete = 0;
+	usb_ept_queue_xfer(ept, req);
+}
+
+static void handle_setup(struct usb_info *ui)
+{
+	struct usb_ctrlrequest ctl;
+	struct usb_request *req = ui->setup_req;
+	int ret;
+#ifdef CONFIG_USB_OTG
+	u8 hnp;
+	unsigned long flags;
+#endif
+	/* USB hardware sometimes generate interrupt before
+	 * 8 bytes of SETUP packet are written to system memory.
+	 * This results in fetching wrong setup_data sometimes.
+	 * TODO: Remove below workaround of adding 1us delay once
+	 * it gets fixed in hardware.
+	 */
+	udelay(10);
+
+	memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl));
+	/* Ensure buffer is read before acknowledging to h/w */
+	mb();
+
+	writel(EPT_RX(0), USB_ENDPTSETUPSTAT);
+
+	if (ctl.bRequestType & USB_DIR_IN)
+		atomic_set(&ui->ep0_dir, USB_DIR_IN);
+	else
+		atomic_set(&ui->ep0_dir, USB_DIR_OUT);
+
+	/* any pending ep0 transactions must be canceled */
+	flush_endpoint(&ui->ep0out);
+	flush_endpoint(&ui->ep0in);
+
+	dev_dbg(&ui->pdev->dev,
+		"setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n",
+	       ctl.bRequestType, ctl.bRequest, ctl.wValue,
+	       ctl.wIndex, ctl.wLength);
+
+	if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) ==
+					(USB_DIR_IN | USB_TYPE_STANDARD)) {
+		if (ctl.bRequest == USB_REQ_GET_STATUS) {
+			/* OTG supplement Rev 2.0 introduces another device
+			 * GET_STATUS request for HNP polling with length = 1.
+			 */
+			u8 len = 2;
+			switch (ctl.bRequestType & USB_RECIP_MASK) {
+			case USB_RECIP_ENDPOINT:
+			{
+				struct msm_endpoint *ept;
+				unsigned num =
+					ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+				u16 temp = 0;
+
+				if (num == 0) {
+					memset(req->buf, 0, 2);
+					break;
+				}
+				if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+					num += 16;
+				ept = &ui->ep0out + num;
+				temp = usb_ep_get_stall(ept);
+				temp = temp << USB_ENDPOINT_HALT;
+				memcpy(req->buf, &temp, 2);
+				break;
+			}
+			case USB_RECIP_DEVICE:
+			{
+				u16 temp = 0;
+
+				if (ctl.wIndex == OTG_STATUS_SELECTOR) {
+#ifdef CONFIG_USB_OTG
+					spin_lock_irqsave(&ui->lock, flags);
+					hnp = (ui->gadget.host_request <<
+							HOST_REQUEST_FLAG);
+					ui->hnp_avail = 1;
+					spin_unlock_irqrestore(&ui->lock,
+							flags);
+					memcpy(req->buf, &hnp, 1);
+					len = 1;
+#else
+					goto stall;
+#endif
+				} else {
+					temp = (atomic_read(&ui->self_powered)
+						<< USB_DEVICE_SELF_POWERED);
+					temp |= (atomic_read(&ui->remote_wakeup)
+						<< USB_DEVICE_REMOTE_WAKEUP);
+					memcpy(req->buf, &temp, 2);
+				}
+				break;
+			}
+			case USB_RECIP_INTERFACE:
+				memset(req->buf, 0, 2);
+				break;
+			default:
+				goto stall;
+			}
+			ep0_setup_send(ui, len);
+			return;
+		}
+	}
+	if (ctl.bRequestType ==
+		    (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) {
+		if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) ||
+				(ctl.bRequest == USB_REQ_SET_FEATURE)) {
+			if ((ctl.wValue == 0) && (ctl.wLength == 0)) {
+				unsigned num = ctl.wIndex & 0x0f;
+
+				if (num != 0) {
+					struct msm_endpoint *ept;
+
+					if (ctl.wIndex & 0x80)
+						num += 16;
+					ept = &ui->ep0out + num;
+
+					if (ept->wedged)
+						goto ack;
+					if (ctl.bRequest == USB_REQ_SET_FEATURE)
+						usb_ept_set_halt(&ept->ep, 1);
+					else
+						usb_ept_set_halt(&ept->ep, 0);
+				}
+				goto ack;
+			}
+		}
+	}
+	if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) {
+		if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			atomic_set(&ui->configured, !!ctl.wValue);
+			msm_hsusb_set_state(USB_STATE_CONFIGURED);
+		} else if (ctl.bRequest == USB_REQ_SET_ADDRESS) {
+			/*
+			 * Gadget speed should be set when PCI interrupt
+			 * occurs. But sometimes, PCI interrupt is not
+			 * occuring after reset. Hence update the gadget
+			 * speed here.
+			 */
+			if (ui->gadget.speed == USB_SPEED_UNKNOWN) {
+				dev_info(&ui->pdev->dev,
+					"PCI intr missed"
+					"set speed explictly\n");
+				msm_hsusb_set_speed(ui);
+			}
+			msm_hsusb_set_state(USB_STATE_ADDRESS);
+
+			/* write address delayed (will take effect
+			** after the next IN txn)
+			*/
+			writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR);
+			goto ack;
+		} else if (ctl.bRequest == USB_REQ_SET_FEATURE) {
+			switch (ctl.wValue) {
+			case USB_DEVICE_TEST_MODE:
+				switch (ctl.wIndex) {
+				case J_TEST:
+				case K_TEST:
+				case SE0_NAK_TEST:
+				case TST_PKT_TEST:
+					atomic_set(&ui->test_mode, ctl.wIndex);
+					goto ack;
+				}
+				goto stall;
+			case USB_DEVICE_REMOTE_WAKEUP:
+				atomic_set(&ui->remote_wakeup, 1);
+				goto ack;
+#ifdef CONFIG_USB_OTG
+			case USB_DEVICE_B_HNP_ENABLE:
+				ui->gadget.b_hnp_enable = 1;
+				goto ack;
+			case USB_DEVICE_A_HNP_SUPPORT:
+			case USB_DEVICE_A_ALT_HNP_SUPPORT:
+				/* B-devices compliant to OTG spec
+				 * Rev 2.0 are not required to
+				 * suppport these features.
+				 */
+				goto stall;
+#endif
+			}
+		} else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) &&
+				(ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) {
+			atomic_set(&ui->remote_wakeup, 0);
+			goto ack;
+		}
+	}
+
+	/* delegate if we get here */
+	if (ui->driver) {
+		ret = ui->driver->setup(&ui->gadget, &ctl);
+		if (ret >= 0)
+			return;
+	}
+
+stall:
+	/* stall ep0 on error */
+	ep0_setup_stall(ui);
+	return;
+
+ack:
+	ep0_setup_ack(ui);
+}
+
+static void handle_endpoint(struct usb_info *ui, unsigned bit)
+{
+	struct msm_endpoint *ept = ui->ept + bit;
+	struct msm_request *req;
+	unsigned long flags;
+	int req_dequeue = 1;
+	unsigned info;
+
+	/*
+	INFO("handle_endpoint() %d %s req=%p(%08x)\n",
+		ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+		ept->req, ept->req ? ept->req->item_dma : 0);
+	*/
+
+	/* expire all requests that are no longer active */
+	spin_lock_irqsave(&ui->lock, flags);
+	while ((req = ept->req)) {
+		/* if we've processed all live requests, time to
+		 * restart the hardware on the next non-live request
+		 */
+		if (!req->live) {
+			usb_ept_start(ept);
+			break;
+		}
+
+dequeue:
+		/* clean speculative fetches on req->item->info */
+		dma_coherent_post_ops();
+		info = req->item->info;
+		/* if the transaction is still in-flight, stop here */
+		if (info & INFO_ACTIVE) {
+			if (req_dequeue) {
+				req_dequeue = 0;
+				ui->dTD_update_fail_count++;
+				ept->dTD_update_fail_count++;
+				udelay(10);
+				goto dequeue;
+			} else {
+				break;
+			}
+		}
+		req_dequeue = 0;
+
+		del_timer(&ept->prime_timer);
+		/* advance ept queue to the next request */
+		ept->req = req->next;
+		if (ept->req == 0)
+			ept->last = 0;
+
+		dma_unmap_single(NULL, req->dma, req->req.length,
+				 (ept->flags & EPT_FLAG_IN) ?
+				 DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+		if (info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TXN_ERROR)) {
+			/* XXX pass on more specific error code */
+			req->req.status = -EIO;
+			req->req.actual = 0;
+			dev_err(&ui->pdev->dev,
+				"ept %d %s error. info=%08x\n",
+			       ept->num,
+			       (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+			       info);
+		} else {
+			req->req.status = 0;
+			req->req.actual =
+				req->req.length - ((info >> 16) & 0x7FFF);
+		}
+		req->busy = 0;
+		req->live = 0;
+
+		if (req->req.complete) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			req->req.complete(&ept->ep, &req->req);
+			spin_lock_irqsave(&ui->lock, flags);
+		}
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void flush_endpoint_hw(struct usb_info *ui, unsigned bits)
+{
+	/* flush endpoint, canceling transactions
+	** - this can take a "large amount of time" (per databook)
+	** - the flush can fail in some cases, thus we check STAT
+	**   and repeat if we're still operating
+	**   (does the fact that this doesn't use the tripwire matter?!)
+	*/
+	do {
+		writel(bits, USB_ENDPTFLUSH);
+		while (readl(USB_ENDPTFLUSH) & bits)
+			udelay(100);
+	} while (readl(USB_ENDPTSTAT) & bits);
+}
+
+static void flush_endpoint_sw(struct msm_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req, *next_req = NULL;
+	unsigned long flags;
+
+	/* inactive endpoints have nothing to do here */
+	if (ept->ep.maxpacket == 0)
+		return;
+
+	/* put the queue head in a sane state */
+	ept->head->info = 0;
+	ept->head->next = TERMINATE;
+
+	/* cancel any pending requests */
+	spin_lock_irqsave(&ui->lock, flags);
+	req = ept->req;
+	ept->req = 0;
+	ept->last = 0;
+	while (req != 0) {
+		req->busy = 0;
+		req->live = 0;
+		req->req.status = -ESHUTDOWN;
+		req->req.actual = 0;
+
+		/* Gadget driver may free the request in completion
+		 * handler. So keep a copy of next req pointer
+		 * before calling completion handler.
+		 */
+		next_req = req->next;
+		if (req->req.complete) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			req->req.complete(&ept->ep, &req->req);
+			spin_lock_irqsave(&ui->lock, flags);
+		}
+		req = next_req;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void flush_endpoint(struct msm_endpoint *ept)
+{
+	del_timer(&ept->prime_timer);
+	flush_endpoint_hw(ept->ui, (1 << ept->bit));
+	flush_endpoint_sw(ept);
+}
+
+static irqreturn_t usb_interrupt(int irq, void *data)
+{
+	struct usb_info *ui = data;
+	unsigned n;
+	unsigned long flags;
+
+	n = readl(USB_USBSTS);
+	writel(n, USB_USBSTS);
+
+	/* somehow we got an IRQ while in the reset sequence: ignore it */
+	if (!atomic_read(&ui->running))
+		return IRQ_HANDLED;
+
+	if (n & STS_PCI) {
+		msm_hsusb_set_speed(ui);
+		if (atomic_read(&ui->configured)) {
+			wake_lock(&ui->wlock);
+
+			spin_lock_irqsave(&ui->lock, flags);
+			ui->usb_state = USB_STATE_CONFIGURED;
+			ui->flags = USB_FLAG_CONFIGURED;
+			spin_unlock_irqrestore(&ui->lock, flags);
+
+			ui->driver->resume(&ui->gadget);
+			schedule_work(&ui->work);
+		} else {
+			msm_hsusb_set_state(USB_STATE_DEFAULT);
+		}
+
+#ifdef CONFIG_USB_OTG
+		/* notify otg to clear A_BIDL_ADIS timer */
+		if (ui->gadget.is_a_peripheral)
+			usb_phy_set_suspend(ui->xceiv, 0);
+#endif
+	}
+
+	if (n & STS_URI) {
+		dev_dbg(&ui->pdev->dev, "reset\n");
+		spin_lock_irqsave(&ui->lock, flags);
+		ui->gadget.speed = USB_SPEED_UNKNOWN;
+		spin_unlock_irqrestore(&ui->lock, flags);
+#ifdef CONFIG_USB_OTG
+		/* notify otg to clear A_BIDL_ADIS timer */
+		if (ui->gadget.is_a_peripheral)
+			usb_phy_set_suspend(ui->xceiv, 0);
+		spin_lock_irqsave(&ui->lock, flags);
+		/* Host request is persistent across reset */
+		ui->gadget.b_hnp_enable = 0;
+		ui->hnp_avail = 0;
+		spin_unlock_irqrestore(&ui->lock, flags);
+#endif
+		msm_hsusb_set_state(USB_STATE_DEFAULT);
+		atomic_set(&ui->remote_wakeup, 0);
+		if (!ui->gadget.is_a_peripheral)
+			schedule_delayed_work(&ui->chg_stop, 0);
+
+		writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
+		writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
+		writel(0xffffffff, USB_ENDPTFLUSH);
+		writel(0, USB_ENDPTCTRL(1));
+
+		wake_lock(&ui->wlock);
+		if (atomic_read(&ui->configured)) {
+			/* marking us offline will cause ept queue attempts
+			** to fail
+			*/
+			atomic_set(&ui->configured, 0);
+			/* Defer sending offline uevent to userspace */
+			atomic_set(&ui->offline_pending, 1);
+
+			/* XXX: we can't seem to detect going offline,
+			 * XXX:  so deconfigure on reset for the time being
+			 */
+			dev_dbg(&ui->pdev->dev,
+					"usb: notify offline\n");
+			ui->driver->disconnect(&ui->gadget);
+			/* cancel pending ep0 transactions */
+			flush_endpoint(&ui->ep0out);
+			flush_endpoint(&ui->ep0in);
+
+		}
+		/* Start phy stuck timer */
+		if (ui->pdata && ui->pdata->is_phy_status_timer_on)
+			mod_timer(&phy_status_timer, PHY_STATUS_CHECK_DELAY);
+	}
+
+	if (n & STS_SLI) {
+		dev_dbg(&ui->pdev->dev, "suspend\n");
+
+		spin_lock_irqsave(&ui->lock, flags);
+		ui->usb_state = USB_STATE_SUSPENDED;
+		ui->flags = USB_FLAG_SUSPEND;
+		spin_unlock_irqrestore(&ui->lock, flags);
+
+		ui->driver->suspend(&ui->gadget);
+		schedule_work(&ui->work);
+#ifdef CONFIG_USB_OTG
+		/* notify otg for
+		 * 1. kicking A_BIDL_ADIS timer in case of A-peripheral
+		 * 2. disabling pull-up and kicking B_ASE0_RST timer
+		 */
+		if (ui->gadget.b_hnp_enable || ui->gadget.is_a_peripheral)
+			usb_phy_set_suspend(ui->xceiv, 1);
+#endif
+	}
+
+	if (n & STS_UI) {
+		n = readl(USB_ENDPTSETUPSTAT);
+		if (n & EPT_RX(0))
+			handle_setup(ui);
+
+		n = readl(USB_ENDPTCOMPLETE);
+		writel(n, USB_ENDPTCOMPLETE);
+		while (n) {
+			unsigned bit = __ffs(n);
+			handle_endpoint(ui, bit);
+			n = n & (~(1 << bit));
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static void usb_prepare(struct usb_info *ui)
+{
+	spin_lock_init(&ui->lock);
+
+	memset(ui->buf, 0, 4096);
+	ui->head = (void *) (ui->buf + 0);
+
+	/* only important for reset/reinit */
+	memset(ui->ept, 0, sizeof(ui->ept));
+	ui->next_item = 0;
+	ui->next_ifc_num = 0;
+
+	init_endpoints(ui);
+
+	ui->ep0in.ep.maxpacket = 64;
+	ui->ep0out.ep.maxpacket = 64;
+
+	ui->setup_req =
+		usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE, GFP_KERNEL);
+
+	INIT_WORK(&ui->work, usb_do_work);
+	INIT_DELAYED_WORK(&ui->chg_det, usb_chg_detect);
+	INIT_DELAYED_WORK(&ui->chg_stop, usb_chg_stop);
+	INIT_DELAYED_WORK(&ui->rw_work, usb_do_remote_wakeup);
+	if (ui->pdata && ui->pdata->is_phy_status_timer_on)
+		INIT_WORK(&ui->phy_status_check, usb_phy_stuck_recover);
+}
+
+static void usb_reset(struct usb_info *ui)
+{
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+
+	dev_dbg(&ui->pdev->dev, "reset controller\n");
+
+	atomic_set(&ui->running, 0);
+
+	/*
+	 * PHY reset takes minimum 100 msec. Hence reset only link
+	 * during HNP. Reset PHY and link in B-peripheral mode.
+	 */
+	if (ui->gadget.is_a_peripheral)
+		otg->reset(ui->xceiv, 0);
+	else
+		otg->reset(ui->xceiv, 1);
+
+	/* set usb controller interrupt threshold to zero*/
+	writel((readl(USB_USBCMD) & ~USBCMD_ITC_MASK) | USBCMD_ITC(0),
+							USB_USBCMD);
+
+	writel(ui->dma, USB_ENDPOINTLISTADDR);
+
+	configure_endpoints(ui);
+
+	/* marking us offline will cause ept queue attempts to fail */
+	atomic_set(&ui->configured, 0);
+
+	if (ui->driver) {
+		dev_dbg(&ui->pdev->dev, "usb: notify offline\n");
+		ui->driver->disconnect(&ui->gadget);
+	}
+
+	/* cancel pending ep0 transactions */
+	flush_endpoint(&ui->ep0out);
+	flush_endpoint(&ui->ep0in);
+
+	/* enable interrupts */
+	writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
+
+	/* Ensure that h/w RESET is completed before returning */
+	mb();
+
+	atomic_set(&ui->running, 1);
+}
+
+static void usb_start(struct usb_info *ui)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->flags |= USB_FLAG_START;
+	schedule_work(&ui->work);
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static int usb_free(struct usb_info *ui, int ret)
+{
+	if (ret)
+		dev_dbg(&ui->pdev->dev, "usb_free(%d)\n", ret);
+
+	usb_del_gadget_udc(&ui->gadget);
+
+	if (ui->xceiv)
+		usb_put_transceiver(ui->xceiv);
+
+	if (ui->irq)
+		free_irq(ui->irq, 0);
+	if (ui->pool)
+		dma_pool_destroy(ui->pool);
+	if (ui->dma)
+		dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma);
+	kfree(ui);
+	return ret;
+}
+
+static void usb_do_work_check_vbus(struct usb_info *ui)
+{
+	unsigned long iflags;
+
+	spin_lock_irqsave(&ui->lock, iflags);
+	if (is_usb_online(ui))
+		ui->flags |= USB_FLAG_VBUS_ONLINE;
+	else
+		ui->flags |= USB_FLAG_VBUS_OFFLINE;
+	spin_unlock_irqrestore(&ui->lock, iflags);
+}
+
+static void usb_do_work(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, work);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long iflags;
+	unsigned flags, _vbus;
+
+	for (;;) {
+		spin_lock_irqsave(&ui->lock, iflags);
+		flags = ui->flags;
+		ui->flags = 0;
+		_vbus = is_usb_online(ui);
+		spin_unlock_irqrestore(&ui->lock, iflags);
+
+		/* give up if we have nothing to do */
+		if (flags == 0)
+			break;
+
+		switch (ui->state) {
+		case USB_STATE_IDLE:
+			if (flags & USB_FLAG_START) {
+				int ret;
+
+				if (!_vbus) {
+					ui->state = USB_STATE_OFFLINE;
+					break;
+				}
+
+				pm_runtime_get_noresume(&ui->pdev->dev);
+				pm_runtime_resume(&ui->pdev->dev);
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: IDLE -> ONLINE\n");
+				usb_reset(ui);
+				ret = request_irq(otg->irq, usb_interrupt,
+							IRQF_SHARED,
+							ui->pdev->name, ui);
+				/* FIXME: should we call BUG_ON when
+				 * requst irq fails
+				 */
+				if (ret) {
+					dev_err(&ui->pdev->dev,
+						"hsusb: peripheral: request irq"
+						" failed:(%d)", ret);
+					break;
+				}
+				ui->irq = otg->irq;
+				ui->state = USB_STATE_ONLINE;
+				usb_do_work_check_vbus(ui);
+
+				if (!atomic_read(&ui->softconnect))
+					break;
+
+				msm72k_pullup_internal(&ui->gadget, 1);
+
+				if (!ui->gadget.is_a_peripheral)
+					schedule_delayed_work(
+							&ui->chg_det,
+							USB_CHG_DET_DELAY);
+
+			}
+			break;
+		case USB_STATE_ONLINE:
+			if (atomic_read(&ui->offline_pending)) {
+				switch_set_state(&ui->sdev, 0);
+				atomic_set(&ui->offline_pending, 0);
+			}
+
+			/* If at any point when we were online, we received
+			 * the signal to go offline, we must honor it
+			 */
+			if (flags & USB_FLAG_VBUS_OFFLINE) {
+
+				ui->chg_current = 0;
+				/* wait incase chg_detect is running */
+				if (!ui->gadget.is_a_peripheral)
+					cancel_delayed_work_sync(&ui->chg_det);
+
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: ONLINE -> OFFLINE\n");
+
+				atomic_set(&ui->running, 0);
+				atomic_set(&ui->remote_wakeup, 0);
+				atomic_set(&ui->configured, 0);
+
+				if (ui->driver) {
+					dev_dbg(&ui->pdev->dev,
+						"usb: notify offline\n");
+					ui->driver->disconnect(&ui->gadget);
+				}
+				/* cancel pending ep0 transactions */
+				flush_endpoint(&ui->ep0out);
+				flush_endpoint(&ui->ep0in);
+
+				/* synchronize with irq context */
+				spin_lock_irqsave(&ui->lock, iflags);
+#ifdef CONFIG_USB_OTG
+				ui->gadget.host_request = 0;
+				ui->gadget.b_hnp_enable = 0;
+				ui->hnp_avail = 0;
+#endif
+				msm72k_pullup_internal(&ui->gadget, 0);
+				spin_unlock_irqrestore(&ui->lock, iflags);
+
+
+				/* if charger is initialized to known type
+				 * we must let modem know about charger
+				 * disconnection
+				 */
+				usb_phy_set_power(ui->xceiv, 0);
+
+				if (ui->irq) {
+					free_irq(ui->irq, ui);
+					ui->irq = 0;
+				}
+
+
+				switch_set_state(&ui->sdev, 0);
+
+				ui->state = USB_STATE_OFFLINE;
+				usb_do_work_check_vbus(ui);
+				pm_runtime_put_noidle(&ui->pdev->dev);
+				pm_runtime_suspend(&ui->pdev->dev);
+				wake_unlock(&ui->wlock);
+				break;
+			}
+			if (flags & USB_FLAG_SUSPEND) {
+				int maxpower = usb_get_max_power(ui);
+
+				if (maxpower < 0)
+					break;
+
+				usb_phy_set_power(ui->xceiv, 0);
+				/* To support TCXO during bus suspend
+				 * This might be dummy check since bus suspend
+				 * is not implemented as of now
+				 * */
+				if (release_wlocks)
+					wake_unlock(&ui->wlock);
+
+				/* TBD: Initiate LPM at usb bus suspend */
+				break;
+			}
+			if (flags & USB_FLAG_CONFIGURED) {
+				int maxpower = usb_get_max_power(ui);
+
+				/* We may come here even when no configuration
+				 * is selected. Send online/offline event
+				 * accordingly.
+				 */
+				switch_set_state(&ui->sdev,
+						atomic_read(&ui->configured));
+
+				if (maxpower < 0)
+					break;
+
+				ui->chg_current = maxpower;
+				usb_phy_set_power(ui->xceiv, maxpower);
+				break;
+			}
+			if (flags & USB_FLAG_RESET) {
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: ONLINE -> RESET\n");
+				msm72k_pullup_internal(&ui->gadget, 0);
+				usb_reset(ui);
+				msm72k_pullup_internal(&ui->gadget, 1);
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: RESET -> ONLINE\n");
+				break;
+			}
+			break;
+		case USB_STATE_OFFLINE:
+			/* If we were signaled to go online and vbus is still
+			 * present when we received the signal, go online.
+			 */
+			if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) {
+				int ret;
+
+				pm_runtime_get_noresume(&ui->pdev->dev);
+				pm_runtime_resume(&ui->pdev->dev);
+				dev_dbg(&ui->pdev->dev,
+					"msm72k_udc: OFFLINE -> ONLINE\n");
+
+				usb_reset(ui);
+				ui->state = USB_STATE_ONLINE;
+				usb_do_work_check_vbus(ui);
+				ret = request_irq(otg->irq, usb_interrupt,
+							IRQF_SHARED,
+							ui->pdev->name, ui);
+				/* FIXME: should we call BUG_ON when
+				 * requst irq fails
+				 */
+				if (ret) {
+					dev_err(&ui->pdev->dev,
+						"hsusb: peripheral: request irq"
+						" failed:(%d)", ret);
+					break;
+				}
+				ui->irq = otg->irq;
+				enable_irq_wake(otg->irq);
+
+				if (!atomic_read(&ui->softconnect))
+					break;
+				msm72k_pullup_internal(&ui->gadget, 1);
+
+				if (!ui->gadget.is_a_peripheral)
+					schedule_delayed_work(
+							&ui->chg_det,
+							USB_CHG_DET_DELAY);
+			}
+			break;
+		}
+	}
+}
+
+/* FIXME - the callers of this function should use a gadget API instead.
+ * This is called from htc_battery.c and board-halibut.c
+ * WARNING - this can get called before this driver is initialized.
+ */
+void msm_hsusb_set_vbus_state(int online)
+{
+	unsigned long flags;
+	struct usb_info *ui = the_usb_info;
+
+	if (!ui) {
+		pr_err("%s called before driver initialized\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (is_usb_online(ui) ==  online)
+		goto out;
+
+	if (online) {
+		ui->usb_state = USB_STATE_POWERED;
+		ui->flags |= USB_FLAG_VBUS_ONLINE;
+	} else {
+		ui->gadget.speed = USB_SPEED_UNKNOWN;
+		ui->usb_state = USB_STATE_NOTATTACHED;
+		ui->flags |= USB_FLAG_VBUS_OFFLINE;
+	}
+	if (in_interrupt()) {
+		schedule_work(&ui->work);
+	} else {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		usb_do_work(&ui->work);
+		return;
+	}
+out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+
+void usb_function_reenumerate(void)
+{
+	struct usb_info *ui = the_usb_info;
+
+	/* disable and re-enable the D+ pullup */
+	dev_dbg(&ui->pdev->dev, "disable pullup\n");
+	writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+
+	msleep(10);
+
+	dev_dbg(&ui->pdev->dev, "enable pullup\n");
+	writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+}
+
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_read_status(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	char *buf = debug_buffer;
+	unsigned long flags;
+	struct msm_endpoint *ept;
+	struct msm_request *req;
+	int n;
+	int i = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+		   "regs: setup=%08x prime=%08x stat=%08x done=%08x\n",
+		   readl(USB_ENDPTSETUPSTAT),
+		   readl(USB_ENDPTPRIME),
+		   readl(USB_ENDPTSTAT),
+		   readl(USB_ENDPTCOMPLETE));
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+		   "regs:   cmd=%08x   sts=%08x intr=%08x port=%08x\n\n",
+		   readl(USB_USBCMD),
+		   readl(USB_USBSTS),
+		   readl(USB_USBINTR),
+		   readl(USB_PORTSC));
+
+
+	for (n = 0; n < 32; n++) {
+		ept = ui->ept + n;
+		if (ept->ep.maxpacket == 0)
+			continue;
+
+		i += scnprintf(buf + i, PAGE_SIZE - i,
+			"ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n",
+			ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out",
+			ept->head->config, ept->head->active,
+			ept->head->next, ept->head->info);
+
+		for (req = ept->req; req; req = req->next)
+			i += scnprintf(buf + i, PAGE_SIZE - i,
+			"  req @%08x next=%08x info=%08x page0=%08x %c %c\n",
+				req->item_dma, req->item->next,
+				req->item->info, req->item->page0,
+				req->busy ? 'B' : ' ',
+				req->live ? 'L' : ' ');
+	}
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+			   "phy failure count: %d\n", ui->phy_fail_count);
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+}
+
+static ssize_t debug_write_reset(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->flags |= USB_FLAG_RESET;
+	schedule_work(&ui->work);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+
+static ssize_t debug_write_cycle(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	usb_function_reenumerate();
+	return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+const struct file_operations debug_stat_ops = {
+	.open = debug_open,
+	.read = debug_read_status,
+};
+
+const struct file_operations debug_reset_ops = {
+	.open = debug_open,
+	.write = debug_write_reset,
+};
+
+const struct file_operations debug_cycle_ops = {
+	.open = debug_open,
+	.write = debug_write_cycle,
+};
+
+static ssize_t debug_read_release_wlocks(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	char kbuf[10];
+	size_t c = 0;
+
+	memset(kbuf, 0, 10);
+
+	c = scnprintf(kbuf, 10, "%d", release_wlocks);
+
+	if (copy_to_user(ubuf, kbuf, c))
+		return -EFAULT;
+
+	return c;
+}
+static ssize_t debug_write_release_wlocks(struct file *file,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	char kbuf[10];
+	long temp;
+
+	memset(kbuf, 0, 10);
+
+	if (copy_from_user(kbuf, buf, count > 10 ? 10 : count))
+		return -EFAULT;
+
+	if (strict_strtol(kbuf, 10, &temp))
+		return -EINVAL;
+
+	if (temp)
+		release_wlocks = 1;
+	else
+		release_wlocks = 0;
+
+	return count;
+}
+static int debug_wake_lock_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+const struct file_operations debug_wlocks_ops = {
+	.open = debug_wake_lock_open,
+	.read = debug_read_release_wlocks,
+	.write = debug_write_release_wlocks,
+};
+
+static ssize_t debug_reprime_ep(struct file *file, const char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	struct msm_endpoint *ept;
+	char kbuf[10];
+	unsigned int ep_num, dir;
+	unsigned long flags;
+	unsigned n, i;
+
+	memset(kbuf, 0, 10);
+
+	if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count))
+		return -EFAULT;
+
+	if (sscanf(kbuf, "%u %u", &ep_num, &dir) != 2)
+		return -EINVAL;
+
+	if (dir)
+		i = ep_num + 16;
+	else
+		i = ep_num;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ept = ui->ept + i;
+	n = 1 << ept->bit;
+
+	if ((readl_relaxed(USB_ENDPTPRIME) & n))
+		goto out;
+
+	if (readl_relaxed(USB_ENDPTSTAT) & n)
+		goto out;
+
+	/* clear speculative loads on item->info */
+	rmb();
+	if (ept->req && (ept->req->item->info & INFO_ACTIVE)) {
+		pr_err("%s(): ept%d%s prime failed. ept: config: %x"
+				"active: %x next: %x info: %x\n",
+				__func__, ept->num,
+				ept->flags & EPT_FLAG_IN ? "in" : "out",
+				ept->head->config, ept->head->active,
+				ept->head->next, ept->head->info);
+		writel_relaxed(n, USB_ENDPTPRIME);
+	}
+out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+
+static char buffer[512];
+static ssize_t debug_prime_fail_read(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct usb_info *ui = file->private_data;
+	char *buf = buffer;
+	unsigned long flags;
+	struct msm_endpoint *ept;
+	int n;
+	int i = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	for (n = 0; n < 32; n++) {
+		ept = ui->ept + n;
+		if (ept->ep.maxpacket == 0)
+			continue;
+
+		i += scnprintf(buf + i, PAGE_SIZE - i,
+			"ept%d %s false_prime_count=%lu prime_fail_count=%d dtd_fail_count=%lu\n",
+			ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out",
+			ept->false_prime_fail_count,
+			ept->actual_prime_fail_count,
+			ept->dTD_update_fail_count);
+	}
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+			   "dTD_update_fail count: %lu\n",
+			    ui->dTD_update_fail_count);
+
+	i += scnprintf(buf + i, PAGE_SIZE - i,
+			   "prime_fail count: %d\n", ui->prime_fail_count);
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+}
+
+static int debug_prime_fail_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+const struct file_operations prime_fail_ops = {
+	.open = debug_prime_fail_open,
+	.read = debug_prime_fail_read,
+	.write = debug_reprime_ep,
+};
+
+static void usb_debugfs_init(struct usb_info *ui)
+{
+	struct dentry *dent;
+	dent = debugfs_create_dir(dev_name(&ui->pdev->dev), 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops);
+	debugfs_create_file("reset", 0222, dent, ui, &debug_reset_ops);
+	debugfs_create_file("cycle", 0222, dent, ui, &debug_cycle_ops);
+	debugfs_create_file("release_wlocks", 0666, dent, ui,
+						&debug_wlocks_ops);
+	debugfs_create_file("prime_fail_countt", 0666, dent, ui,
+						&prime_fail_ops);
+}
+#else
+static void usb_debugfs_init(struct usb_info *ui) {}
+#endif
+
+static int
+msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct msm_endpoint *ept;
+	unsigned char ep_type;
+
+	if (_ep == NULL || desc == NULL)
+		return -EINVAL;
+
+	ept = to_msm_endpoint(_ep);
+	ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	_ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+	config_ept(ept);
+	ept->wedged = 0;
+	usb_ept_enable(ept, 1, ep_type);
+	return 0;
+}
+
+static int msm72k_disable(struct usb_ep *_ep)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+
+	usb_ept_enable(ept, 0, 0);
+	flush_endpoint(ept);
+	/*
+	 * Clear descriptors here. Otherwise previous descriptors
+	 * will be used by function drivers upon next enumeration.
+	 */
+	_ep->desc = NULL;
+	return 0;
+}
+
+static struct usb_request *
+msm72k_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	return usb_ept_alloc_req(to_msm_endpoint(_ep), 0, gfp_flags);
+}
+
+static void
+msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct msm_request *req = to_msm_request(_req);
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+	struct usb_info *ui = ept->ui;
+
+	/* request should not be busy */
+	BUG_ON(req->busy);
+	if (req->alloced)
+		kfree(req->req.buf);
+	dma_pool_free(ui->pool, req->item, req->item_dma);
+	kfree(req);
+}
+
+static int
+msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags)
+{
+	struct msm_endpoint *ep = to_msm_endpoint(_ep);
+	struct usb_info *ui = ep->ui;
+
+	if (ep == &ui->ep0in) {
+		struct msm_request *r = to_msm_request(req);
+		if (!req->length)
+			goto ep_queue_done;
+		r->gadget_complete = req->complete;
+		/* ep0_queue_ack_complete queue a receive for ACK before
+		** calling req->complete
+		*/
+		req->complete = ep0_queue_ack_complete;
+		if (atomic_read(&ui->ep0_dir) == USB_DIR_OUT)
+			ep = &ui->ep0out;
+		goto ep_queue_done;
+	}
+
+ep_queue_done:
+	return usb_ept_queue_xfer(ep, req);
+}
+
+static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct msm_endpoint *ep = to_msm_endpoint(_ep);
+	struct msm_request *req = to_msm_request(_req);
+	struct usb_info *ui = ep->ui;
+
+	struct msm_request *temp_req;
+	unsigned long flags;
+
+	if (!(ui && req && ep->req))
+		return -EINVAL;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (!req->busy) {
+		dev_dbg(&ui->pdev->dev, "%s: !req->busy\n", __func__);
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return -EINVAL;
+	}
+	del_timer(&ep->prime_timer);
+	/* Stop the transfer */
+	do {
+		writel((1 << ep->bit), USB_ENDPTFLUSH);
+		while (readl(USB_ENDPTFLUSH) & (1 << ep->bit))
+			udelay(100);
+	} while (readl(USB_ENDPTSTAT) & (1 << ep->bit));
+
+	req->req.status = 0;
+	req->busy = 0;
+
+	if (ep->req == req) {
+		ep->req = req->next;
+		ep->head->next = req->item->next;
+	} else {
+		req->prev->next = req->next;
+		if (req->next)
+			req->next->prev = req->prev;
+		req->prev->item->next = req->item->next;
+	}
+
+	if (!req->next)
+		ep->last = req->prev;
+
+	/* initialize request to default */
+	req->item->next = TERMINATE;
+	req->item->info = 0;
+	req->live = 0;
+	dma_unmap_single(NULL, req->dma, req->req.length,
+		(ep->flags & EPT_FLAG_IN) ?
+		DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+	if (req->req.complete) {
+		req->req.status = -ECONNRESET;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		req->req.complete(&ep->ep, &req->req);
+		spin_lock_irqsave(&ui->lock, flags);
+	}
+
+	if (!req->live) {
+		/* Reprime the endpoint for the remaining transfers */
+		for (temp_req = ep->req ; temp_req ; temp_req = temp_req->next)
+			temp_req->live = 0;
+		if (ep->req)
+			usb_ept_start(ep);
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return 0;
+}
+
+static int
+usb_ept_set_halt(struct usb_ep *_ep, int value)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+	struct usb_info *ui = ept->ui;
+	unsigned int in = ept->flags & EPT_FLAG_IN;
+	unsigned int n;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in) {
+		if (value)
+			n |= CTRL_TXS;
+		else {
+			n &= ~CTRL_TXS;
+			n |= CTRL_TXR;
+		}
+	} else {
+		if (value)
+			n |= CTRL_RXS;
+		else {
+			n &= ~CTRL_RXS;
+			n |= CTRL_RXR;
+		}
+	}
+	writel(n, USB_ENDPTCTRL(ept->num));
+	if (!value)
+		ept->wedged = 0;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return 0;
+}
+
+static int
+msm72k_set_halt(struct usb_ep *_ep, int value)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+	unsigned int in = ept->flags & EPT_FLAG_IN;
+
+	if (value && in && ept->req)
+		return -EAGAIN;
+
+	usb_ept_set_halt(_ep, value);
+
+	return 0;
+}
+
+static int
+msm72k_fifo_status(struct usb_ep *_ep)
+{
+	return -EOPNOTSUPP;
+}
+
+static void
+msm72k_fifo_flush(struct usb_ep *_ep)
+{
+	flush_endpoint(to_msm_endpoint(_ep));
+}
+static int msm72k_set_wedge(struct usb_ep *_ep)
+{
+	struct msm_endpoint *ept = to_msm_endpoint(_ep);
+
+	if (ept->num == 0)
+		return -EINVAL;
+
+	ept->wedged = 1;
+
+	return msm72k_set_halt(_ep, 1);
+}
+
+static const struct usb_ep_ops msm72k_ep_ops = {
+	.enable		= msm72k_enable,
+	.disable	= msm72k_disable,
+
+	.alloc_request	= msm72k_alloc_request,
+	.free_request	= msm72k_free_request,
+
+	.queue		= msm72k_queue,
+	.dequeue	= msm72k_dequeue,
+
+	.set_halt	= msm72k_set_halt,
+	.set_wedge	= msm72k_set_wedge,
+	.fifo_status	= msm72k_fifo_status,
+	.fifo_flush	= msm72k_fifo_flush,
+};
+
+static int msm72k_get_frame(struct usb_gadget *_gadget)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+
+	/* frame number is in bits 13:3 */
+	return (readl(USB_FRINDEX) >> 3) & 0x000007FF;
+}
+
+/* VBUS reporting logically comes from a transceiver */
+static int msm72k_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+
+	if (is_active || atomic_read(&otg->chg_type)
+					 == USB_CHG_TYPE__WALLCHARGER)
+		wake_lock(&ui->wlock);
+
+	msm_hsusb_set_vbus_state(is_active);
+	return 0;
+}
+
+/* SW workarounds
+Issue #1	- USB Spoof Disconnect Failure
+Symptom	- Writing 0 to run/stop bit of USBCMD doesn't cause disconnect
+SW workaround	- Making opmode non-driving and SuspendM set in function
+		register of SMSC phy
+*/
+/* drivers may have software control over D+ pullup */
+static int msm72k_pullup_internal(struct usb_gadget *_gadget, int is_active)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+
+	if (is_active) {
+		spin_lock_irqsave(&ui->lock, flags);
+		if (is_usb_online(ui) && ui->driver)
+			writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+		spin_unlock_irqrestore(&ui->lock, flags);
+	} else {
+		writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+		/* S/W workaround, Issue#1 */
+		usb_phy_io_write(ui->xceiv, 0x48, 0x04);
+	}
+
+	/* Ensure pull-up operation is completed before returning */
+	mb();
+
+	return 0;
+}
+
+static int msm72k_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	unsigned long flags;
+
+	atomic_set(&ui->softconnect, is_active);
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->usb_state == USB_STATE_NOTATTACHED || ui->driver == NULL ||
+		atomic_read(&otg->chg_type) == USB_CHG_TYPE__WALLCHARGER) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	msm72k_pullup_internal(_gadget, is_active);
+
+	if (is_active && !ui->gadget.is_a_peripheral)
+		schedule_delayed_work(&ui->chg_det, USB_CHG_DET_DELAY);
+
+	return 0;
+}
+
+static int msm72k_wakeup(struct usb_gadget *_gadget)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+
+	if (!atomic_read(&ui->remote_wakeup)) {
+		dev_err(&ui->pdev->dev,
+			"%s: remote wakeup not supported\n", __func__);
+		return -ENOTSUPP;
+	}
+
+	if (!atomic_read(&ui->configured)) {
+		dev_err(&ui->pdev->dev,
+			"%s: device is not configured\n", __func__);
+		return -ENODEV;
+	}
+	usb_phy_set_suspend(ui->xceiv, 0);
+
+	disable_irq(otg->irq);
+
+	if (!is_usb_active())
+		writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+
+	/* Ensure that USB port is resumed before enabling the IRQ */
+	mb();
+
+	enable_irq(otg->irq);
+
+	return 0;
+}
+
+/* when Gadget is configured, it will indicate how much power
+ * can be pulled from vbus, as specified in configuiration descriptor
+ */
+static int msm72k_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->b_max_pow = mA;
+	ui->flags = USB_FLAG_CONFIGURED;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	schedule_work(&ui->work);
+
+	return 0;
+}
+
+static int msm72k_set_selfpowered(struct usb_gadget *_gadget, int set)
+{
+	struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (set) {
+		if (ui->pdata && ui->pdata->self_powered)
+			atomic_set(&ui->self_powered, 1);
+		else
+			ret = -EOPNOTSUPP;
+	} else {
+		/* We can always work as a bus powered device */
+		atomic_set(&ui->self_powered, 0);
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return ret;
+
+}
+
+static int msm72k_gadget_start(struct usb_gadget_driver *driver,
+		int (*bind)(struct usb_gadget *));
+static int msm72k_gadget_stop(struct usb_gadget_driver *driver);
+
+
+static const struct usb_gadget_ops msm72k_ops = {
+	.get_frame	= msm72k_get_frame,
+	.vbus_session	= msm72k_udc_vbus_session,
+	.vbus_draw	= msm72k_udc_vbus_draw,
+	.pullup		= msm72k_pullup,
+	.wakeup		= msm72k_wakeup,
+	.set_selfpowered = msm72k_set_selfpowered,
+	.start		= msm72k_gadget_start,
+	.stop		= msm72k_gadget_stop
+};
+
+static void usb_do_remote_wakeup(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+
+	msm72k_wakeup(&ui->gadget);
+}
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+
+	msm72k_wakeup(&ui->gadget);
+
+	return count;
+}
+
+static ssize_t show_usb_state(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	size_t i;
+	char *state[] = {"USB_STATE_NOTATTACHED", "USB_STATE_ATTACHED",
+			"USB_STATE_POWERED", "USB_STATE_UNAUTHENTICATED",
+			"USB_STATE_RECONNECTING", "USB_STATE_DEFAULT",
+			"USB_STATE_ADDRESS", "USB_STATE_CONFIGURED",
+			"USB_STATE_SUSPENDED"
+	};
+
+	i = scnprintf(buf, PAGE_SIZE, "%s\n", state[msm_hsusb_get_state()]);
+	return i;
+}
+
+static ssize_t show_usb_speed(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	size_t i;
+	char *speed[] = {"USB_SPEED_UNKNOWN", "USB_SPEED_LOW",
+			"USB_SPEED_FULL", "USB_SPEED_HIGH"};
+
+	i = scnprintf(buf, PAGE_SIZE, "%s\n", speed[ui->gadget.speed]);
+	return i;
+}
+
+static ssize_t store_usb_chg_current(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long mA;
+
+	if (ui->gadget.is_a_peripheral)
+		return -EINVAL;
+
+	if (strict_strtoul(buf, 10, &mA))
+		return -EINVAL;
+
+	ui->chg_current = mA;
+	usb_phy_set_power(ui->xceiv, mA);
+
+	return count;
+}
+
+static ssize_t show_usb_chg_current(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	size_t count;
+
+	count = snprintf(buf, PAGE_SIZE, "%d", ui->chg_current);
+
+	return count;
+}
+
+static ssize_t show_usb_chg_type(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	struct msm_otg *otg = to_msm_otg(ui->xceiv);
+	size_t count;
+	char *chg_type[] = {"STD DOWNSTREAM PORT",
+			"CARKIT",
+			"DEDICATED CHARGER",
+			"INVALID"};
+
+	count = snprintf(buf, PAGE_SIZE, "%s",
+			chg_type[atomic_read(&otg->chg_type)]);
+
+	return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+static DEVICE_ATTR(usb_state, S_IRUSR, show_usb_state, 0);
+static DEVICE_ATTR(usb_speed, S_IRUSR, show_usb_speed, 0);
+static DEVICE_ATTR(chg_type, S_IRUSR, show_usb_chg_type, 0);
+static DEVICE_ATTR(chg_current, S_IWUSR | S_IRUSR,
+		show_usb_chg_current, store_usb_chg_current);
+
+#ifdef CONFIG_USB_OTG
+static ssize_t store_host_req(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long val, flags;
+
+	if (strict_strtoul(buf, 10, &val))
+		return -EINVAL;
+
+	dev_dbg(&ui->pdev->dev, "%s host request\n",
+			val ? "set" : "clear");
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->hnp_avail)
+		ui->gadget.host_request = !!val;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+static DEVICE_ATTR(host_request, S_IWUSR, NULL, store_host_req);
+
+/* How do we notify user space about HNP availability?
+ * As we are compliant to Rev 2.0, Host will not set a_hnp_support.
+ * Introduce hnp_avail flag and set when HNP polling request arrives.
+ * The expectation is that user space checks hnp availability before
+ * requesting host role via above sysfs node.
+ */
+static ssize_t show_host_avail(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	size_t count;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	count = snprintf(buf, PAGE_SIZE, "%d\n", ui->hnp_avail);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return count;
+}
+static DEVICE_ATTR(host_avail, S_IRUSR, show_host_avail, NULL);
+
+static struct attribute *otg_attrs[] = {
+	&dev_attr_host_request.attr,
+	&dev_attr_host_avail.attr,
+	NULL,
+};
+
+static struct attribute_group otg_attr_grp = {
+	.name  = "otg",
+	.attrs = otg_attrs,
+};
+#endif
+
+static int msm72k_probe(struct platform_device *pdev)
+{
+	struct usb_info *ui;
+	struct msm_otg *otg;
+	int retval;
+
+	dev_dbg(&pdev->dev, "msm72k_probe\n");
+	ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL);
+	if (!ui)
+		return -ENOMEM;
+
+	ui->pdev = pdev;
+	ui->pdata = pdev->dev.platform_data;
+
+	ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL);
+	if (!ui->buf)
+		return usb_free(ui, -ENOMEM);
+
+	ui->pool = dma_pool_create("msm72k_udc", NULL, 32, 32, 0);
+	if (!ui->pool)
+		return usb_free(ui, -ENOMEM);
+
+	ui->xceiv = usb_get_transceiver();
+	if (!ui->xceiv)
+		return usb_free(ui, -ENODEV);
+
+	otg = to_msm_otg(ui->xceiv);
+	ui->addr = otg->regs;
+
+	ui->gadget.ops = &msm72k_ops;
+	ui->gadget.max_speed = USB_SPEED_HIGH;
+	device_initialize(&ui->gadget.dev);
+	dev_set_name(&ui->gadget.dev, "gadget");
+	ui->gadget.dev.parent = &pdev->dev;
+	ui->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+#ifdef CONFIG_USB_OTG
+	ui->gadget.is_otg = 1;
+#endif
+
+	retval = usb_add_gadget_udc(&pdev->dev, &ui->gadget);
+	if (retval)
+		return usb_free(ui, retval);
+
+	ui->sdev.name = DRIVER_NAME;
+	ui->sdev.print_name = print_switch_name;
+	ui->sdev.print_state = print_switch_state;
+
+	retval = switch_dev_register(&ui->sdev);
+	if (retval)
+		return usb_free(ui, retval);
+
+	the_usb_info = ui;
+
+	wake_lock_init(&ui->wlock,
+			WAKE_LOCK_SUSPEND, "usb_bus_active");
+
+	usb_debugfs_init(ui);
+
+	usb_prepare(ui);
+
+#ifdef CONFIG_USB_OTG
+	retval = sysfs_create_group(&pdev->dev.kobj, &otg_attr_grp);
+	if (retval) {
+		dev_err(&ui->pdev->dev,
+			"failed to create otg sysfs directory:"
+			"err:(%d)\n", retval);
+	}
+#endif
+
+	retval = otg_set_peripheral(ui->xceiv->otg, &ui->gadget);
+	if (retval) {
+		dev_err(&ui->pdev->dev,
+			"%s: Cannot bind the transceiver, retval:(%d)\n",
+			__func__, retval);
+		switch_dev_unregister(&ui->sdev);
+		wake_lock_destroy(&ui->wlock);
+		return usb_free(ui, retval);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	/* Setup phy stuck timer */
+	if (ui->pdata && ui->pdata->is_phy_status_timer_on)
+		setup_timer(&phy_status_timer, usb_phy_status_check_timer, 0);
+	return 0;
+}
+
+static int msm72k_gadget_start(struct usb_gadget_driver *driver,
+			    int (*bind)(struct usb_gadget *))
+{
+	struct usb_info *ui = the_usb_info;
+	int			retval, n;
+
+	if (!driver
+			|| driver->max_speed < USB_SPEED_FULL
+			|| !bind
+			|| !driver->disconnect
+			|| !driver->setup)
+		return -EINVAL;
+	if (!ui)
+		return -ENODEV;
+	if (ui->driver)
+		return -EBUSY;
+
+	/* first hook up the driver ... */
+	ui->driver = driver;
+	ui->gadget.dev.driver = &driver->driver;
+	ui->gadget.name = driver_name;
+	INIT_LIST_HEAD(&ui->gadget.ep_list);
+	ui->gadget.ep0 = &ui->ep0in.ep;
+	INIT_LIST_HEAD(&ui->gadget.ep0->ep_list);
+	ui->gadget.speed = USB_SPEED_UNKNOWN;
+	atomic_set(&ui->softconnect, 1);
+
+	for (n = 1; n < 16; n++) {
+		struct msm_endpoint *ept = ui->ept + n;
+		list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list);
+		ept->ep.maxpacket = 512;
+	}
+	for (n = 17; n < 32; n++) {
+		struct msm_endpoint *ept = ui->ept + n;
+		list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list);
+		ept->ep.maxpacket = 512;
+	}
+
+	retval = device_add(&ui->gadget.dev);
+	if (retval)
+		goto fail;
+
+	retval = bind(&ui->gadget);
+	if (retval) {
+		dev_err(&ui->pdev->dev, "bind to driver %s --> error %d\n",
+				driver->driver.name, retval);
+		device_del(&ui->gadget.dev);
+		goto fail;
+	}
+
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev, "failed to create sysfs entry:"
+			"(wakeup) error: (%d)\n", retval);
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_usb_state);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev, "failed to create sysfs entry:"
+			" (usb_state) error: (%d)\n", retval);
+
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_usb_speed);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev, "failed to create sysfs entry:"
+			" (usb_speed) error: (%d)\n", retval);
+
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_chg_type);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev,
+			"failed to create sysfs entry(chg_type): err:(%d)\n",
+					retval);
+	retval = device_create_file(&ui->gadget.dev, &dev_attr_chg_current);
+	if (retval != 0)
+		dev_err(&ui->pdev->dev,
+			"failed to create sysfs entry(chg_current):"
+			"err:(%d)\n", retval);
+
+	dev_dbg(&ui->pdev->dev, "registered gadget driver '%s'\n",
+			driver->driver.name);
+	usb_start(ui);
+
+	return 0;
+
+fail:
+	ui->driver = NULL;
+	ui->gadget.dev.driver = NULL;
+	return retval;
+}
+
+static int msm72k_gadget_stop(struct usb_gadget_driver *driver)
+{
+	struct usb_info *dev = the_usb_info;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver || !driver->unbind)
+		return -EINVAL;
+
+	msm72k_pullup_internal(&dev->gadget, 0);
+	if (dev->irq) {
+		free_irq(dev->irq, dev);
+		dev->irq = 0;
+	}
+	dev->state = USB_STATE_IDLE;
+	atomic_set(&dev->configured, 0);
+	switch_set_state(&dev->sdev, 0);
+	/* cancel pending ep0 transactions */
+	flush_endpoint(&dev->ep0out);
+	flush_endpoint(&dev->ep0in);
+
+	device_remove_file(&dev->gadget.dev, &dev_attr_wakeup);
+	device_remove_file(&dev->gadget.dev, &dev_attr_usb_state);
+	device_remove_file(&dev->gadget.dev, &dev_attr_usb_speed);
+	device_remove_file(&dev->gadget.dev, &dev_attr_chg_type);
+	device_remove_file(&dev->gadget.dev, &dev_attr_chg_current);
+	driver->disconnect(&dev->gadget);
+	driver->unbind(&dev->gadget);
+	dev->gadget.dev.driver = NULL;
+	dev->driver = NULL;
+
+	device_del(&dev->gadget.dev);
+
+	dev_dbg(&dev->pdev->dev,
+		"unregistered gadget driver '%s'\n", driver->driver.name);
+	return 0;
+}
+
+
+static int msm72k_udc_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int msm72k_udc_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static int msm72k_udc_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+
+static struct dev_pm_ops msm72k_udc_dev_pm_ops = {
+	.runtime_suspend = msm72k_udc_runtime_suspend,
+	.runtime_resume = msm72k_udc_runtime_resume,
+	.runtime_idle = msm72k_udc_runtime_idle
+};
+
+static int __exit msm72k_remove(struct platform_device *pdev)
+{
+	struct usb_info *ui = container_of(&pdev, struct usb_info, pdev);
+
+	return usb_free(ui, 0);
+}
+
+static struct platform_driver usb_driver = {
+	.probe = msm72k_probe,
+	.remove = msm72k_remove,
+	.driver = { .name = "msm_hsusb",
+		    .pm = &msm72k_udc_dev_pm_ops, },
+};
+
+static int __init init(void)
+{
+	return platform_driver_register(&usb_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+	platform_driver_unregister(&usb_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Mike Lockwood, Brian Swetland");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/qcom_maemo.c b/drivers/usb/gadget/qcom_maemo.c
new file mode 100644
index 0000000..39686c4
--- /dev/null
+++ b/drivers/usb/gadget/qcom_maemo.c
@@ -0,0 +1,304 @@
+/*
+ * Qualcomm Maemo Composite driver
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program from the Code Aurora Forum is free software; you can
+ * redistribute it and/or modify it under the GNU General Public License
+ * version 2 and only version 2 as published by the Free Software Foundation.
+ * The original work available from [git.kernel.org ] is subject to the
+ * notice below.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+
+
+#define DRIVER_DESC		"Qcom Maemo Composite Gadget"
+#define VENDOR_ID		0x05c6
+#define PRODUCT_ID		0x902E
+
+/*
+ * kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#define USB_ETH
+
+#define USB_ETH_RNDIS
+#ifdef USB_ETH_RNDIS
+#  include "f_rndis.c"
+#  include "rndis.c"
+#endif
+
+
+#include "u_serial.c"
+#include "f_serial.c"
+
+#include "u_ether.c"
+
+#undef DBG     /* u_ether.c has broken idea about macros */
+#undef VDBG    /* so clean up after it */
+#undef ERROR
+#undef INFO
+
+#include "f_mass_storage.c"
+#include "f_diag.c"
+#include "f_rmnet.c"
+
+/*-------------------------------------------------------------------------*/
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX         0
+#define STRING_PRODUCT_IDX              1
+#define STRING_SERIAL_IDX               2
+
+/* String Table */
+static struct usb_string strings_dev[] = {
+	/* These dummy values should be overridden by platform data */
+	[STRING_MANUFACTURER_IDX].s = "Qualcomm Incorporated",
+	[STRING_PRODUCT_IDX].s = "Usb composition",
+	[STRING_SERIAL_IDX].s = "0123456789ABCDEF",
+	{  }                    /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language       = 0x0409,       /* en-us */
+	.strings        = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+	.bLength              = sizeof(device_desc),
+	.bDescriptorType      = USB_DT_DEVICE,
+	.bcdUSB               = __constant_cpu_to_le16(0x0200),
+	.bDeviceClass         = USB_CLASS_PER_INTERFACE,
+	.bDeviceSubClass      =      0,
+	.bDeviceProtocol      =      0,
+	.idVendor             = __constant_cpu_to_le16(VENDOR_ID),
+	.idProduct            = __constant_cpu_to_le16(PRODUCT_ID),
+	.bcdDevice            = __constant_cpu_to_le16(0xffff),
+	.bNumConfigurations   = 1,
+};
+
+static u8 hostaddr[ETH_ALEN];
+static struct usb_diag_ch *diag_ch;
+static struct usb_diag_platform_data usb_diag_pdata = {
+	.ch_name = DIAG_LEGACY,
+};
+
+/****************************** Configurations ******************************/
+static struct fsg_module_parameters mod_data = {
+	.stall = 0
+};
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static struct fsg_common *fsg_common;
+static int maemo_setup_config(struct usb_configuration *c,
+			const struct usb_ctrlrequest *ctrl);
+
+static int maemo_do_config(struct usb_configuration *c)
+{
+	int ret;
+
+	ret = rndis_bind_config(c, hostaddr);
+	if (ret < 0)
+		return ret;
+
+	ret = diag_function_add(c);
+	if (ret < 0)
+		return ret;
+
+	ret = gser_bind_config(c, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = gser_bind_config(c, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rmnet_function_add(c);
+	if (ret < 0)
+		return ret;
+
+	ret = fsg_add(c->cdev, c, fsg_common);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct usb_configuration maemo_config_driver = {
+	.label			= "Qcom Maemo Gadget",
+	.bind			= maemo_do_config,
+	.setup			= maemo_setup_config,
+	.bConfigurationValue	= 1,
+	.bMaxPower		= 0xFA,
+};
+static int maemo_setup_config(struct usb_configuration *c,
+		const struct usb_ctrlrequest *ctrl)
+{
+	int i;
+	int ret = -EOPNOTSUPP;
+
+	for (i = 0; i < maemo_config_driver.next_interface_id; i++) {
+		if (maemo_config_driver.interface[i]->setup) {
+			ret = maemo_config_driver.interface[i]->setup(
+				maemo_config_driver.interface[i], ctrl);
+			if (ret >= 0)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int maemo_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget *gadget = cdev->gadget;
+	int status, gcnum;
+
+	/* set up diag channel */
+	diag_ch = diag_setup(&usb_diag_pdata);
+	if (IS_ERR(diag_ch))
+		return PTR_ERR(diag_ch);
+
+	/* set up network link layer */
+	status = gether_setup(cdev->gadget, hostaddr);
+	if (status < 0)
+		goto diag_clean;
+
+	/* set up serial link layer */
+	status = gserial_setup(cdev->gadget, 2);
+	if (status < 0)
+		goto fail0;
+
+	/* set up mass storage function */
+	fsg_common = fsg_common_from_params(0, cdev, &mod_data);
+	if (IS_ERR(fsg_common)) {
+		status = PTR_ERR(fsg_common);
+		goto fail1;
+	}
+
+	gcnum = usb_gadget_controller_number(gadget);
+	if (gcnum >= 0)
+		device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+	else {
+		/* gadget zero is so simple (for now, no altsettings) that
+		 * it SHOULD NOT have problems with bulk-capable hardware.
+		 * so just warn about unrcognized controllers -- don't panic.
+		 *
+		 * things like configuration and altsetting numbering
+		 * can need hardware-specific attention though.
+		 */
+		WARNING(cdev, "controller '%s' not recognized\n",
+			gadget->name);
+		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+	}
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	*/
+
+	status = usb_string_id(cdev);
+	if (status < 0)
+		goto fail2;
+	strings_dev[STRING_MANUFACTURER_IDX].id = status;
+	device_desc.iManufacturer = status;
+
+	status = usb_string_id(cdev);
+	if (status < 0)
+		goto fail2;
+	strings_dev[STRING_PRODUCT_IDX].id = status;
+	device_desc.iProduct = status;
+
+	if (!usb_gadget_set_selfpowered(gadget))
+		maemo_config_driver.bmAttributes |= USB_CONFIG_ATT_SELFPOWER;
+
+	if (gadget->ops->wakeup)
+		maemo_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+
+	/* register our first configuration */
+	status = usb_add_config(cdev, &maemo_config_driver);
+	if (status < 0)
+		goto fail2;
+
+	usb_gadget_set_selfpowered(gadget);
+	dev_info(&gadget->dev, DRIVER_DESC "\n");
+	fsg_common_put(fsg_common);
+	return 0;
+
+fail2:
+	fsg_common_put(fsg_common);
+fail1:
+	gserial_cleanup();
+fail0:
+	gether_cleanup();
+diag_clean:
+	diag_cleanup(diag_ch);
+
+	return status;
+}
+
+static int __exit maemo_unbind(struct usb_composite_dev *cdev)
+{
+	gserial_cleanup();
+	gether_cleanup();
+	diag_cleanup(diag_ch);
+	return 0;
+}
+
+static struct usb_composite_driver qcom_maemo_driver = {
+	.name		= "Qcom Maemo Gadget",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.bind		= maemo_bind,
+	.unbind		= __exit_p(maemo_unbind),
+};
+
+static int __init qcom_maemo_usb_init(void)
+{
+	return usb_composite_register(&qcom_maemo_driver);
+}
+module_init(qcom_maemo_usb_init);
+
+static void __exit qcom_maemo_usb_cleanup(void)
+{
+	usb_composite_unregister(&qcom_maemo_driver);
+}
+module_exit(qcom_maemo_usb_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 73a934a..0cb2121 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -1146,11 +1146,15 @@
 
 #endif /* CONFIG_USB_GADGET_DEBUG_FILES */
 
+static bool rndis_initialized;
 
 int rndis_init(void)
 {
 	u8 i;
 
+	if (rndis_initialized)
+		return 0;
+
 	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
 #ifdef	CONFIG_USB_GADGET_DEBUG_FILES
 		char name [20];
@@ -1177,6 +1181,7 @@
 		INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
 	}
 
+	rndis_initialized = true;
 	return 0;
 }
 
@@ -1185,7 +1190,13 @@
 #ifdef CONFIG_USB_GADGET_DEBUG_FILES
 	u8 i;
 	char name[20];
+#endif
 
+	if (!rndis_initialized)
+		return;
+	rndis_initialized = false;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
 	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
 		sprintf(name, NAME_TEMPLATE, i);
 		remove_proc_entry(name, NULL);
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 8081ca3..3c57df4 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -207,6 +207,17 @@
 	unsigned int	blkbits;	/* Bits of logical block size of bound block device */
 	unsigned int	blksize;	/* logical block size of bound block device */
 	struct device	dev;
+#ifdef CONFIG_USB_MSC_PROFILING
+	spinlock_t	lock;
+	struct {
+
+		unsigned long rbytes;
+		unsigned long wbytes;
+		ktime_t rtime;
+		ktime_t wtime;
+	} perf;
+
+#endif
 };
 
 #define fsg_lun_is_open(curlun)	((curlun)->filp != NULL)
@@ -221,6 +232,9 @@
 #define EP0_BUFSIZE	256
 #define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */
 
+#ifdef CONFIG_USB_CSW_HACK
+#define fsg_num_buffers		4
+#else
 #ifdef CONFIG_USB_GADGET_DEBUG_FILES
 
 static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
@@ -236,6 +250,7 @@
 #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
 
 #endif /* CONFIG_USB_DEBUG */
+#endif /* CONFIG_USB_CSW_HACK */
 
 /* check if fsg_num_buffers is within a valid range */
 static inline int fsg_num_buffers_validate(void)
@@ -786,6 +801,43 @@
 	return sprintf(buf, "%u\n", curlun->nofua);
 }
 
+#ifdef CONFIG_USB_MSC_PROFILING
+static ssize_t fsg_show_perf(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	unsigned long rbytes, wbytes;
+	int64_t rtime, wtime;
+
+	spin_lock(&curlun->lock);
+	rbytes = curlun->perf.rbytes;
+	wbytes = curlun->perf.wbytes;
+	rtime = ktime_to_us(curlun->perf.rtime);
+	wtime = ktime_to_us(curlun->perf.wtime);
+	spin_unlock(&curlun->lock);
+
+	return snprintf(buf, PAGE_SIZE, "Write performance :"
+					"%lu bytes in %lld microseconds\n"
+					"Read performance :"
+					"%lu bytes in %lld microseconds\n",
+					wbytes, wtime, rbytes, rtime);
+}
+static ssize_t fsg_store_perf(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	int value;
+
+	sscanf(buf, "%d", &value);
+	if (!value) {
+		spin_lock(&curlun->lock);
+		memset(&curlun->perf, 0, sizeof(curlun->perf));
+		spin_unlock(&curlun->lock);
+	}
+
+	return count;
+}
+#endif
 static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
 			     char *buf)
 {
@@ -872,10 +924,16 @@
 	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
 	int		rc = 0;
 
+
+#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+	/* disabled in android because we need to allow closing the backing file
+	 * if the media was removed
+	 */
 	if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
 		LDBG(curlun, "eject attempt prevented\n");
 		return -EBUSY;				/* "Door is locked" */
 	}
+#endif
 
 	/* Remove a trailing newline */
 	if (count > 0 && buf[count-1] == '\n')
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
new file mode 100644
index 0000000..d379c66
--- /dev/null
+++ b/drivers/usb/gadget/u_bam.c
@@ -0,0 +1,1262 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <mach/msm_smd.h>
+#include <linux/netdevice.h>
+#include <mach/bam_dmux.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
+#include "u_rmnet.h"
+
+#define BAM_N_PORTS	1
+#define BAM2BAM_N_PORTS	3
+
+static struct workqueue_struct *gbam_wq;
+static int n_bam_ports;
+static int n_bam2bam_ports;
+static unsigned n_tx_req_queued;
+static unsigned bam_ch_ids[] = { 8 };
+
+static const char *bam_ch_names[] = { "bam_dmux_ch_8" };
+
+#define BAM_PENDING_LIMIT			220
+#define BAM_MUX_TX_PKT_DROP_THRESHOLD		1000
+#define BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD		500
+#define BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD		300
+#define BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT	1
+
+#define BAM_MUX_HDR				8
+
+#define BAM_MUX_RX_Q_SIZE			16
+#define BAM_MUX_TX_Q_SIZE			200
+#define BAM_MUX_RX_REQ_SIZE			(2048 - BAM_MUX_HDR)
+
+#define DL_INTR_THRESHOLD			20
+
+unsigned int bam_mux_tx_pkt_drop_thld = BAM_MUX_TX_PKT_DROP_THRESHOLD;
+module_param(bam_mux_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_fctrl_en_thld = BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD;
+module_param(bam_mux_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_fctrl_support = BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT;
+module_param(bam_mux_rx_fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_fctrl_dis_thld = BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD;
+module_param(bam_mux_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_tx_q_size = BAM_MUX_TX_Q_SIZE;
+module_param(bam_mux_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_q_size = BAM_MUX_RX_Q_SIZE;
+module_param(bam_mux_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int bam_mux_rx_req_size = BAM_MUX_RX_REQ_SIZE;
+module_param(bam_mux_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int dl_intr_threshold = DL_INTR_THRESHOLD;
+module_param(dl_intr_threshold, uint, S_IRUGO | S_IWUSR);
+
+#define BAM_CH_OPENED	BIT(0)
+#define BAM_CH_READY	BIT(1)
+
+struct bam_ch_info {
+	unsigned long		flags;
+	unsigned		id;
+
+	struct list_head        tx_idle;
+	struct sk_buff_head	tx_skb_q;
+
+	struct list_head        rx_idle;
+	struct sk_buff_head	rx_skb_q;
+
+	struct gbam_port	*port;
+	struct work_struct	write_tobam_w;
+	struct work_struct	write_tohost_w;
+
+	struct usb_request	*rx_req;
+	struct usb_request	*tx_req;
+
+	u8					src_pipe_idx;
+	u8					dst_pipe_idx;
+	u8					connection_idx;
+
+	/* stats */
+	unsigned int		pending_with_bam;
+	unsigned int		tohost_drp_cnt;
+	unsigned int		tomodem_drp_cnt;
+	unsigned int		tx_len;
+	unsigned int		rx_len;
+	unsigned long		to_modem;
+	unsigned long		to_host;
+};
+
+struct gbam_port {
+	unsigned		port_num;
+	spinlock_t		port_lock_ul;
+	spinlock_t		port_lock_dl;
+
+	struct grmnet		*port_usb;
+	struct grmnet		*gr;
+
+	struct bam_ch_info	data_ch;
+
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+};
+
+static struct bam_portmaster {
+	struct gbam_port *port;
+	struct platform_driver pdrv;
+} bam_ports[BAM_N_PORTS];
+
+struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
+static void gbam_start_rx(struct gbam_port *port);
+static void gbam_start_endless_rx(struct gbam_port *port);
+static void gbam_start_endless_tx(struct gbam_port *port);
+
+/*---------------misc functions---------------- */
+static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int gbam_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num,
+		void (*cb)(struct usb_ep *ep, struct usb_request *),
+		gfp_t flags)
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("%s: ep:%p head:%p num:%d cb:%p", __func__,
+			ep, head, num, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_ep_alloc_request(ep, flags);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+/*--------------------------------------------- */
+
+/*------------data_path----------------------------*/
+static void gbam_write_data_tohost(struct gbam_port *port)
+{
+	unsigned long			flags;
+	struct bam_ch_info		*d = &port->data_ch;
+	struct sk_buff			*skb;
+	int				ret;
+	struct usb_request		*req;
+	struct usb_ep			*ep;
+
+	spin_lock_irqsave(&port->port_lock_dl, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_dl, flags);
+		return;
+	}
+
+	ep = port->port_usb->in;
+
+	while (!list_empty(&d->tx_idle)) {
+		skb = __skb_dequeue(&d->tx_skb_q);
+		if (!skb) {
+			spin_unlock_irqrestore(&port->port_lock_dl, flags);
+			return;
+		}
+		req = list_first_entry(&d->tx_idle,
+				struct usb_request,
+				list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+		n_tx_req_queued++;
+		if (n_tx_req_queued == dl_intr_threshold) {
+			req->no_interrupt = 0;
+			n_tx_req_queued = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+
+		list_del(&req->list);
+
+		spin_unlock(&port->port_lock_dl);
+		ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+		spin_lock(&port->port_lock_dl);
+		if (ret) {
+			pr_err("%s: usb epIn failed\n", __func__);
+			list_add(&req->list, &d->tx_idle);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		d->to_host++;
+	}
+	spin_unlock_irqrestore(&port->port_lock_dl, flags);
+}
+
+static void gbam_write_data_tohost_w(struct work_struct *w)
+{
+	struct bam_ch_info	*d;
+	struct gbam_port	*port;
+
+	d = container_of(w, struct bam_ch_info, write_tohost_w);
+	port = d->port;
+
+	gbam_write_data_tohost(port);
+}
+
+void gbam_data_recv_cb(void *p, struct sk_buff *skb)
+{
+	struct gbam_port	*port = p;
+	struct bam_ch_info	*d = &port->data_ch;
+	unsigned long		flags;
+
+	if (!skb)
+		return;
+
+	pr_debug("%s: p:%p#%d d:%p skb_len:%d\n", __func__,
+			port, port->port_num, d, skb->len);
+
+	spin_lock_irqsave(&port->port_lock_dl, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_dl, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (d->tx_skb_q.qlen > bam_mux_tx_pkt_drop_thld) {
+		d->tohost_drp_cnt++;
+		if (printk_ratelimit())
+			pr_err("%s: tx pkt dropped: tx_drop_cnt:%u\n",
+					__func__, d->tohost_drp_cnt);
+		spin_unlock_irqrestore(&port->port_lock_dl, flags);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	__skb_queue_tail(&d->tx_skb_q, skb);
+	spin_unlock_irqrestore(&port->port_lock_dl, flags);
+
+	gbam_write_data_tohost(port);
+}
+
+void gbam_data_write_done(void *p, struct sk_buff *skb)
+{
+	struct gbam_port	*port = p;
+	struct bam_ch_info	*d = &port->data_ch;
+	unsigned long		flags;
+
+	if (!skb)
+		return;
+
+	dev_kfree_skb_any(skb);
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+
+	d->pending_with_bam--;
+
+	pr_debug("%s: port:%p d:%p tom:%lu pbam:%u, pno:%d\n", __func__,
+			port, d, d->to_modem,
+			d->pending_with_bam, port->port_num);
+
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	queue_work(gbam_wq, &d->write_tobam_w);
+}
+
+static void gbam_data_write_tobam(struct work_struct *w)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	int			ret;
+	int			qlen;
+
+	d = container_of(w, struct bam_ch_info, write_tobam_w);
+	port = d->port;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+
+	while (d->pending_with_bam < BAM_PENDING_LIMIT) {
+		skb =  __skb_dequeue(&d->rx_skb_q);
+		if (!skb)
+			break;
+
+		d->pending_with_bam++;
+		d->to_modem++;
+
+		pr_debug("%s: port:%p d:%p tom:%lu pbam:%u pno:%d\n", __func__,
+				port, d, d->to_modem, d->pending_with_bam,
+				port->port_num);
+
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		ret = msm_bam_dmux_write(d->id, skb);
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		if (ret) {
+			pr_debug("%s: write error:%d\n", __func__, ret);
+			d->pending_with_bam--;
+			d->to_modem--;
+			d->tomodem_drp_cnt++;
+			dev_kfree_skb_any(skb);
+			break;
+		}
+	}
+
+	qlen = d->rx_skb_q.qlen;
+
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	if (qlen < BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD)
+		gbam_start_rx(port);
+}
+/*-------------------------------------------------------------*/
+
+static void gbam_epin_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gbam_port	*port = ep->driver_data;
+	struct bam_ch_info	*d;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		break;
+	default:
+		pr_err("%s: data tx ep error %d\n",
+				__func__, status);
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	if (!port)
+		return;
+
+	spin_lock(&port->port_lock_dl);
+	d = &port->data_ch;
+	list_add_tail(&req->list, &d->tx_idle);
+	spin_unlock(&port->port_lock_dl);
+
+	queue_work(gbam_wq, &d->write_tohost_w);
+}
+
+static void
+gbam_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gbam_port	*port = ep->driver_data;
+	struct bam_ch_info	*d = &port->data_ch;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+	int			queue = 0;
+
+	switch (status) {
+	case 0:
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* cable disconnection */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		if (printk_ratelimit())
+			pr_err("%s: %s response error %d, %d/%d\n",
+				__func__, ep->name, status,
+				req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	spin_lock(&port->port_lock_ul);
+	if (queue) {
+		__skb_queue_tail(&d->rx_skb_q, skb);
+		queue_work(gbam_wq, &d->write_tobam_w);
+	}
+
+	/* TODO: Handle flow control gracefully by having
+	 * having call back mechanism from bam driver
+	 */
+	if (bam_mux_rx_fctrl_support &&
+		d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld) {
+
+		list_add_tail(&req->list, &d->rx_idle);
+		spin_unlock(&port->port_lock_ul);
+		return;
+	}
+	spin_unlock(&port->port_lock_ul);
+
+	skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
+	if (!skb) {
+		spin_lock(&port->port_lock_ul);
+		list_add_tail(&req->list, &d->rx_idle);
+		spin_unlock(&port->port_lock_ul);
+		return;
+	}
+	skb_reserve(skb, BAM_MUX_HDR);
+
+	req->buf = skb->data;
+	req->length = bam_mux_rx_req_size;
+	req->context = skb;
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status) {
+		dev_kfree_skb_any(skb);
+
+		if (printk_ratelimit())
+			pr_err("%s: data rx enqueue err %d\n",
+					__func__, status);
+
+		spin_lock(&port->port_lock_ul);
+		list_add_tail(&req->list, &d->rx_idle);
+		spin_unlock(&port->port_lock_ul);
+	}
+}
+
+static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_start_rx(struct gbam_port *port)
+{
+	struct usb_request		*req;
+	struct bam_ch_info		*d;
+	struct usb_ep			*ep;
+	unsigned long			flags;
+	int				ret;
+	struct sk_buff			*skb;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+
+	d = &port->data_ch;
+	ep = port->port_usb->out;
+
+	while (port->port_usb && !list_empty(&d->rx_idle)) {
+
+		if (bam_mux_rx_fctrl_support &&
+			d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld)
+			break;
+
+		req = list_first_entry(&d->rx_idle, struct usb_request, list);
+
+		skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
+		if (!skb)
+			break;
+		skb_reserve(skb, BAM_MUX_HDR);
+
+		list_del(&req->list);
+		req->buf = skb->data;
+		req->length = bam_mux_rx_req_size;
+		req->context = skb;
+
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+
+			if (printk_ratelimit())
+				pr_err("%s: rx queue failed\n", __func__);
+
+			if (port->port_usb)
+				list_add(&req->list, &d->rx_idle);
+			else
+				usb_ep_free_request(ep, req);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+}
+
+static void gbam_start_endless_rx(struct gbam_port *port)
+{
+	struct bam_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_endless_tx(struct gbam_port *port)
+{
+	struct bam_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_io(struct gbam_port *port)
+{
+	unsigned long		flags;
+	struct usb_ep		*ep;
+	int			ret;
+	struct bam_ch_info	*d;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+
+	d = &port->data_ch;
+	ep = port->port_usb->out;
+	ret = gbam_alloc_requests(ep, &d->rx_idle, bam_mux_rx_q_size,
+			gbam_epout_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: rx req allocation failed\n", __func__);
+		return;
+	}
+
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	spin_lock_irqsave(&port->port_lock_dl, flags);
+	ep = port->port_usb->in;
+	ret = gbam_alloc_requests(ep, &d->tx_idle, bam_mux_tx_q_size,
+			gbam_epin_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: tx req allocation failed\n", __func__);
+		gbam_free_requests(ep, &d->rx_idle);
+		return;
+	}
+
+	spin_unlock_irqrestore(&port->port_lock_dl, flags);
+
+	/* queue out requests */
+	gbam_start_rx(port);
+}
+
+static void gbam_notify(void *p, int event, unsigned long data)
+{
+	switch (event) {
+	case BAM_DMUX_RECEIVE:
+		gbam_data_recv_cb(p, (struct sk_buff *)(data));
+		break;
+	case BAM_DMUX_WRITE_DONE:
+		gbam_data_write_done(p, (struct sk_buff *)(data));
+		break;
+	}
+}
+
+static void gbam_free_buffers(struct gbam_port *port)
+{
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	struct bam_ch_info	*d;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+
+	if (!port || !port->port_usb)
+		goto free_buf_out;
+
+	d = &port->data_ch;
+
+	gbam_free_requests(port->port_usb->in, &d->tx_idle);
+	gbam_free_requests(port->port_usb->out, &d->rx_idle);
+
+	while ((skb = __skb_dequeue(&d->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&d->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+
+free_buf_out:
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+}
+
+static void gbam_disconnect_work(struct work_struct *w)
+{
+	struct gbam_port *port =
+			container_of(w, struct gbam_port, disconnect_w);
+	struct bam_ch_info *d = &port->data_ch;
+
+	if (!test_bit(BAM_CH_OPENED, &d->flags))
+		return;
+
+	msm_bam_dmux_close(d->id);
+	clear_bit(BAM_CH_OPENED, &d->flags);
+}
+
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+	struct gbam_port *port =
+			container_of(w, struct gbam_port, disconnect_w);
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = 0;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	/* disable endpoints */
+	usb_ep_disable(port->gr->out);
+	usb_ep_disable(port->gr->in);
+
+	port->gr->in->driver_data = NULL;
+	port->gr->out->driver_data = NULL;
+}
+
+static void gbam_connect_work(struct work_struct *w)
+{
+	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+	struct bam_ch_info *d = &port->data_ch;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	if (!port->port_usb) {
+		spin_unlock(&port->port_lock_dl);
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+		return;
+	}
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	if (!test_bit(BAM_CH_READY, &d->flags))
+		return;
+
+	ret = msm_bam_dmux_open(d->id, port, gbam_notify);
+	if (ret) {
+		pr_err("%s: unable open bam ch:%d err:%d\n",
+				__func__, d->id, ret);
+		return;
+	}
+	set_bit(BAM_CH_OPENED, &d->flags);
+
+	gbam_start_io(port);
+
+	pr_debug("%s: done\n", __func__);
+}
+
+static void gbam2bam_connect_work(struct work_struct *w)
+{
+	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+	struct bam_ch_info *d = &port->data_ch;
+	u32 sps_params;
+	int ret;
+	unsigned long flags;
+
+	ret = usb_ep_enable(port->gr->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, port->gr->in);
+		return;
+	}
+	port->gr->in->driver_data = port;
+
+	ret = usb_ep_enable(port->gr->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, port->gr->out);
+		port->gr->in->driver_data = 0;
+		return;
+	}
+	port->gr->out->driver_data = port;
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = port->gr;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+						  &d->dst_pipe_idx);
+	if (ret) {
+		pr_err("%s: usb_bam_connect failed: err:%d\n",
+			__func__, ret);
+		return;
+	}
+
+	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+	if (!d->rx_req)
+		return;
+
+	d->rx_req->context = port;
+	d->rx_req->complete = gbam_endless_rx_complete;
+	d->rx_req->length = 0;
+	sps_params = (MSM_SPS_MODE | d->src_pipe_idx |
+				 MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
+	d->rx_req->udc_priv = sps_params;
+	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
+	if (!d->tx_req)
+		return;
+
+	d->tx_req->context = port;
+	d->tx_req->complete = gbam_endless_tx_complete;
+	d->tx_req->length = 0;
+	sps_params = (MSM_SPS_MODE | d->dst_pipe_idx |
+				 MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
+	d->tx_req->udc_priv = sps_params;
+
+	/* queue in & out requests */
+	gbam_start_endless_rx(port);
+	gbam_start_endless_tx(port);
+
+	pr_debug("%s: done\n", __func__);
+}
+
+/* BAM data channel ready, allow attempt to open */
+static int gbam_data_ch_probe(struct platform_device *pdev)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	int			i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_bam_ports; i++) {
+		port = bam_ports[i].port;
+		d = &port->data_ch;
+
+		if (!strncmp(bam_ch_names[i], pdev->name,
+					BAM_DMUX_CH_NAME_MAX_LEN)) {
+			set_bit(BAM_CH_READY, &d->flags);
+
+			/* if usb is online, try opening bam_ch */
+			spin_lock_irqsave(&port->port_lock_ul, flags);
+			spin_lock(&port->port_lock_dl);
+			if (port->port_usb)
+				queue_work(gbam_wq, &port->connect_w);
+			spin_unlock(&port->port_lock_dl);
+			spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/* BAM data channel went inactive, so close it */
+static int gbam_data_ch_remove(struct platform_device *pdev)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	struct usb_ep		*ep_in = NULL;
+	struct usb_ep		*ep_out = NULL;
+	unsigned long		flags;
+	int			i;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_bam_ports; i++) {
+		if (!strncmp(bam_ch_names[i], pdev->name,
+					BAM_DMUX_CH_NAME_MAX_LEN)) {
+			port = bam_ports[i].port;
+			d = &port->data_ch;
+
+			spin_lock_irqsave(&port->port_lock_ul, flags);
+			spin_lock(&port->port_lock_dl);
+			if (port->port_usb) {
+				ep_in = port->port_usb->in;
+				ep_out = port->port_usb->out;
+			}
+			spin_unlock(&port->port_lock_dl);
+			spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+			if (ep_in)
+				usb_ep_fifo_flush(ep_in);
+			if (ep_out)
+				usb_ep_fifo_flush(ep_out);
+
+			gbam_free_buffers(port);
+
+			msm_bam_dmux_close(d->id);
+
+			/* bam dmux will free all pending skbs */
+			d->pending_with_bam = 0;
+
+			clear_bit(BAM_CH_READY, &d->flags);
+			clear_bit(BAM_CH_OPENED, &d->flags);
+		}
+	}
+
+	return 0;
+}
+
+static void gbam_port_free(int portno)
+{
+	struct gbam_port *port = bam_ports[portno].port;
+	struct platform_driver *pdrv = &bam_ports[portno].pdrv;
+
+	if (port) {
+		kfree(port);
+		platform_driver_unregister(pdrv);
+	}
+}
+
+static void gbam2bam_port_free(int portno)
+{
+	struct gbam_port *port = bam2bam_ports[portno];
+
+	kfree(port);
+}
+
+static int gbam_port_alloc(int portno)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock_ul);
+	spin_lock_init(&port->port_lock_dl);
+	INIT_WORK(&port->connect_w, gbam_connect_work);
+	INIT_WORK(&port->disconnect_w, gbam_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	INIT_LIST_HEAD(&d->tx_idle);
+	INIT_LIST_HEAD(&d->rx_idle);
+	INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);
+	INIT_WORK(&d->write_tohost_w, gbam_write_data_tohost_w);
+	skb_queue_head_init(&d->tx_skb_q);
+	skb_queue_head_init(&d->rx_skb_q);
+	d->id = bam_ch_ids[portno];
+
+	bam_ports[portno].port = port;
+
+	pdrv = &bam_ports[portno].pdrv;
+	pdrv->probe = gbam_data_ch_probe;
+	pdrv->remove = gbam_data_ch_remove;
+	pdrv->driver.name = bam_ch_names[portno];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+static int gbam2bam_port_alloc(int portno)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+
+	port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock_ul);
+	spin_lock_init(&port->port_lock_dl);
+
+	INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+	INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	bam2bam_ports[portno] = port;
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t gbam_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < n_bam_ports; i++) {
+		port = bam_ports[i].port;
+		if (!port)
+			continue;
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		spin_lock(&port->port_lock_dl);
+
+		d = &port->data_ch;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"#PORT:%d port:%p data_ch:%p#\n"
+				"dpkts_to_usbhost: %lu\n"
+				"dpkts_to_modem:  %lu\n"
+				"dpkts_pwith_bam: %u\n"
+				"to_usbhost_dcnt:  %u\n"
+				"tomodem__dcnt:  %u\n"
+				"tx_buf_len:	 %u\n"
+				"rx_buf_len:	 %u\n"
+				"data_ch_open:   %d\n"
+				"data_ch_ready:  %d\n",
+				i, port, &port->data_ch,
+				d->to_host, d->to_modem,
+				d->pending_with_bam,
+				d->tohost_drp_cnt, d->tomodem_drp_cnt,
+				d->tx_skb_q.qlen, d->rx_skb_q.qlen,
+				test_bit(BAM_CH_OPENED, &d->flags),
+				test_bit(BAM_CH_READY, &d->flags));
+
+		spin_unlock(&port->port_lock_dl);
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t gbam_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < n_bam_ports; i++) {
+		port = bam_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock_ul, flags);
+		spin_lock(&port->port_lock_dl);
+
+		d = &port->data_ch;
+
+		d->to_host = 0;
+		d->to_modem = 0;
+		d->pending_with_bam = 0;
+		d->tohost_drp_cnt = 0;
+		d->tomodem_drp_cnt = 0;
+
+		spin_unlock(&port->port_lock_dl);
+		spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	}
+	return count;
+}
+
+const struct file_operations gbam_stats_ops = {
+	.read = gbam_read_stats,
+	.write = gbam_reset_stats,
+};
+
+static void gbam_debugfs_init(void)
+{
+	struct dentry *dent;
+	struct dentry *dfile;
+
+	dent = debugfs_create_dir("usb_rmnet", 0);
+	if (IS_ERR(dent))
+		return;
+
+	/* TODO: Implement cleanup function to remove created file */
+	dfile = debugfs_create_file("status", 0444, dent, 0, &gbam_stats_ops);
+	if (!dfile || IS_ERR(dfile))
+		debugfs_remove(dent);
+}
+#else
+static void gam_debugfs_init(void) { }
+#endif
+
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	unsigned long		flags;
+	struct bam_ch_info	*d;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (trans == USB_GADGET_XPORT_BAM &&
+		port_num >= n_bam_ports) {
+		pr_err("%s: invalid bam portno#%d\n",
+			   __func__, port_num);
+		return;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM &&
+		port_num >= n_bam2bam_ports) {
+		pr_err("%s: invalid bam2bam portno#%d\n",
+			   __func__, port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+	if (trans == USB_GADGET_XPORT_BAM)
+		port = bam_ports[port_num].port;
+	else
+		port = bam2bam_ports[port_num];
+
+	d = &port->data_ch;
+	port->gr = gr;
+
+	if (trans == USB_GADGET_XPORT_BAM) {
+		gbam_free_buffers(port);
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = 0;
+	n_tx_req_queued = 0;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+		/* disable endpoints */
+		usb_ep_disable(gr->out);
+		usb_ep_disable(gr->in);
+	}
+
+	queue_work(gbam_wq, &port->disconnect_w);
+}
+
+int gbam_connect(struct grmnet *gr, u8 port_num,
+				 enum transport_type trans, u8 connection_idx)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+	int			ret;
+	unsigned long		flags;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM)
+		port = bam_ports[port_num].port;
+	else
+		port = bam2bam_ports[port_num];
+
+	d = &port->data_ch;
+
+	if (trans == USB_GADGET_XPORT_BAM) {
+		ret = usb_ep_enable(gr->in);
+		if (ret) {
+			pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+					__func__, gr->in);
+			return ret;
+		}
+		gr->in->driver_data = port;
+
+		ret = usb_ep_enable(gr->out);
+		if (ret) {
+			pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+					__func__, gr->out);
+			gr->in->driver_data = 0;
+			return ret;
+		}
+		gr->out->driver_data = port;
+
+	spin_lock_irqsave(&port->port_lock_ul, flags);
+	spin_lock(&port->port_lock_dl);
+	port->port_usb = gr;
+
+		d->to_host = 0;
+		d->to_modem = 0;
+		d->pending_with_bam = 0;
+		d->tohost_drp_cnt = 0;
+		d->tomodem_drp_cnt = 0;
+	spin_unlock(&port->port_lock_dl);
+	spin_unlock_irqrestore(&port->port_lock_ul, flags);
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM) {
+		port->gr = gr;
+		d->connection_idx = connection_idx;
+	}
+
+	queue_work(gbam_wq, &port->connect_w);
+
+	return 0;
+}
+
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port)
+{
+	int	i;
+	int	ret;
+
+	pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n",
+			  __func__, no_bam_port, no_bam2bam_port);
+
+	if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS
+		|| no_bam2bam_port > BAM2BAM_N_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d,%d\n",
+				__func__, no_bam_port, no_bam2bam_port);
+		return -EINVAL;
+	}
+
+	gbam_wq = alloc_workqueue("k_gbam", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!gbam_wq) {
+		pr_err("%s: Unable to create workqueue gbam_wq\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < no_bam_port; i++) {
+		n_bam_ports++;
+		ret = gbam_port_alloc(i);
+		if (ret) {
+			n_bam_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_bam_ports;
+		}
+	}
+
+	for (i = 0; i < no_bam2bam_port; i++) {
+		n_bam2bam_ports++;
+		ret = gbam2bam_port_alloc(i);
+		if (ret) {
+			n_bam2bam_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_bam_ports;
+		}
+	}
+	gbam_debugfs_init();
+	return 0;
+
+free_bam_ports:
+	for (i = 0; i < n_bam_ports; i++)
+		gbam_port_free(i);
+	for (i = 0; i < n_bam2bam_ports; i++)
+		gbam2bam_port_free(i);
+	destroy_workqueue(gbam_wq);
+
+	return ret;
+}
+
+static int gbam_wake_cb(void *param)
+{
+	struct gbam_port	*port = (struct gbam_port *)param;
+	struct bam_ch_info *d;
+	struct f_rmnet		*dev;
+
+	dev = port_to_rmnet(port->gr);
+	d = &port->data_ch;
+
+	pr_debug("%s: woken up by peer\n", __func__);
+
+	return usb_gadget_wakeup(dev->cdev->gadget);
+}
+
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info *d;
+
+	if (trans != USB_GADGET_XPORT_BAM2BAM)
+		return;
+
+	port = bam2bam_ports[port_num];
+	d = &port->data_ch;
+
+	pr_debug("%s: suspended port %d\n", __func__, port_num);
+
+	usb_bam_register_wake_cb(d->connection_idx, gbam_wake_cb, port);
+}
+
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info *d;
+
+	if (trans != USB_GADGET_XPORT_BAM2BAM)
+		return;
+
+	port = bam2bam_ports[port_num];
+	d = &port->data_ch;
+
+	pr_debug("%s: resumed port %d\n", __func__, port_num);
+
+	usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
+}
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
new file mode 100644
index 0000000..73b4e75
--- /dev/null
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -0,0 +1,328 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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.
+ */
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/bam_dmux.h>
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
+#define BAM2BAM_DATA_N_PORTS	1
+
+static struct workqueue_struct *bam_data_wq;
+static int n_bam2bam_data_ports;
+
+#define SPS_PARAMS_SPS_MODE		BIT(5)
+#define SPS_PARAMS_TBE		        BIT(6)
+#define MSM_VENDOR_ID			BIT(16)
+
+struct data_port {
+	struct usb_function		func;
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+};
+
+struct bam_data_ch_info {
+	unsigned long		flags;
+	unsigned		id;
+
+	struct bam_data_port	*port;
+	struct work_struct	write_tobam_w;
+
+	struct usb_request	*rx_req;
+	struct usb_request	*tx_req;
+
+	u8			src_pipe_idx;
+	u8			dst_pipe_idx;
+	u8			connection_idx;
+};
+
+struct bam_data_port {
+	unsigned			port_num;
+	struct data_port		*port_usb;
+	struct bam_data_ch_info		data_ch;
+
+	struct work_struct		connect_w;
+	struct work_struct		disconnect_w;
+};
+
+struct bam_data_port *bam2bam_data_ports[BAM2BAM_DATA_N_PORTS];
+
+/*------------data_path----------------------------*/
+
+static void bam_data_endless_rx_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_info("status: %d\n", status);
+}
+
+static void bam_data_endless_tx_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_info("status: %d\n", status);
+}
+
+static void bam_data_start_endless_rx(struct bam_data_port *port)
+{
+	struct bam_data_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("error enqueuing transfer, %d\n", status);
+}
+
+static void bam_data_start_endless_tx(struct bam_data_port *port)
+{
+	struct bam_data_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("error enqueuing transfer, %d\n", status);
+}
+
+static void bam2bam_data_disconnect_work(struct work_struct *w)
+{
+	struct bam_data_port *port =
+			container_of(w, struct bam_data_port, disconnect_w);
+
+	pr_info("Enter");
+
+	/* disable endpoints */
+	if (!port->port_usb || !port->port_usb->out || !port->port_usb->in) {
+		pr_err("port_usb->out/in == NULL. Exit");
+		return;
+	}
+	usb_ep_disable(port->port_usb->out);
+	usb_ep_disable(port->port_usb->in);
+
+	port->port_usb->in->driver_data = NULL;
+	port->port_usb->out->driver_data = NULL;
+
+	port->port_usb = 0;
+
+	pr_info("Exit");
+}
+
+static void bam2bam_data_connect_work(struct work_struct *w)
+{
+	struct bam_data_port *port = container_of(w, struct bam_data_port,
+						  connect_w);
+	struct bam_data_ch_info *d = &port->data_ch;
+	u32 sps_params;
+	int ret;
+
+	pr_info("Enter");
+
+	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+						  &d->dst_pipe_idx);
+	d->src_pipe_idx = 11;
+	d->dst_pipe_idx = 10;
+
+	if (ret) {
+		pr_err("usb_bam_connect failed: err:%d\n", ret);
+		return;
+	}
+
+	if (!port->port_usb) {
+		pr_err("port_usb is NULL");
+		return;
+	}
+
+	if (!port->port_usb->out) {
+		pr_err("port_usb->out (bulk out ep) is NULL");
+		return;
+	}
+
+	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+	if (!d->rx_req)
+		return;
+
+	d->rx_req->context = port;
+	d->rx_req->complete = bam_data_endless_rx_complete;
+	d->rx_req->length = 0;
+	sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
+				 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+	d->rx_req->udc_priv = sps_params;
+	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
+	if (!d->tx_req)
+		return;
+
+	d->tx_req->context = port;
+	d->tx_req->complete = bam_data_endless_tx_complete;
+	d->tx_req->length = 0;
+	sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
+				 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+	d->tx_req->udc_priv = sps_params;
+
+	/* queue in & out requests */
+	bam_data_start_endless_rx(port);
+	bam_data_start_endless_tx(port);
+
+	pr_info("Done\n");
+}
+
+static void bam2bam_data_port_free(int portno)
+{
+	kfree(bam2bam_data_ports[portno]);
+	bam2bam_data_ports[portno] = NULL;
+}
+
+static int bam2bam_data_port_alloc(int portno)
+{
+	struct bam_data_port	*port = NULL;
+	struct bam_data_ch_info	*d = NULL;
+
+	port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
+	INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	bam2bam_data_ports[portno] = port;
+
+	pr_info("port:%p portno:%d\n", port, portno);
+
+	return 0;
+}
+
+void bam_data_disconnect(struct data_port *gr, u8 port_num)
+{
+	struct bam_data_port	*port;
+	struct bam_data_ch_info	*d;
+
+	pr_info("dev:%p port#%d\n", gr, port_num);
+
+	if (port_num >= n_bam2bam_data_ports) {
+		pr_err("invalid bam2bam portno#%d\n", port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("mbim data port is null\n");
+		return;
+	}
+
+	port = bam2bam_data_ports[port_num];
+
+	d = &port->data_ch;
+	port->port_usb = gr;
+
+	queue_work(bam_data_wq, &port->disconnect_w);
+}
+
+int bam_data_connect(struct data_port *gr, u8 port_num,
+				 u8 connection_idx)
+{
+	struct bam_data_port	*port;
+	struct bam_data_ch_info	*d;
+	int			ret;
+
+	pr_info("dev:%p port#%d\n", gr, port_num);
+
+	if (port_num >= n_bam2bam_data_ports) {
+		pr_err("invalid portno#%d\n", port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("mbim data port is null\n");
+		return -ENODEV;
+	}
+
+	port = bam2bam_data_ports[port_num];
+
+	d = &port->data_ch;
+
+	ret = usb_ep_enable(gr->in);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
+		return ret;
+	}
+	gr->in->driver_data = port;
+
+	ret = usb_ep_enable(gr->out);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
+		gr->in->driver_data = 0;
+		return ret;
+	}
+	gr->out->driver_data = port;
+
+	port->port_usb = gr;
+
+	d->connection_idx = connection_idx;
+
+	queue_work(bam_data_wq, &port->connect_w);
+
+	return 0;
+}
+
+int bam_data_setup(unsigned int no_bam2bam_port)
+{
+	int	i;
+	int	ret;
+
+	pr_info("requested %d BAM2BAM ports", no_bam2bam_port);
+
+	if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
+		pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
+		return -EINVAL;
+	}
+
+	bam_data_wq = alloc_workqueue("k_bam_data",
+				      WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!bam_data_wq) {
+		pr_err("Failed to create workqueue\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < no_bam2bam_port; i++) {
+		n_bam2bam_data_ports++;
+		ret = bam2bam_data_port_alloc(i);
+		if (ret) {
+			n_bam2bam_data_ports--;
+			pr_err("Failed to alloc port:%d\n", i);
+			goto free_bam_ports;
+		}
+	}
+
+	return 0;
+
+free_bam_ports:
+	for (i = 0; i < n_bam2bam_data_ports; i++)
+		bam2bam_data_port_free(i);
+	destroy_workqueue(bam_data_wq);
+
+	return ret;
+}
+
diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c
new file mode 100644
index 0000000..fdfab96
--- /dev/null
+++ b/drivers/usb/gadget/u_ctrl_hsic.c
@@ -0,0 +1,617 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/usb_bridge.h>
+#include <mach/usb_gadget_xport.h>
+
+/* from cdc-acm.h */
+#define ACM_CTRL_RTS		(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR		(1 << 0)	/* host is ready for data r/w */
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+
+
+static unsigned int	no_ctrl_ports;
+
+static const char	*ctrl_bridge_names[] = {
+	"dun_ctrl_hsic0",
+	"rmnet_ctrl_hsic0"
+};
+
+#define CTRL_BRIDGE_NAME_MAX_LEN	20
+#define READ_BUF_LEN			1024
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct gctrl_port {
+	/* port */
+	unsigned		port_num;
+
+	/* gadget */
+	spinlock_t		port_lock;
+	void			*port_usb;
+
+	/* work queue*/
+	struct workqueue_struct	*wq;
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+
+	enum gadget_type	gtype;
+
+	/*ctrl pkt response cb*/
+	int (*send_cpkt_response)(void *g, void *buf, size_t len);
+
+	struct bridge		brdg;
+
+	/* bridge status */
+	unsigned long		bridge_sts;
+
+	/* control bits */
+	unsigned		cbits_tomodem;
+	unsigned		cbits_tohost;
+
+	/* counters */
+	unsigned long		to_modem;
+	unsigned long		to_host;
+	unsigned long		drp_cpkt_cnt;
+};
+
+static struct {
+	struct gctrl_port	*port;
+	struct platform_driver	pdrv;
+} gctrl_ports[NUM_PORTS];
+
+static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual)
+{
+	struct gctrl_port	*port = dev;
+	int retval = 0;
+
+	pr_debug_ratelimited("%s: read complete bytes read: %d\n",
+			__func__, actual);
+
+	/* send it to USB here */
+	if (port && port->send_cpkt_response) {
+		retval = port->send_cpkt_response(port->port_usb, buf, actual);
+		port->to_host++;
+	}
+
+	return retval;
+}
+
+static int
+ghsic_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
+{
+	void			*cbuf;
+	struct gctrl_port	*port;
+
+	if (portno >= no_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return -ENODEV;
+	}
+
+	port = gctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	cbuf = kmalloc(len, GFP_ATOMIC);
+	if (!cbuf)
+		return -ENOMEM;
+
+	memcpy(cbuf, buf, len);
+
+	/* drop cpkt if ch is not open */
+	if (!test_bit(CH_OPENED, &port->bridge_sts)) {
+		port->drp_cpkt_cnt++;
+		kfree(cbuf);
+		return 0;
+	}
+
+	pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
+
+	ctrl_bridge_write(port->brdg.ch_id, cbuf, len);
+
+	port->to_modem++;
+
+	return 0;
+}
+
+static void
+ghsic_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct gctrl_port	*port;
+
+	if (portno >= no_ctrl_ports || !gptr) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	port = gctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (cbits == port->cbits_tomodem)
+		return;
+
+	port->cbits_tomodem = cbits;
+
+	if (!test_bit(CH_OPENED, &port->bridge_sts))
+		return;
+
+	pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+
+	ctrl_bridge_set_cbits(port->brdg.ch_id, cbits);
+}
+
+static void ghsic_ctrl_connect_w(struct work_struct *w)
+{
+	struct gserial		*gser = NULL;
+	struct grmnet		*gr = NULL;
+	struct gctrl_port	*port =
+			container_of(w, struct gctrl_port, connect_w);
+	unsigned long		flags;
+	int			retval;
+	unsigned		cbits;
+
+	if (!port || !test_bit(CH_READY, &port->bridge_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	retval = ctrl_bridge_open(&port->brdg);
+	if (retval) {
+		pr_err("%s: ctrl bridge open failed :%d\n", __func__, retval);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		ctrl_bridge_close(port->brdg.ch_id);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return;
+	}
+	set_bit(CH_OPENED, &port->bridge_sts);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	cbits = ctrl_bridge_get_cbits_tohost(port->brdg.ch_id);
+
+	if (port->gtype == USB_GADGET_SERIAL && (cbits & ACM_CTRL_DCD)) {
+		gser = port->port_usb;
+		if (gser && gser->connect)
+			gser->connect(gser);
+		return;
+	}
+
+	if (port->gtype == USB_GADGET_RMNET) {
+		gr = port->port_usb;
+		if (gr && gr->connect)
+			gr->connect(gr);
+	}
+}
+
+int ghsic_ctrl_connect(void *gptr, int port_num)
+{
+	struct gctrl_port	*port;
+	struct gserial		*gser;
+	struct grmnet		*gr;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	if (port_num > no_ctrl_ports || !gptr) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	port = gctrl_ports[port_num].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+		gser->notify_modem = ghsic_send_cbits_tomodem;
+	}
+
+	if (port->gtype == USB_GADGET_RMNET) {
+		gr = gptr;
+		port->send_cpkt_response = gr->send_cpkt_response;
+		gr->send_encap_cmd = ghsic_send_cpkt_tomodem;
+		gr->notify_modem = ghsic_send_cbits_tomodem;
+	}
+
+	port->port_usb = gptr;
+	port->to_host = 0;
+	port->to_modem = 0;
+	port->drp_cpkt_cnt = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+static void gctrl_disconnect_w(struct work_struct *w)
+{
+	struct gctrl_port	*port =
+			container_of(w, struct gctrl_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->bridge_sts))
+		return;
+
+	/* send the dtr zero */
+	ctrl_bridge_close(port->brdg.ch_id);
+	clear_bit(CH_OPENED, &port->bridge_sts);
+}
+
+void ghsic_ctrl_disconnect(void *gptr, int port_num)
+{
+	struct gctrl_port	*port;
+	struct gserial		*gser = NULL;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = gctrl_ports[port_num].port;
+
+	if (port_num > no_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL)
+		gser = gptr;
+	 else
+		gr = gptr;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (gr) {
+		gr->send_encap_cmd = 0;
+		gr->notify_modem = 0;
+	}
+
+	if (gser)
+		gser->notify_modem = 0;
+	port->cbits_tomodem = 0;
+	port->port_usb = 0;
+	port->send_cpkt_response = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+static void ghsic_ctrl_status(void *ctxt, unsigned int ctrl_bits)
+{
+	struct gctrl_port	*port = ctxt;
+	struct gserial		*gser;
+
+	pr_debug("%s - input control lines: dcd%c dsr%c break%c "
+		 "ring%c framing%c parity%c overrun%c\n", __func__,
+		 ctrl_bits & ACM_CTRL_DCD ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_DSR ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_BRK ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_RI  ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_PARITY ? '+' : '-',
+		 ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-');
+
+	port->cbits_tohost = ctrl_bits;
+	gser = port->port_usb;
+	if (gser && gser->send_modem_ctrl_bits)
+		gser->send_modem_ctrl_bits(gser, ctrl_bits);
+}
+
+static int ghsic_ctrl_probe(struct platform_device *pdev)
+{
+	struct gctrl_port	*port;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= no_ctrl_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gctrl_ports[pdev->id].port;
+	set_bit(CH_READY, &port->bridge_sts);
+
+	/* if usb is online, start read */
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		queue_work(port->wq, &port->connect_w);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return 0;
+}
+
+static int ghsic_ctrl_remove(struct platform_device *pdev)
+{
+	struct gctrl_port	*port;
+	struct gserial		*gser = NULL;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= no_ctrl_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gctrl_ports[pdev->id].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		goto not_ready;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL)
+		gser = port->port_usb;
+	else
+		gr = port->port_usb;
+
+	port->cbits_tohost = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (gr && gr->disconnect)
+		gr->disconnect(gr);
+
+	if (gser && gser->disconnect)
+		gser->disconnect(gser);
+
+	ctrl_bridge_close(port->brdg.ch_id);
+
+	clear_bit(CH_OPENED, &port->bridge_sts);
+not_ready:
+	clear_bit(CH_READY, &port->bridge_sts);
+
+	return 0;
+}
+
+static void ghsic_ctrl_port_free(int portno)
+{
+	struct gctrl_port	*port = gctrl_ports[portno].port;
+	struct platform_driver	*pdrv = &gctrl_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	kfree(port);
+
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+}
+
+static int gctrl_port_alloc(int portno, enum gadget_type gtype)
+{
+	struct gctrl_port	*port;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, ctrl_bridge_names[portno]);
+		return -ENOMEM;
+	}
+
+	port->port_num = portno;
+	port->gtype = gtype;
+
+	spin_lock_init(&port->port_lock);
+
+	INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w);
+	INIT_WORK(&port->disconnect_w, gctrl_disconnect_w);
+
+	port->brdg.ch_id = portno;
+	port->brdg.ctx = port;
+	port->brdg.ops.send_pkt = ghsic_ctrl_receive;
+	if (port->gtype == USB_GADGET_SERIAL)
+		port->brdg.ops.send_cbits = ghsic_ctrl_status;
+	gctrl_ports[portno].port = port;
+
+	pdrv = &gctrl_ports[portno].pdrv;
+	pdrv->probe = ghsic_ctrl_probe;
+	pdrv->remove = ghsic_ctrl_remove;
+	pdrv->driver.name = ctrl_bridge_names[portno];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
+{
+	int		first_port_id = no_ctrl_ports;
+	int		total_num_ports = num_ports + no_ctrl_ports;
+	int		i;
+	int		ret = 0;
+
+	if (!num_ports || total_num_ports > NUM_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: requested ports:%d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < (first_port_id + num_ports); i++) {
+
+		/*probe can be called while port_alloc,so update no_ctrl_ports*/
+		no_ctrl_ports++;
+		ret = gctrl_port_alloc(i, gtype);
+		if (ret) {
+			no_ctrl_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < no_ctrl_ports; i++)
+		ghsic_ctrl_port_free(i);
+		no_ctrl_ports = first_port_id;
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t gctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gctrl_port	*port;
+	struct platform_driver	*pdrv;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < no_ctrl_ports; i++) {
+		port = gctrl_ports[i].port;
+		if (!port)
+			continue;
+		pdrv = &gctrl_ports[i].pdrv;
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName:        %s\n"
+				"#PORT:%d port: %p\n"
+				"to_usbhost:    %lu\n"
+				"to_modem:      %lu\n"
+				"cpkt_drp_cnt:  %lu\n"
+				"DTR:           %s\n"
+				"ch_open:       %d\n"
+				"ch_ready:      %d\n",
+				pdrv->driver.name,
+				i, port,
+				port->to_host, port->to_modem,
+				port->drp_cpkt_cnt,
+				port->cbits_tomodem ? "HIGH" : "LOW",
+				test_bit(CH_OPENED, &port->bridge_sts),
+				test_bit(CH_READY, &port->bridge_sts));
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t gctrl_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct gctrl_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < no_ctrl_ports; i++) {
+		port = gctrl_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->to_host = 0;
+		port->to_modem = 0;
+		port->drp_cpkt_cnt = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations gctrl_stats_ops = {
+	.read = gctrl_read_stats,
+	.write = gctrl_reset_stats,
+};
+
+struct dentry	*gctrl_dent;
+struct dentry	*gctrl_dfile;
+static void gctrl_debugfs_init(void)
+{
+	gctrl_dent = debugfs_create_dir("ghsic_ctrl_xport", 0);
+	if (IS_ERR(gctrl_dent))
+		return;
+
+	gctrl_dfile =
+		debugfs_create_file("status", 0444, gctrl_dent, 0,
+			&gctrl_stats_ops);
+	if (!gctrl_dfile || IS_ERR(gctrl_dfile))
+		debugfs_remove(gctrl_dent);
+}
+
+static void gctrl_debugfs_exit(void)
+{
+	debugfs_remove(gctrl_dfile);
+	debugfs_remove(gctrl_dent);
+}
+
+#else
+static void gctrl_debugfs_init(void) { }
+static void gctrl_debugfs_exit(void) { }
+#endif
+
+static int __init gctrl_init(void)
+{
+	gctrl_debugfs_init();
+
+	return 0;
+}
+module_init(gctrl_init);
+
+static void __exit gctrl_exit(void)
+{
+	gctrl_debugfs_exit();
+}
+module_exit(gctrl_exit);
+MODULE_DESCRIPTION("hsic control xport driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_ctrl_hsuart.c b/drivers/usb/gadget/u_ctrl_hsuart.c
new file mode 100644
index 0000000..7102d81
--- /dev/null
+++ b/drivers/usb/gadget/u_ctrl_hsuart.c
@@ -0,0 +1,576 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+#include <linux/smux.h>
+
+#include <mach/usb_gadget_xport.h>
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+static unsigned int num_ctrl_ports;
+
+static const char *ghsuart_ctrl_names[] = {
+	"SMUX_RMNET_CTL_HSUART"
+};
+
+struct ghsuart_ctrl_port {
+	/* port */
+	unsigned port_num;
+	/* gadget */
+	enum gadget_type gtype;
+	spinlock_t port_lock;
+	void *port_usb;
+	/* work queue*/
+	struct workqueue_struct	*wq;
+	struct work_struct connect_w;
+	struct work_struct disconnect_w;
+	/*ctrl pkt response cb*/
+	int (*send_cpkt_response)(void *g, void *buf, size_t len);
+	void *ctxt;
+	unsigned int ch_id;
+	/* flow control bits */
+	unsigned long flags;
+	int (*send_pkt)(void *, void *, size_t actual);
+	/* Channel status */
+	unsigned long channel_sts;
+	/* control bits */
+	unsigned cbits_tomodem;
+	/* counters */
+	unsigned long to_modem;
+	unsigned long to_host;
+	unsigned long drp_cpkt_cnt;
+};
+
+static struct {
+	struct ghsuart_ctrl_port	*port;
+	struct platform_driver	pdrv;
+} ghsuart_ctrl_ports[NUM_HSUART_PORTS];
+
+static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual);
+
+static void smux_control_event(void *priv, int event_type, const void *metadata)
+{
+	struct grmnet		*gr = NULL;
+	struct ghsuart_ctrl_port	*port = priv;
+	void			*buf;
+	unsigned long		flags;
+	size_t			len;
+
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (!port->port_usb) {
+			spin_unlock_irqrestore(&port->port_lock, flags);
+			return;
+		}
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		set_bit(CH_OPENED, &port->channel_sts);
+		if (port->gtype == USB_GADGET_RMNET) {
+			gr = port->port_usb;
+			if (gr && gr->connect)
+				gr->connect(gr);
+		}
+		break;
+	case SMUX_DISCONNECTED:
+		clear_bit(CH_OPENED, &port->channel_sts);
+		break;
+	case SMUX_READ_DONE:
+		len = ((struct smux_meta_read *)metadata)->len;
+		buf = ((struct smux_meta_read *)metadata)->buffer;
+		ghsuart_ctrl_receive(port, buf, len);
+		break;
+	case SMUX_READ_FAIL:
+		buf = ((struct smux_meta_read *)metadata)->buffer;
+		kfree(buf);
+		break;
+	case SMUX_WRITE_DONE:
+	case SMUX_WRITE_FAIL:
+		buf = ((struct smux_meta_write *)metadata)->buffer;
+		kfree(buf);
+		break;
+	case SMUX_LOW_WM_HIT:
+	case SMUX_HIGH_WM_HIT:
+	case SMUX_TIOCM_UPDATE:
+		break;
+	default:
+		pr_err("%s Event %d not supported\n", __func__, event_type);
+	};
+}
+
+static int rx_control_buffer(void *priv, void **pkt_priv, void **buffer,
+			int size)
+{
+	void *rx_buf;
+
+	rx_buf = kmalloc(size, GFP_KERNEL);
+	if (!rx_buf)
+		return -EAGAIN;
+	*buffer = rx_buf;
+	*pkt_priv = NULL;
+
+	return 0;
+}
+
+static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual)
+{
+	struct ghsuart_ctrl_port	*port = dev;
+	int retval = 0;
+
+	pr_debug_ratelimited("%s: read complete bytes read: %d\n",
+			__func__, actual);
+
+	/* send it to USB here */
+	if (port && port->send_cpkt_response) {
+		retval = port->send_cpkt_response(port->port_usb, buf, actual);
+		port->to_host++;
+	}
+	kfree(buf);
+	return retval;
+}
+
+static int
+ghsuart_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
+{
+	void			*cbuf;
+	struct ghsuart_ctrl_port	*port;
+	int			ret;
+
+	if (portno >= num_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return -ENODEV;
+	}
+
+	port = ghsuart_ctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+	/* drop cpkt if ch is not open */
+	if (!test_bit(CH_OPENED, &port->channel_sts)) {
+		port->drp_cpkt_cnt++;
+		return 0;
+	}
+	cbuf = kmalloc(len, GFP_ATOMIC);
+	if (!cbuf)
+		return -ENOMEM;
+
+	memcpy(cbuf, buf, len);
+
+	pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
+
+	ret = msm_smux_write(port->ch_id, port, (void *)cbuf, len);
+	if (ret < 0) {
+		pr_err_ratelimited("%s: write error:%d\n",
+				__func__, ret);
+		port->drp_cpkt_cnt++;
+		kfree(cbuf);
+		return ret;
+	}
+	port->to_modem++;
+
+	return 0;
+}
+
+static void
+ghsuart_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct ghsuart_ctrl_port	*port;
+
+	if (portno >= num_ctrl_ports || !gptr) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	port = ghsuart_ctrl_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (cbits == port->cbits_tomodem)
+		return;
+
+	port->cbits_tomodem = cbits;
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+	/* Send the control bits to the Modem */
+	msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
+}
+
+static void ghsuart_ctrl_connect_w(struct work_struct *w)
+{
+	struct ghsuart_ctrl_port	*port =
+			container_of(w, struct ghsuart_ctrl_port, connect_w);
+	int			retval;
+
+	if (!port || !test_bit(CH_READY, &port->channel_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	retval = msm_smux_open(port->ch_id, port->ctxt, smux_control_event,
+				rx_control_buffer);
+	if (retval < 0) {
+		pr_err(" %s smux_open failed\n", __func__);
+		return;
+	}
+
+}
+
+int ghsuart_ctrl_connect(void *gptr, int port_num)
+{
+	struct ghsuart_ctrl_port	*port;
+	struct grmnet		*gr;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	if (port_num > num_ctrl_ports || !gptr) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	port = ghsuart_ctrl_ports[port_num].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	gr = gptr;
+	port->send_cpkt_response = gr->send_cpkt_response;
+	gr->send_encap_cmd = ghsuart_send_cpkt_tomodem;
+	gr->notify_modem = ghsuart_send_cbits_tomodem;
+
+	port->port_usb = gptr;
+	port->to_host = 0;
+	port->to_modem = 0;
+	port->drp_cpkt_cnt = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (test_bit(CH_READY, &port->channel_sts))
+		queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+static void ghsuart_ctrl_disconnect_w(struct work_struct *w)
+{
+	struct ghsuart_ctrl_port	*port =
+			container_of(w, struct ghsuart_ctrl_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	msm_smux_close(port->ch_id);
+	clear_bit(CH_OPENED, &port->channel_sts);
+}
+
+void ghsuart_ctrl_disconnect(void *gptr, int port_num)
+{
+	struct gctrl_port	*port;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	if (port_num > num_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	port = gctrl_ports[port_num].port;
+
+	if (!gptr || !port) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+
+	gr = gptr;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	gr->send_encap_cmd = 0;
+	gr->notify_modem = 0;
+	port->cbits_tomodem = 0;
+	port->port_usb = 0;
+	port->send_cpkt_response = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+static int ghsuart_ctrl_probe(struct platform_device *pdev)
+{
+	struct ghsuart_ctrl_port	*port;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	port = ghsuart_ctrl_ports[pdev->id].port;
+	set_bit(CH_READY, &port->channel_sts);
+
+	/* if usb is online, start read */
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		queue_work(port->wq, &port->connect_w);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return 0;
+}
+
+static int ghsuart_ctrl_remove(struct platform_device *pdev)
+{
+	struct ghsuart_ctrl_port	*port;
+	struct grmnet		*gr = NULL;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	port = ghsuart_ctrl_ports[pdev->id].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		goto not_ready;
+	}
+
+	gr = port->port_usb;
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (gr && gr->disconnect)
+		gr->disconnect(gr);
+
+	clear_bit(CH_OPENED, &port->channel_sts);
+not_ready:
+	clear_bit(CH_READY, &port->channel_sts);
+
+	return 0;
+}
+
+static void ghsuart_ctrl_port_free(int portno)
+{
+	struct ghsuart_ctrl_port	*port = ghsuart_ctrl_ports[portno].port;
+	struct platform_driver	*pdrv = &gctrl_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+	kfree(port);
+}
+
+static int ghsuart_ctrl_port_alloc(int portno, enum gadget_type gtype)
+{
+	struct ghsuart_ctrl_port	*port;
+	struct platform_driver	*pdrv;
+	int err;
+
+	port = kzalloc(sizeof(struct ghsuart_ctrl_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(ghsuart_ctrl_names[portno]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, ghsuart_ctrl_names[portno]);
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	port->port_num = portno;
+	port->gtype = gtype;
+
+	spin_lock_init(&port->port_lock);
+
+	INIT_WORK(&port->connect_w, ghsuart_ctrl_connect_w);
+	INIT_WORK(&port->disconnect_w, ghsuart_ctrl_disconnect_w);
+
+	port->ch_id = SMUX_USB_RMNET_CTL_0;
+	port->ctxt = port;
+	port->send_pkt = ghsuart_ctrl_receive;
+	ghsuart_ctrl_ports[portno].port = port;
+
+	pdrv = &ghsuart_ctrl_ports[portno].pdrv;
+	pdrv->probe = ghsuart_ctrl_probe;
+	pdrv->remove = ghsuart_ctrl_remove;
+	pdrv->driver.name = ghsuart_ctrl_names[portno];
+	pdrv->driver.owner = THIS_MODULE;
+
+	err = platform_driver_register(pdrv);
+	if (unlikely(err < 0))
+		return err;
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+int ghsuart_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
+{
+	int	first_port_id = num_ctrl_ports;
+	int	total_num_ports = num_ports + num_ctrl_ports;
+	int	i;
+	int	ret = 0;
+
+	if (!num_ports || total_num_ports > NUM_HSUART_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: requested ports:%d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < (first_port_id + num_ports); i++) {
+
+		num_ctrl_ports++;
+		ret = ghsuart_ctrl_port_alloc(i, gtype);
+		if (ret) {
+			num_ctrl_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < num_ctrl_ports; i++)
+		ghsuart_ctrl_port_free(i);
+		num_ctrl_ports = first_port_id;
+	return ret;
+}
+
+#define DEBUG_BUF_SIZE	1024
+static ssize_t ghsuart_ctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct ghsuart_ctrl_port	*port;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < num_ctrl_ports; i++) {
+		port = ghsuart_ctrl_ports[i].port;
+		if (!port)
+			continue;
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"#PORT:%d port: %p\n"
+				"to_usbhost:    %lu\n"
+				"to_modem:      %lu\n"
+				"cpkt_drp_cnt:  %lu\n"
+				"DTR:           %s\n",
+				i, port,
+				port->to_host, port->to_modem,
+				port->drp_cpkt_cnt,
+				port->cbits_tomodem ? "HIGH" : "LOW");
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ghsuart_ctrl_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ghsuart_ctrl_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < num_ctrl_ports; i++) {
+		port = ghsuart_ctrl_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->to_host = 0;
+		port->to_modem = 0;
+		port->drp_cpkt_cnt = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+	return count;
+}
+
+static const struct file_operations ghsuart_ctrl_stats_ops = {
+	.read = ghsuart_ctrl_read_stats,
+	.write = ghsuart_ctrl_reset_stats,
+};
+
+static struct dentry	*ghsuart_ctrl_dent;
+static int ghsuart_ctrl_debugfs_init(void)
+{
+	struct dentry	*ghsuart_ctrl_dfile;
+
+	ghsuart_ctrl_dent = debugfs_create_dir("ghsuart_ctrl_xport", 0);
+	if (!ghsuart_ctrl_dent || IS_ERR(ghsuart_ctrl_dent))
+		return -ENODEV;
+
+	ghsuart_ctrl_dfile =
+		debugfs_create_file("status", S_IRUGO | S_IWUSR,
+				ghsuart_ctrl_dent, 0, &gctrl_stats_ops);
+	if (!ghsuart_ctrl_dfile || IS_ERR(ghsuart_ctrl_dfile)) {
+		debugfs_remove(ghsuart_ctrl_dent);
+		ghsuart_ctrl_dent = NULL;
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void ghsuart_ctrl_debugfs_exit(void)
+{
+	debugfs_remove_recursive(ghsuart_ctrl_dent);
+}
+
+static int __init ghsuart_ctrl_init(void)
+{
+	int ret;
+
+	ret = ghsuart_ctrl_debugfs_init();
+	if (ret) {
+		pr_debug("mode debugfs file is not available\n");
+		return ret;
+	}
+	return 0;
+}
+module_init(ghsuart_ctrl_init);
+
+static void __exit ghsuart_ctrl_exit(void)
+{
+	ghsuart_ctrl_debugfs_exit();
+}
+module_exit(ghsuart_ctrl_exit);
+
+MODULE_DESCRIPTION("HSUART control xport for RmNet");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
new file mode 100644
index 0000000..89d2887
--- /dev/null
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -0,0 +1,1143 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/usb_bridge.h>
+#include <mach/usb_gadget_xport.h>
+
+static unsigned int no_data_ports;
+
+static const char *data_bridge_names[] = {
+	"dun_data_hsic0",
+	"rmnet_data_hsic0"
+};
+
+#define DATA_BRIDGE_NAME_MAX_LEN		20
+
+#define GHSIC_DATA_RMNET_RX_Q_SIZE		50
+#define GHSIC_DATA_RMNET_TX_Q_SIZE		300
+#define GHSIC_DATA_SERIAL_RX_Q_SIZE		10
+#define GHSIC_DATA_SERIAL_TX_Q_SIZE		20
+#define GHSIC_DATA_RX_REQ_SIZE			2048
+#define GHSIC_DATA_TX_INTR_THRESHOLD		20
+
+static unsigned int ghsic_data_rmnet_tx_q_size = GHSIC_DATA_RMNET_TX_Q_SIZE;
+module_param(ghsic_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_rmnet_rx_q_size = GHSIC_DATA_RMNET_RX_Q_SIZE;
+module_param(ghsic_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_serial_tx_q_size = GHSIC_DATA_SERIAL_TX_Q_SIZE;
+module_param(ghsic_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_serial_rx_q_size = GHSIC_DATA_SERIAL_RX_Q_SIZE;
+module_param(ghsic_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_rx_req_size = GHSIC_DATA_RX_REQ_SIZE;
+module_param(ghsic_data_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int ghsic_data_tx_intr_thld = GHSIC_DATA_TX_INTR_THRESHOLD;
+module_param(ghsic_data_tx_intr_thld, uint, S_IRUGO | S_IWUSR);
+
+/*flow ctrl*/
+#define GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD	500
+#define GHSIC_DATA_FLOW_CTRL_DISABLE		300
+#define GHSIC_DATA_FLOW_CTRL_SUPPORT		1
+#define GHSIC_DATA_PENDLIMIT_WITH_BRIDGE	500
+
+static unsigned int ghsic_data_fctrl_support = GHSIC_DATA_FLOW_CTRL_SUPPORT;
+module_param(ghsic_data_fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_fctrl_en_thld =
+		GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD;
+module_param(ghsic_data_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_fctrl_dis_thld = GHSIC_DATA_FLOW_CTRL_DISABLE;
+module_param(ghsic_data_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_pend_limit_with_bridge =
+		GHSIC_DATA_PENDLIMIT_WITH_BRIDGE;
+module_param(ghsic_data_pend_limit_with_bridge, uint, S_IRUGO | S_IWUSR);
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct gdata_port {
+	/* port */
+	unsigned		port_num;
+
+	/* gadget */
+	atomic_t		connected;
+	struct usb_ep		*in;
+	struct usb_ep		*out;
+
+	enum gadget_type	gtype;
+
+	/* data transfer queues */
+	unsigned int		tx_q_size;
+	struct list_head	tx_idle;
+	struct sk_buff_head	tx_skb_q;
+	spinlock_t		tx_lock;
+
+	unsigned int		rx_q_size;
+	struct list_head	rx_idle;
+	struct sk_buff_head	rx_skb_q;
+	spinlock_t		rx_lock;
+
+	/* work */
+	struct workqueue_struct	*wq;
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+	struct work_struct	write_tomdm_w;
+	struct work_struct	write_tohost_w;
+
+	struct bridge		brdg;
+
+	/*bridge status*/
+	unsigned long		bridge_sts;
+
+	unsigned int		n_tx_req_queued;
+
+	/*counters*/
+	unsigned long		to_modem;
+	unsigned long		to_host;
+	unsigned int		rx_throttled_cnt;
+	unsigned int		rx_unthrottled_cnt;
+	unsigned int		tx_throttled_cnt;
+	unsigned int		tx_unthrottled_cnt;
+	unsigned int		tomodem_drp_cnt;
+	unsigned int		unthrottled_pnd_skbs;
+};
+
+static struct {
+	struct gdata_port	*port;
+	struct platform_driver	pdrv;
+} gdata_ports[NUM_PORTS];
+
+static unsigned int get_timestamp(void);
+static void dbg_timestamp(char *, struct sk_buff *);
+static void ghsic_data_start_rx(struct gdata_port *port);
+
+static void ghsic_data_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num,
+		void (*cb)(struct usb_ep *ep, struct usb_request *),
+		gfp_t flags)
+{
+	int			i;
+	struct usb_request	*req;
+
+	pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
+			ep->name, head, num, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_ep_alloc_request(ep, flags);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void ghsic_data_unthrottle_tx(void *ctx)
+{
+	struct gdata_port	*port = ctx;
+	unsigned long		flags;
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->tx_unthrottled_cnt++;
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->write_tomdm_w);
+	pr_debug("%s: port num =%d unthrottled\n", __func__,
+		port->port_num);
+}
+
+static void ghsic_data_write_tohost(struct work_struct *w)
+{
+	unsigned long		flags;
+	struct sk_buff		*skb;
+	int			ret;
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	struct gdata_port	*port;
+	struct timestamp_info	*info;
+
+	port = container_of(w, struct gdata_port, write_tohost_w);
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	while (!list_empty(&port->tx_idle)) {
+		skb = __skb_dequeue(&port->tx_skb_q);
+		if (!skb)
+			break;
+
+		req = list_first_entry(&port->tx_idle, struct usb_request,
+				list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		port->n_tx_req_queued++;
+		if (port->n_tx_req_queued == ghsic_data_tx_intr_thld) {
+			req->no_interrupt = 0;
+			port->n_tx_req_queued = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+
+		list_del(&req->list);
+
+		info = (struct timestamp_info *)skb->cb;
+		info->tx_queued = get_timestamp();
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->tx_lock, flags);
+		if (ret) {
+			pr_err("%s: usb epIn failed\n", __func__);
+			list_add(&req->list, &port->tx_idle);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_host++;
+		if (ghsic_data_fctrl_support &&
+			port->tx_skb_q.qlen <= ghsic_data_fctrl_dis_thld &&
+			test_and_clear_bit(RX_THROTTLED, &port->brdg.flags)) {
+			port->rx_unthrottled_cnt++;
+			port->unthrottled_pnd_skbs = port->tx_skb_q.qlen;
+			pr_debug_ratelimited("%s: disable flow ctrl:"
+					" tx skbq len: %u\n",
+					__func__, port->tx_skb_q.qlen);
+			data_bridge_unthrottle_rx(port->brdg.ch_id);
+		}
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+}
+
+static int ghsic_data_receive(void *p, void *data, size_t len)
+{
+	struct gdata_port	*port = p;
+	unsigned long		flags;
+	struct sk_buff		*skb = data;
+
+	if (!port || !atomic_read(&port->connected)) {
+		dev_kfree_skb_any(skb);
+		return -ENOTCONN;
+	}
+
+	pr_debug("%s: p:%p#%d skb_len:%d\n", __func__,
+			port, port->port_num, skb->len);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	__skb_queue_tail(&port->tx_skb_q, skb);
+
+	if (ghsic_data_fctrl_support &&
+			port->tx_skb_q.qlen >= ghsic_data_fctrl_en_thld) {
+		set_bit(RX_THROTTLED, &port->brdg.flags);
+		port->rx_throttled_cnt++;
+		pr_debug_ratelimited("%s: flow ctrl enabled: tx skbq len: %u\n",
+					__func__, port->tx_skb_q.qlen);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		queue_work(port->wq, &port->write_tohost_w);
+		return -EBUSY;
+	}
+
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	queue_work(port->wq, &port->write_tohost_w);
+
+	return 0;
+}
+
+static void ghsic_data_write_tomdm(struct work_struct *w)
+{
+	struct gdata_port	*port;
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	unsigned long		flags;
+	int			ret;
+
+	port = container_of(w, struct gdata_port, write_tomdm_w);
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (test_bit(TX_THROTTLED, &port->brdg.flags)) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		goto start_rx;
+	}
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q))) {
+		pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__,
+				port, port->to_modem, port->port_num);
+
+		info = (struct timestamp_info *)skb->cb;
+		info->rx_done_sent = get_timestamp();
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = data_bridge_write(port->brdg.ch_id, skb);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret < 0) {
+			if (ret == -EBUSY) {
+				/*flow control*/
+				port->tx_throttled_cnt++;
+				break;
+			}
+			pr_err_ratelimited("%s: write error:%d\n",
+					__func__, ret);
+			port->tomodem_drp_cnt++;
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_modem++;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+start_rx:
+	ghsic_data_start_rx(port);
+}
+
+static void ghsic_data_epin_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gdata_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		dbg_timestamp("DL", skb);
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err("%s: data tx ep error %d\n", __func__, status);
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	spin_lock(&port->tx_lock);
+	list_add_tail(&req->list, &port->tx_idle);
+	spin_unlock(&port->tx_lock);
+
+	queue_work(port->wq, &port->write_tohost_w);
+}
+
+static void
+ghsic_data_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gdata_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	int			status = req->status;
+	int			queue = 0;
+
+	switch (status) {
+	case 0:
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* cable disconnection */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err_ratelimited("%s: %s response error %d, %d/%d\n",
+					__func__, ep->name, status,
+				req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		break;
+	}
+
+	spin_lock(&port->rx_lock);
+	if (queue) {
+		info->rx_done = get_timestamp();
+		__skb_queue_tail(&port->rx_skb_q, skb);
+		list_add_tail(&req->list, &port->rx_idle);
+		queue_work(port->wq, &port->write_tomdm_w);
+	}
+	spin_unlock(&port->rx_lock);
+}
+
+static void ghsic_data_start_rx(struct gdata_port *port)
+{
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	unsigned long		flags;
+	int			ret;
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	unsigned int		created;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	while (atomic_read(&port->connected) && !list_empty(&port->rx_idle)) {
+		if (port->rx_skb_q.qlen > ghsic_data_pend_limit_with_bridge)
+			break;
+
+		req = list_first_entry(&port->rx_idle,
+					struct usb_request, list);
+
+		created = get_timestamp();
+		skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC);
+		if (!skb)
+			break;
+		info = (struct timestamp_info *)skb->cb;
+		info->created = created;
+		list_del(&req->list);
+		req->buf = skb->data;
+		req->length = ghsic_data_rx_req_size;
+		req->context = skb;
+
+		info->rx_queued = get_timestamp();
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+
+			pr_err_ratelimited("%s: rx queue failed\n", __func__);
+
+			if (atomic_read(&port->connected))
+				list_add(&req->list, &port->rx_idle);
+			else
+				usb_ep_free_request(ep, req);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static void ghsic_data_start_io(struct gdata_port *port)
+{
+	unsigned long	flags;
+	struct usb_ep	*ep;
+	int		ret;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ret = ghsic_data_alloc_requests(ep, &port->rx_idle,
+		port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: rx req allocation failed\n", __func__);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ret = ghsic_data_alloc_requests(ep, &port->tx_idle,
+		port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: tx req allocation failed\n", __func__);
+		ghsic_data_free_requests(ep, &port->rx_idle);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	/* queue out requests */
+	ghsic_data_start_rx(port);
+}
+
+static void ghsic_data_connect_w(struct work_struct *w)
+{
+	struct gdata_port	*port =
+		container_of(w, struct gdata_port, connect_w);
+	int			ret;
+
+	if (!port || !atomic_read(&port->connected) ||
+		!test_bit(CH_READY, &port->bridge_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	ret = data_bridge_open(&port->brdg);
+	if (ret) {
+		pr_err("%s: unable open bridge ch:%d err:%d\n",
+				__func__, port->brdg.ch_id, ret);
+		return;
+	}
+
+	set_bit(CH_OPENED, &port->bridge_sts);
+
+	ghsic_data_start_io(port);
+}
+
+static void ghsic_data_disconnect_w(struct work_struct *w)
+{
+	struct gdata_port	*port =
+		container_of(w, struct gdata_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->bridge_sts))
+		return;
+
+	data_bridge_close(port->brdg.ch_id);
+	clear_bit(CH_OPENED, &port->bridge_sts);
+}
+
+static void ghsic_data_free_buffers(struct gdata_port *port)
+{
+	struct sk_buff	*skb;
+	unsigned long	flags;
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	if (!port->in) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ghsic_data_free_requests(port->in, &port->tx_idle);
+
+	while ((skb = __skb_dequeue(&port->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (!port->out) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ghsic_data_free_requests(port->out, &port->rx_idle);
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static int ghsic_data_probe(struct platform_device *pdev)
+{
+	struct gdata_port *port;
+
+	pr_debug("%s: name:%s no_data_ports= %d\n",
+		__func__, pdev->name, no_data_ports);
+
+	if (pdev->id >= no_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gdata_ports[pdev->id].port;
+	set_bit(CH_READY, &port->bridge_sts);
+
+	/* if usb is online, try opening bridge */
+	if (atomic_read(&port->connected))
+		queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+/* mdm disconnect */
+static int ghsic_data_remove(struct platform_device *pdev)
+{
+	struct gdata_port *port;
+	struct usb_ep	*ep_in;
+	struct usb_ep	*ep_out;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= no_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = gdata_ports[pdev->id].port;
+
+	ep_in = port->in;
+	if (ep_in)
+		usb_ep_fifo_flush(ep_in);
+
+	ep_out = port->out;
+	if (ep_out)
+		usb_ep_fifo_flush(ep_out);
+
+	ghsic_data_free_buffers(port);
+
+	data_bridge_close(port->brdg.ch_id);
+
+	clear_bit(CH_READY, &port->bridge_sts);
+	clear_bit(CH_OPENED, &port->bridge_sts);
+
+	return 0;
+}
+
+static void ghsic_data_port_free(int portno)
+{
+	struct gdata_port	*port = gdata_ports[portno].port;
+	struct platform_driver	*pdrv = &gdata_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	kfree(port);
+
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+}
+
+static int ghsic_data_port_alloc(unsigned port_num, enum gadget_type gtype)
+{
+	struct gdata_port	*port;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(data_bridge_names[port_num]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, data_bridge_names[port_num]);
+		kfree(port);
+		return -ENOMEM;
+	}
+	port->port_num = port_num;
+
+	/* port initialization */
+	spin_lock_init(&port->rx_lock);
+	spin_lock_init(&port->tx_lock);
+
+	INIT_WORK(&port->connect_w, ghsic_data_connect_w);
+	INIT_WORK(&port->disconnect_w, ghsic_data_disconnect_w);
+	INIT_WORK(&port->write_tohost_w, ghsic_data_write_tohost);
+	INIT_WORK(&port->write_tomdm_w, ghsic_data_write_tomdm);
+
+	INIT_LIST_HEAD(&port->tx_idle);
+	INIT_LIST_HEAD(&port->rx_idle);
+
+	skb_queue_head_init(&port->tx_skb_q);
+	skb_queue_head_init(&port->rx_skb_q);
+
+	port->gtype = gtype;
+	port->brdg.ch_id = port_num;
+	port->brdg.ctx = port;
+	port->brdg.ops.send_pkt = ghsic_data_receive;
+	port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx;
+	gdata_ports[port_num].port = port;
+
+	pdrv = &gdata_ports[port_num].pdrv;
+	pdrv->probe = ghsic_data_probe;
+	pdrv->remove = ghsic_data_remove;
+	pdrv->driver.name = data_bridge_names[port_num];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num);
+
+	return 0;
+}
+
+void ghsic_data_disconnect(void *gptr, int port_num)
+{
+	struct gdata_port	*port;
+	unsigned long		flags;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = gdata_ports[port_num].port;
+
+	if (port_num > no_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	ghsic_data_free_buffers(port);
+
+	/* disable endpoints */
+	if (port->in)
+		usb_ep_disable(port->out);
+
+	if (port->out)
+		usb_ep_disable(port->in);
+
+	atomic_set(&port->connected, 0);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->in = NULL;
+	port->n_tx_req_queued = 0;
+	clear_bit(RX_THROTTLED, &port->brdg.flags);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->out = NULL;
+	clear_bit(TX_THROTTLED, &port->brdg.flags);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+int ghsic_data_connect(void *gptr, int port_num)
+{
+	struct gdata_port		*port;
+	struct gserial			*gser;
+	struct grmnet			*gr;
+	unsigned long			flags;
+	int				ret = 0;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = gdata_ports[port_num].port;
+
+	if (port_num > no_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gser->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gser->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		port->tx_q_size = ghsic_data_serial_tx_q_size;
+		port->rx_q_size = ghsic_data_serial_rx_q_size;
+		gser->in->driver_data = port;
+		gser->out->driver_data = port;
+	} else {
+		gr = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gr->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gr->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		port->tx_q_size = ghsic_data_rmnet_tx_q_size;
+		port->rx_q_size = ghsic_data_rmnet_rx_q_size;
+		gr->in->driver_data = port;
+		gr->out->driver_data = port;
+	}
+
+	ret = usb_ep_enable(port->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, port->in);
+		goto fail;
+	}
+
+	ret = usb_ep_enable(port->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, port->out);
+		usb_ep_disable(port->in);
+		goto fail;
+	}
+
+	atomic_set(&port->connected, 1);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->to_host = 0;
+	port->rx_throttled_cnt = 0;
+	port->rx_unthrottled_cnt = 0;
+	port->unthrottled_pnd_skbs = 0;
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->to_modem = 0;
+	port->tomodem_drp_cnt = 0;
+	port->tx_throttled_cnt = 0;
+	port->tx_unthrottled_cnt = 0;
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->connect_w);
+fail:
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+
+static unsigned int	record_timestamp;
+module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
+
+static struct timestamp_buf dbg_data = {
+	.idx = 0,
+	.lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+/*get_timestamp - returns time of day in us */
+static unsigned int get_timestamp(void)
+{
+	struct timeval	tval;
+	unsigned int	stamp;
+
+	if (!record_timestamp)
+		return 0;
+
+	do_gettimeofday(&tval);
+	/* 2^32 = 4294967296. Limit to 4096s. */
+	stamp = tval.tv_sec & 0xFFF;
+	stamp = stamp * 1000000 + tval.tv_usec;
+	return stamp;
+}
+
+static void dbg_inc(unsigned *idx)
+{
+	*idx = (*idx + 1) & (DBG_DATA_MAX-1);
+}
+
+/**
+* dbg_timestamp - Stores timestamp values of a SKB life cycle
+*	to debug buffer
+* @event: "DL": Downlink Data
+* @skb: SKB used to store timestamp values to debug buffer
+*/
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	unsigned long		flags;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+
+	if (!record_timestamp)
+		return;
+
+	write_lock_irqsave(&dbg_data.lck, flags);
+
+	scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
+		  "%p %u[%s] %u %u %u %u %u %u\n",
+		  skb, skb->len, event, info->created, info->rx_queued,
+		  info->rx_done, info->rx_done_sent, info->tx_queued,
+		  get_timestamp());
+
+	dbg_inc(&dbg_data.idx);
+
+	write_unlock_irqrestore(&dbg_data.lck, flags);
+}
+
+/* show_timestamp: displays the timestamp buffer */
+static ssize_t show_timestamp(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	unsigned long	flags;
+	unsigned	i;
+	unsigned	j = 0;
+	char		*buf;
+	int		ret = 0;
+
+	if (!record_timestamp)
+		return 0;
+
+	buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	read_lock_irqsave(&dbg_data.lck, flags);
+
+	i = dbg_data.idx;
+	for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
+		if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
+			continue;
+		j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+			       "%s\n", dbg_data.buf[i]);
+	}
+
+	read_unlock_irqrestore(&dbg_data.lck, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, j);
+
+	kfree(buf);
+
+	return ret;
+}
+
+const struct file_operations gdata_timestamp_ops = {
+	.read = show_timestamp,
+};
+
+static ssize_t ghsic_data_read_stats(struct file *file,
+	char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct gdata_port	*port;
+	struct platform_driver	*pdrv;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < no_data_ports; i++) {
+		port = gdata_ports[i].port;
+		if (!port)
+			continue;
+		pdrv = &gdata_ports[i].pdrv;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName:           %s\n"
+				"#PORT:%d port#:   %p\n"
+				"data_ch_open:	   %d\n"
+				"data_ch_ready:    %d\n"
+				"\n******UL INFO*****\n\n"
+				"dpkts_to_modem:   %lu\n"
+				"tomodem_drp_cnt:  %u\n"
+				"rx_buf_len:       %u\n"
+				"tx thld cnt       %u\n"
+				"tx unthld cnt     %u\n"
+				"TX_THROTTLED      %d\n",
+				pdrv->driver.name,
+				i, port,
+				test_bit(CH_OPENED, &port->bridge_sts),
+				test_bit(CH_READY, &port->bridge_sts),
+				port->to_modem,
+				port->tomodem_drp_cnt,
+				port->rx_skb_q.qlen,
+				port->tx_throttled_cnt,
+				port->tx_unthrottled_cnt,
+				test_bit(TX_THROTTLED, &port->brdg.flags));
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\n******DL INFO******\n\n"
+				"dpkts_to_usbhost: %lu\n"
+				"tx_buf_len:	   %u\n"
+				"rx thld cnt	   %u\n"
+				"rx unthld cnt	   %u\n"
+				"uthld pnd skbs    %u\n"
+				"RX_THROTTLED	   %d\n",
+				port->to_host,
+				port->tx_skb_q.qlen,
+				port->rx_throttled_cnt,
+				port->rx_unthrottled_cnt,
+				port->unthrottled_pnd_skbs,
+				test_bit(RX_THROTTLED, &port->brdg.flags));
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ghsic_data_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct gdata_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < no_data_ports; i++) {
+		port = gdata_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->to_modem = 0;
+		port->tomodem_drp_cnt = 0;
+		port->tx_throttled_cnt = 0;
+		port->tx_unthrottled_cnt = 0;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->to_host = 0;
+		port->rx_throttled_cnt = 0;
+		port->rx_unthrottled_cnt = 0;
+		port->unthrottled_pnd_skbs = 0;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations ghsic_stats_ops = {
+	.read = ghsic_data_read_stats,
+	.write = ghsic_data_reset_stats,
+};
+
+static struct dentry	*gdata_dent;
+static struct dentry	*gdata_dfile_stats;
+static struct dentry	*gdata_dfile_tstamp;
+
+static void ghsic_data_debugfs_init(void)
+{
+	gdata_dent = debugfs_create_dir("ghsic_data_xport", 0);
+	if (IS_ERR(gdata_dent))
+		return;
+
+	gdata_dfile_stats = debugfs_create_file("status", 0444, gdata_dent, 0,
+			&ghsic_stats_ops);
+	if (!gdata_dfile_stats || IS_ERR(gdata_dfile_stats)) {
+		debugfs_remove(gdata_dent);
+		return;
+	}
+
+	gdata_dfile_tstamp = debugfs_create_file("timestamp", 0644, gdata_dent,
+				0, &gdata_timestamp_ops);
+		if (!gdata_dfile_tstamp || IS_ERR(gdata_dfile_tstamp))
+			debugfs_remove(gdata_dent);
+}
+
+static void ghsic_data_debugfs_exit(void)
+{
+	debugfs_remove(gdata_dfile_stats);
+	debugfs_remove(gdata_dfile_tstamp);
+	debugfs_remove(gdata_dent);
+}
+
+#else
+static void ghsic_data_debugfs_init(void) { }
+static void ghsic_data_debugfs_exit(void) { }
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	return;
+}
+static unsigned int get_timestamp(void)
+{
+	return 0;
+}
+
+#endif
+
+int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype)
+{
+	int		first_port_id = no_data_ports;
+	int		total_num_ports = num_ports + no_data_ports;
+	int		ret = 0;
+	int		i;
+
+	if (!num_ports || total_num_ports > NUM_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+	pr_debug("%s: count: %d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < (num_ports + first_port_id); i++) {
+
+		/*probe can be called while port_alloc,so update no_data_ports*/
+		no_data_ports++;
+		ret = ghsic_data_port_alloc(i, gtype);
+		if (ret) {
+			no_data_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	/*return the starting index*/
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < no_data_ports; i++)
+		ghsic_data_port_free(i);
+		no_data_ports = first_port_id;
+
+	return ret;
+}
+
+static int __init ghsic_data_init(void)
+{
+	ghsic_data_debugfs_init();
+
+	return 0;
+}
+module_init(ghsic_data_init);
+
+static void __exit ghsic_data_exit(void)
+{
+	ghsic_data_debugfs_exit();
+}
+module_exit(ghsic_data_exit);
+MODULE_DESCRIPTION("hsic data xport driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_data_hsuart.c b/drivers/usb/gadget/u_data_hsuart.c
new file mode 100644
index 0000000..b2c57c4
--- /dev/null
+++ b/drivers/usb/gadget/u_data_hsuart.c
@@ -0,0 +1,1142 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/smux.h>
+
+#include <mach/usb_gadget_xport.h>
+
+static unsigned int num_data_ports;
+
+static const char *ghsuart_data_names[] = {
+	"SMUX_DUN_DATA_HSUART",
+	"SMUX_RMNET_DATA_HSUART"
+};
+
+#define DATA_BRIDGE_NAME_MAX_LEN		20
+
+#define GHSUART_DATA_RMNET_RX_Q_SIZE		10
+#define GHSUART_DATA_RMNET_TX_Q_SIZE		20
+#define GHSUART_DATA_SERIAL_RX_Q_SIZE		5
+#define GHSUART_DATA_SERIAL_TX_Q_SIZE		5
+#define GHSUART_DATA_RX_REQ_SIZE		2048
+#define GHSUART_DATA_TX_INTR_THRESHOLD		1
+
+/* from cdc-acm.h */
+#define ACM_CTRL_RTS		(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR		(1 << 0)	/* host is ready for data r/w */
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+
+static unsigned int ghsuart_data_rmnet_tx_q_size = GHSUART_DATA_RMNET_TX_Q_SIZE;
+module_param(ghsuart_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_rmnet_rx_q_size = GHSUART_DATA_RMNET_RX_Q_SIZE;
+module_param(ghsuart_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_serial_tx_q_size =
+					GHSUART_DATA_SERIAL_TX_Q_SIZE;
+module_param(ghsuart_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_serial_rx_q_size =
+				GHSUART_DATA_SERIAL_RX_Q_SIZE;
+module_param(ghsuart_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_rx_req_size = GHSUART_DATA_RX_REQ_SIZE;
+module_param(ghsuart_data_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int ghsuart_data_tx_intr_thld = GHSUART_DATA_TX_INTR_THRESHOLD;
+module_param(ghsuart_data_tx_intr_thld, uint, S_IRUGO | S_IWUSR);
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct ghsuart_data_port {
+	/* port */
+	unsigned		port_num;
+
+	/* gadget */
+	atomic_t		connected;
+	struct usb_ep		*in;
+	struct usb_ep		*out;
+
+	enum gadget_type	gtype;
+	spinlock_t		port_lock;
+	void *port_usb;
+
+	/* data transfer queues */
+	unsigned int		tx_q_size;
+	struct list_head	tx_idle;
+	struct sk_buff_head	tx_skb_q;
+	spinlock_t		tx_lock;
+
+	unsigned int		rx_q_size;
+	struct list_head	rx_idle;
+	struct sk_buff_head	rx_skb_q;
+	spinlock_t		rx_lock;
+
+	/* work */
+	struct workqueue_struct	*wq;
+	struct work_struct	connect_w;
+	struct work_struct	disconnect_w;
+	struct work_struct	write_tomdm_w;
+	struct work_struct	write_tohost_w;
+	void *ctx;
+	unsigned int ch_id;
+	/* flow control bits */
+	unsigned long flags;
+	/* channel status */
+	unsigned long		channel_sts;
+
+	unsigned int		n_tx_req_queued;
+
+	/* control bits */
+	unsigned		cbits_tomodem;
+	unsigned		cbits_tohost;
+
+	/* counters */
+	unsigned long		to_modem;
+	unsigned long		to_host;
+	unsigned int		tomodem_drp_cnt;
+};
+
+static struct {
+	struct ghsuart_data_port	*port;
+	struct platform_driver	pdrv;
+} ghsuart_data_ports[NUM_HSUART_PORTS];
+
+static void ghsuart_data_start_rx(struct ghsuart_data_port *port);
+
+static void ghsuart_data_free_requests(struct usb_ep *ep,
+				 struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int ghsuart_data_alloc_requests(struct usb_ep *ep,
+		struct list_head *head,
+		int num,
+		void (*cb)(struct usb_ep *ep, struct usb_request *),
+		gfp_t flags)
+{
+	int			i;
+	struct usb_request	*req;
+
+	pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
+			ep->name, head, num, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_ep_alloc_request(ep, flags);
+		if (!req) {
+			pr_err("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void ghsuart_data_write_tohost(struct work_struct *w)
+{
+	unsigned long		flags;
+	struct sk_buff		*skb;
+	int			ret;
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	struct ghsuart_data_port	*port;
+
+	port = container_of(w, struct ghsuart_data_port, write_tohost_w);
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	while (!list_empty(&port->tx_idle)) {
+		skb = __skb_dequeue(&port->tx_skb_q);
+		if (!skb)
+			break;
+
+		req = list_first_entry(&port->tx_idle, struct usb_request,
+				list);
+		req->context = skb;
+		req->buf = skb->data;
+		req->length = skb->len;
+
+		port->n_tx_req_queued++;
+		if (port->n_tx_req_queued == ghsuart_data_tx_intr_thld) {
+			req->no_interrupt = 0;
+			port->n_tx_req_queued = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+
+		list_del(&req->list);
+
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->tx_lock, flags);
+		if (ret) {
+			pr_err("%s: usb epIn failed\n", __func__);
+			list_add(&req->list, &port->tx_idle);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_host++;
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+}
+
+static void ghsuart_data_write_tomdm(struct work_struct *w)
+{
+	struct ghsuart_data_port	*port;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	int			ret;
+
+	port = container_of(w, struct ghsuart_data_port, write_tomdm_w);
+
+	if (!port || !atomic_read(&port->connected))
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (test_bit(TX_THROTTLED, &port->flags)) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q))) {
+		pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__,
+				port, port->to_modem, port->port_num);
+
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = msm_smux_write(port->ch_id, skb, skb->data, skb->len);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret < 0) {
+			if (ret == -EAGAIN) {
+				/*flow control*/
+				set_bit(TX_THROTTLED, &port->flags);
+				__skb_queue_head(&port->rx_skb_q, skb);
+				break;
+			}
+			pr_err_ratelimited("%s: write error:%d\n",
+					__func__, ret);
+			port->tomodem_drp_cnt++;
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		port->to_modem++;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+	ghsuart_data_start_rx(port);
+}
+
+static void ghsuart_data_epin_complete(struct usb_ep *ep,
+				struct usb_request *req)
+{
+	struct ghsuart_data_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:
+		/* successful completion */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err("%s: data tx ep error %d\n", __func__, status);
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	spin_lock(&port->tx_lock);
+	list_add_tail(&req->list, &port->tx_idle);
+	spin_unlock(&port->tx_lock);
+
+	queue_work(port->wq, &port->write_tohost_w);
+}
+
+static void
+ghsuart_data_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ghsuart_data_port	*port = ep->driver_data;
+	struct sk_buff		*skb = req->context;
+	int			status = req->status;
+	int			queue = 0;
+
+	switch (status) {
+	case 0:
+		skb_put(skb, req->actual);
+		queue = 1;
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* cable disconnection */
+		dev_kfree_skb_any(skb);
+		req->buf = 0;
+		usb_ep_free_request(ep, req);
+		return;
+	default:
+		pr_err_ratelimited("%s: %s response error %d, %d/%d\n",
+					__func__, ep->name, status,
+				req->actual, req->length);
+		dev_kfree_skb_any(skb);
+		list_add_tail(&req->list, &port->rx_idle);
+		return;
+	}
+
+	spin_lock(&port->rx_lock);
+	if (queue) {
+		__skb_queue_tail(&port->rx_skb_q, skb);
+		list_add_tail(&req->list, &port->rx_idle);
+		queue_work(port->wq, &port->write_tomdm_w);
+	}
+	spin_unlock(&port->rx_lock);
+}
+
+static void ghsuart_data_start_rx(struct ghsuart_data_port *port)
+{
+	struct usb_request	*req;
+	struct usb_ep		*ep;
+	unsigned long		flags;
+	int			ret;
+	struct sk_buff		*skb;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	if (test_bit(TX_THROTTLED, &port->flags)) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	while (atomic_read(&port->connected) && !list_empty(&port->rx_idle)) {
+
+		req = list_first_entry(&port->rx_idle,
+					struct usb_request, list);
+
+		skb = alloc_skb(ghsuart_data_rx_req_size, GFP_ATOMIC);
+		if (!skb)
+			break;
+		list_del(&req->list);
+		req->buf = skb->data;
+		req->length = ghsuart_data_rx_req_size;
+		req->context = skb;
+
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->rx_lock, flags);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+
+			pr_err_ratelimited("%s: rx queue failed\n", __func__);
+
+			if (atomic_read(&port->connected))
+				list_add(&req->list, &port->rx_idle);
+			else
+				usb_ep_free_request(ep, req);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static void ghsuart_data_start_io(struct ghsuart_data_port *port)
+{
+	unsigned long	flags;
+	struct usb_ep	*ep;
+	int		ret;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	ep = port->out;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ret = ghsuart_data_alloc_requests(ep, &port->rx_idle,
+		port->rx_q_size, ghsuart_data_epout_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: rx req allocation failed\n", __func__);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	ep = port->in;
+	if (!ep) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ret = ghsuart_data_alloc_requests(ep, &port->tx_idle,
+		port->tx_q_size, ghsuart_data_epin_complete, GFP_ATOMIC);
+	if (ret) {
+		pr_err("%s: tx req allocation failed\n", __func__);
+		ghsuart_data_free_requests(ep, &port->rx_idle);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	/* queue out requests */
+	ghsuart_data_start_rx(port);
+}
+
+static void ghsuart_dunctrl_status(void *ctxt, unsigned int ctrl_bits)
+{
+	struct ghsuart_data_port  *port = ctxt;
+	struct gserial          *gser;
+	unsigned long	flags;
+
+	pr_debug("%s - input control lines: dcd%c dsr%c break%c "
+	"ring%c framing%c parity%c overrun%c\n", __func__,
+	ctrl_bits & ACM_CTRL_DCD ? '+' : '-',
+	ctrl_bits & ACM_CTRL_DSR ? '+' : '-',
+	ctrl_bits & ACM_CTRL_BRK ? '+' : '-',
+	ctrl_bits & ACM_CTRL_RI  ? '+' : '-',
+	ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-',
+	ctrl_bits & ACM_CTRL_PARITY ? '+' : '-',
+	ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-');
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->cbits_tohost = ctrl_bits;
+	gser = port->port_usb;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+	if (gser && gser->send_modem_ctrl_bits)
+		gser->send_modem_ctrl_bits(gser, ctrl_bits);
+}
+
+const char *event_string(int event_type)
+{
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		return "SMUX_CONNECTED";
+	case SMUX_DISCONNECTED:
+		return "SMUX_DISCONNECTED";
+	case SMUX_READ_DONE:
+		return "SMUX_READ_DONE";
+	case SMUX_READ_FAIL:
+		return "SMUX_READ_FAIL";
+	case SMUX_WRITE_DONE:
+		return "SMUX_WRITE_DONE";
+	case SMUX_WRITE_FAIL:
+		return "SMUX_WRITE_FAIL";
+	case SMUX_HIGH_WM_HIT:
+		return "SMUX_HIGH_WM_HIT";
+	case SMUX_LOW_WM_HIT:
+		return "SMUX_LOW_WM_HIT";
+	case SMUX_TIOCM_UPDATE:
+		return "SMUX_TIOCM_UPDATE";
+	default:
+		return "UNDEFINED";
+	}
+}
+
+static void ghsuart_notify_event(void *priv, int event_type,
+				const void *metadata)
+{
+	struct ghsuart_data_port	*port = priv;
+	struct smux_meta_write *meta_write =
+				(struct smux_meta_write *) metadata;
+	struct smux_meta_read *meta_read =
+				(struct smux_meta_read *) metadata;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	unsigned int		cbits;
+	struct gserial		*gser;
+
+	pr_debug("%s: event type: %s ", __func__, event_string(event_type));
+	switch (event_type) {
+	case SMUX_CONNECTED:
+		set_bit(CH_OPENED, &port->channel_sts);
+		if (port->gtype == USB_GADGET_SERIAL) {
+			cbits = msm_smux_tiocm_get(port->ch_id);
+			if (cbits & ACM_CTRL_DCD) {
+				gser = port->port_usb;
+				if (gser && gser->connect)
+					gser->connect(gser);
+			}
+		}
+		ghsuart_data_start_io(port);
+		break;
+	case SMUX_DISCONNECTED:
+		clear_bit(CH_OPENED, &port->channel_sts);
+		break;
+	case SMUX_READ_DONE:
+		skb = meta_read->pkt_priv;
+		skb->data = meta_read->buffer;
+		skb->len = meta_read->len;
+		spin_lock_irqsave(&port->tx_lock, flags);
+		__skb_queue_tail(&port->tx_skb_q, skb);
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		queue_work(port->wq, &port->write_tohost_w);
+		break;
+	case SMUX_WRITE_DONE:
+		skb = meta_write->pkt_priv;
+		skb->data = meta_write->buffer;
+		dev_kfree_skb_any(skb);
+		queue_work(port->wq, &port->write_tomdm_w);
+		break;
+	case SMUX_READ_FAIL:
+		skb = meta_read->pkt_priv;
+		skb->data = meta_read->buffer;
+		dev_kfree_skb_any(skb);
+		break;
+	case SMUX_WRITE_FAIL:
+		skb = meta_write->pkt_priv;
+		skb->data = meta_write->buffer;
+		dev_kfree_skb_any(skb);
+		break;
+	case SMUX_HIGH_WM_HIT:
+		spin_lock_irqsave(&port->rx_lock, flags);
+		set_bit(TX_THROTTLED, &port->flags);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+	case SMUX_LOW_WM_HIT:
+		spin_lock_irqsave(&port->rx_lock, flags);
+		clear_bit(TX_THROTTLED, &port->flags);
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		queue_work(port->wq, &port->write_tomdm_w);
+		break;
+	case SMUX_TIOCM_UPDATE:
+		if (port->gtype == USB_GADGET_SERIAL) {
+			cbits = msm_smux_tiocm_get(port->ch_id);
+			ghsuart_dunctrl_status(port, cbits);
+		}
+		break;
+	default:
+		pr_err("%s:wrong event recieved\n", __func__);
+	}
+}
+
+static int ghsuart_get_rx_buffer(void *priv, void **pkt_priv,
+			void **buffer, int size)
+{
+	struct sk_buff		*skb;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+	*pkt_priv = skb;
+	*buffer = skb->data;
+
+	return 0;
+}
+
+static void ghsuart_data_connect_w(struct work_struct *w)
+{
+	struct ghsuart_data_port	*port =
+		container_of(w, struct ghsuart_data_port, connect_w);
+	int			ret;
+
+	if (!port || !atomic_read(&port->connected) ||
+		!test_bit(CH_READY, &port->channel_sts))
+		return;
+
+	pr_debug("%s: port:%p\n", __func__, port);
+
+	ret = msm_smux_open(port->ch_id, port, &ghsuart_notify_event,
+				&ghsuart_get_rx_buffer);
+	if (ret) {
+		pr_err("%s: unable to open smux ch:%d err:%d\n",
+				__func__, port->ch_id, ret);
+		return;
+	}
+}
+
+static void ghsuart_data_disconnect_w(struct work_struct *w)
+{
+	struct ghsuart_data_port	*port =
+		container_of(w, struct ghsuart_data_port, disconnect_w);
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	msm_smux_close(port->ch_id);
+	clear_bit(CH_OPENED, &port->channel_sts);
+}
+
+static void ghsuart_data_free_buffers(struct ghsuart_data_port *port)
+{
+	struct sk_buff	*skb;
+	unsigned long	flags;
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	if (!port->in) {
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+		return;
+	}
+
+	ghsuart_data_free_requests(port->in, &port->tx_idle);
+
+	while ((skb = __skb_dequeue(&port->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	if (!port->out) {
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+		return;
+	}
+
+	ghsuart_data_free_requests(port->out, &port->rx_idle);
+
+	while ((skb = __skb_dequeue(&port->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static int ghsuart_data_probe(struct platform_device *pdev)
+{
+	struct ghsuart_data_port *port;
+
+	pr_debug("%s: name:%s num_data_ports= %d\n",
+		__func__, pdev->name, num_data_ports);
+
+	if (pdev->id >= num_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = ghsuart_data_ports[pdev->id].port;
+	set_bit(CH_READY, &port->channel_sts);
+
+	/* if usb is online, try opening bridge */
+	if (atomic_read(&port->connected))
+		queue_work(port->wq, &port->connect_w);
+
+	return 0;
+}
+
+/* mdm disconnect */
+static int ghsuart_data_remove(struct platform_device *pdev)
+{
+	struct ghsuart_data_port *port;
+	struct usb_ep	*ep_in;
+	struct usb_ep	*ep_out;
+	int ret;
+	struct gserial		*gser = NULL;
+	unsigned long	flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	if (pdev->id >= num_data_ports) {
+		pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+		return -EINVAL;
+	}
+
+	port = ghsuart_data_ports[pdev->id].port;
+
+	ep_in = port->in;
+	if (ep_in)
+		usb_ep_fifo_flush(ep_in);
+
+	ep_out = port->out;
+	if (ep_out)
+		usb_ep_fifo_flush(ep_out);
+
+	ghsuart_data_free_buffers(port);
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		spin_lock_irqsave(&port->port_lock, flags);
+		gser = port->port_usb;
+		port->cbits_tohost = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		if (gser && gser->disconnect)
+			gser->disconnect(gser);
+	}
+
+	ret = msm_smux_close(port->ch_id);
+	if (ret < 0)
+		pr_err("%s:Unable to close smux channel: %d\n",
+				__func__, port->ch_id);
+
+	clear_bit(CH_READY, &port->channel_sts);
+	clear_bit(CH_OPENED, &port->channel_sts);
+
+	return 0;
+}
+
+static void ghsuart_data_port_free(int portno)
+{
+	struct ghsuart_data_port	*port = ghsuart_data_ports[portno].port;
+	struct platform_driver	*pdrv = &ghsuart_data_ports[portno].pdrv;
+
+	destroy_workqueue(port->wq);
+	kfree(port);
+
+	if (pdrv)
+		platform_driver_unregister(pdrv);
+}
+
+static void
+ghsuart_send_controlbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct ghsuart_data_port	*port;
+
+	if (portno >= num_ctrl_ports || !gptr) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	port = ghsuart_data_ports[portno].port;
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (cbits == port->cbits_tomodem)
+		return;
+
+	port->cbits_tomodem = cbits;
+
+	if (!test_bit(CH_OPENED, &port->channel_sts))
+		return;
+
+	/* if DTR is high, update latest modem info to Host */
+	if (port->cbits_tomodem & ACM_CTRL_DTR) {
+		unsigned int i;
+
+		i = msm_smux_tiocm_get(port->ch_id);
+		ghsuart_dunctrl_status(port, i);
+	}
+
+	pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+	/* Send the control bits to the Modem */
+	msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
+}
+
+static int ghsuart_data_port_alloc(unsigned port_num, enum gadget_type gtype)
+{
+	struct ghsuart_data_port	*port;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct ghsuart_data_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->wq = create_singlethread_workqueue(ghsuart_data_names[port_num]);
+	if (!port->wq) {
+		pr_err("%s: Unable to create workqueue:%s\n",
+			__func__, ghsuart_data_names[port_num]);
+		kfree(port);
+		return -ENOMEM;
+	}
+	port->port_num = port_num;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock);
+	spin_lock_init(&port->rx_lock);
+	spin_lock_init(&port->tx_lock);
+
+	INIT_WORK(&port->connect_w, ghsuart_data_connect_w);
+	INIT_WORK(&port->disconnect_w, ghsuart_data_disconnect_w);
+	INIT_WORK(&port->write_tohost_w, ghsuart_data_write_tohost);
+	INIT_WORK(&port->write_tomdm_w, ghsuart_data_write_tomdm);
+
+	INIT_LIST_HEAD(&port->tx_idle);
+	INIT_LIST_HEAD(&port->rx_idle);
+
+	skb_queue_head_init(&port->tx_skb_q);
+	skb_queue_head_init(&port->rx_skb_q);
+
+	port->gtype = gtype;
+	if (port->gtype == USB_GADGET_SERIAL)
+		port->ch_id = SMUX_USB_DUN_0;
+	else
+		port->ch_id = SMUX_USB_RMNET_DATA_0;
+	port->ctx = port;
+	ghsuart_data_ports[port_num].port = port;
+
+	pdrv = &ghsuart_data_ports[port_num].pdrv;
+	pdrv->probe = ghsuart_data_probe;
+	pdrv->remove = ghsuart_data_remove;
+	pdrv->driver.name = ghsuart_data_names[port_num];
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num);
+
+	return 0;
+}
+
+void ghsuart_data_disconnect(void *gptr, int port_num)
+{
+	struct ghsuart_data_port	*port;
+	unsigned long		flags;
+	struct gserial		*gser = NULL;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = ghsuart_data_ports[port_num].port;
+
+	if (port_num > num_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	ghsuart_data_free_buffers(port);
+
+	/* disable endpoints */
+	if (port->in)
+		usb_ep_disable(port->in);
+
+	if (port->out)
+		usb_ep_disable(port->out);
+
+	atomic_set(&port->connected, 0);
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+		spin_lock_irqsave(&port->port_lock, flags);
+		gser->notify_modem = 0;
+		port->cbits_tomodem = 0;
+		port->port_usb = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->in = NULL;
+	port->n_tx_req_queued = 0;
+	clear_bit(RX_THROTTLED, &port->flags);
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->out = NULL;
+	clear_bit(TX_THROTTLED, &port->flags);
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->disconnect_w);
+}
+
+int ghsuart_data_connect(void *gptr, int port_num)
+{
+	struct ghsuart_data_port		*port;
+	struct gserial			*gser;
+	struct grmnet			*gr;
+	unsigned long			flags;
+	int				ret = 0;
+
+	pr_debug("%s: port#%d\n", __func__, port_num);
+
+	port = ghsuart_data_ports[port_num].port;
+
+	if (port_num > num_data_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gptr || !port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (port->gtype == USB_GADGET_SERIAL) {
+		gser = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gser->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gser->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+
+		port->tx_q_size = ghsuart_data_serial_tx_q_size;
+		port->rx_q_size = ghsuart_data_serial_rx_q_size;
+		gser->in->driver_data = port;
+		gser->out->driver_data = port;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		gser->notify_modem = ghsuart_send_controlbits_tomodem;
+		port->port_usb = gptr;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	} else {
+		gr = gptr;
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->in = gr->in;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->out = gr->out;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		port->tx_q_size = ghsuart_data_rmnet_tx_q_size;
+		port->rx_q_size = ghsuart_data_rmnet_rx_q_size;
+		gr->in->driver_data = port;
+		gr->out->driver_data = port;
+	}
+
+	ret = usb_ep_enable(port->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, port->in);
+		goto fail;
+	}
+
+	ret = usb_ep_enable(port->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, port->out);
+		usb_ep_disable(port->in);
+		goto fail;
+	}
+
+	atomic_set(&port->connected, 1);
+
+	spin_lock_irqsave(&port->tx_lock, flags);
+	port->to_host = 0;
+	spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	spin_lock_irqsave(&port->rx_lock, flags);
+	port->to_modem = 0;
+	port->tomodem_drp_cnt = 0;
+	spin_unlock_irqrestore(&port->rx_lock, flags);
+
+	queue_work(port->wq, &port->connect_w);
+fail:
+	return ret;
+}
+
+#define DEBUG_BUF_SIZE 1024
+static ssize_t ghsuart_data_read_stats(struct file *file,
+	char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct ghsuart_data_port	*port;
+	struct platform_driver	*pdrv;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < num_data_ports; i++) {
+		port = ghsuart_data_ports[i].port;
+		if (!port)
+			continue;
+		pdrv = &ghsuart_data_ports[i].pdrv;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName:           %s\n"
+				"#PORT:%d port#:   %p\n"
+				"data_ch_open:	   %d\n"
+				"data_ch_ready:    %d\n"
+				"\n******UL INFO*****\n\n"
+				"dpkts_to_modem:   %lu\n"
+				"tomodem_drp_cnt:  %u\n"
+				"rx_buf_len:       %u\n"
+				"TX_THROTTLED      %d\n",
+				pdrv->driver.name,
+				i, port,
+				test_bit(CH_OPENED, &port->channel_sts),
+				test_bit(CH_READY, &port->channel_sts),
+				port->to_modem,
+				port->tomodem_drp_cnt,
+				port->rx_skb_q.qlen,
+				test_bit(TX_THROTTLED, &port->flags));
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\n******DL INFO******\n\n"
+				"dpkts_to_usbhost: %lu\n"
+				"tx_buf_len:	   %u\n"
+				"RX_THROTTLED	   %d\n",
+				port->to_host,
+				port->tx_skb_q.qlen,
+				test_bit(RX_THROTTLED, &port->flags));
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ghsuart_data_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ghsuart_data_port	*port;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < num_data_ports; i++) {
+		port = ghsuart_data_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->rx_lock, flags);
+		port->to_modem = 0;
+		port->tomodem_drp_cnt = 0;
+		spin_unlock_irqrestore(&port->rx_lock, flags);
+
+		spin_lock_irqsave(&port->tx_lock, flags);
+		port->to_host = 0;
+		spin_unlock_irqrestore(&port->tx_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations ghsuart_data_stats_ops = {
+	.read = ghsuart_data_read_stats,
+	.write = ghsuart_data_reset_stats,
+};
+
+static struct dentry	*ghsuart_data_dent;
+static int ghsuart_data_debugfs_init(void)
+{
+	struct dentry	 *ghsuart_data_dfile;
+
+	ghsuart_data_dent = debugfs_create_dir("ghsic_data_xport", 0);
+	if (!ghsuart_data_dent || IS_ERR(ghsuart_data_dent))
+		return -ENODEV;
+
+	ghsuart_data_dfile = debugfs_create_file("status", S_IRUGO | S_IWUSR,
+				 ghsuart_data_dent, 0, &ghsuart_data_stats_ops);
+	if (!ghsuart_data_dfile || IS_ERR(ghsuart_data_dfile)) {
+		debugfs_remove(ghsuart_data_dent);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void ghsuart_data_debugfs_exit(void)
+{
+	debugfs_remove_recursive(ghsuart_data_dent);
+}
+
+int ghsuart_data_setup(unsigned num_ports, enum gadget_type gtype)
+{
+	int		first_port_id = num_data_ports;
+	int		total_num_ports = num_ports + num_data_ports;
+	int		ret = 0;
+	int		i;
+
+	if (!num_ports || total_num_ports > NUM_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, num_ports);
+		return -EINVAL;
+	}
+	pr_debug("%s: count: %d\n", __func__, num_ports);
+
+	for (i = first_port_id; i < total_num_ports; i++) {
+
+		/*probe can be called while port_alloc,so update no_data_ports*/
+		num_data_ports++;
+		ret = ghsuart_data_port_alloc(i, gtype);
+		if (ret) {
+			num_data_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_ports;
+		}
+	}
+
+	/*return the starting index*/
+	return first_port_id;
+
+free_ports:
+	for (i = first_port_id; i < num_data_ports; i++)
+		ghsuart_data_port_free(i);
+		num_data_ports = first_port_id;
+
+	return ret;
+}
+
+static int __init ghsuart_data_init(void)
+{
+	int ret;
+
+	ret = ghsuart_data_debugfs_init();
+	if (ret) {
+		pr_debug("mode debugfs file is not available");
+		return ret;
+	}
+
+	return 0;
+}
+module_init(ghsuart_data_init);
+
+static void __exit ghsuart_data_exit(void)
+{
+	ghsuart_data_debugfs_exit();
+}
+module_exit(ghsuart_data_exit);
+
+MODULE_DESCRIPTION("hsuart data xport driver for DUN and RMNET");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index c8702c8..9e3301f 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -58,7 +58,7 @@
 
 	spinlock_t		req_lock;	/* guard {rx,tx}_reqs */
 	struct list_head	tx_reqs, rx_reqs;
-	atomic_t		tx_qlen;
+	unsigned		tx_qlen;
 
 	struct sk_buff_head	rx_frames;
 
@@ -476,7 +476,6 @@
 	spin_unlock(&dev->req_lock);
 	dev_kfree_skb_any(skb);
 
-	atomic_dec(&dev->tx_qlen);
 	if (netif_carrier_ok(dev->net))
 		netif_wake_queue(dev->net);
 }
@@ -590,12 +589,19 @@
 
 	req->length = length;
 
-	/* throttle high/super speed IRQ rate back slightly */
-	if (gadget_is_dualspeed(dev->gadget))
-		req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
-				     dev->gadget->speed == USB_SPEED_SUPER)
-			? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
-			: 0;
+	/* throttle highspeed IRQ rate back slightly */
+	if (gadget_is_dualspeed(dev->gadget) &&
+			 (dev->gadget->speed == USB_SPEED_HIGH)) {
+		dev->tx_qlen++;
+		if (dev->tx_qlen == qmult) {
+			req->no_interrupt = 0;
+			dev->tx_qlen = 0;
+		} else {
+			req->no_interrupt = 1;
+		}
+	} else {
+		req->no_interrupt = 0;
+	}
 
 	retval = usb_ep_queue(in, req, GFP_ATOMIC);
 	switch (retval) {
@@ -604,7 +610,6 @@
 		break;
 	case 0:
 		net->trans_start = jiffies;
-		atomic_inc(&dev->tx_qlen);
 	}
 
 	if (retval) {
@@ -630,7 +635,7 @@
 	rx_fill(dev, gfp_flags);
 
 	/* and open the tx floodgates */
-	atomic_set(&dev->tx_qlen, 0);
+	dev->tx_qlen = 0;
 	netif_wake_queue(dev->net);
 }
 
@@ -956,7 +961,6 @@
 	struct eth_dev		*dev = link->ioport;
 	struct usb_request	*req;
 
-	WARN_ON(!dev);
 	if (!dev)
 		return;
 
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
new file mode 100644
index 0000000..0f7c4fb
--- /dev/null
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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.
+ */
+
+#ifndef __U_RMNET_H
+#define __U_RMNET_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+struct rmnet_ctrl_pkt {
+	void			*buf;
+	int			len;
+	struct list_head	list;
+};
+
+struct grmnet {
+	struct usb_function		func;
+
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+
+	/* to usb host, aka laptop, windows pc etc. Will
+	 * be filled by usb driver of rmnet functionality
+	 */
+	int (*send_cpkt_response)(void *g, void *buf, size_t len);
+
+	/* to modem, and to be filled by driver implementing
+	 * control function
+	 */
+	int (*send_encap_cmd)(u8 port_num, void *buf, size_t len);
+
+	void (*notify_modem)(void *g, u8 port_num, int cbits);
+
+	void (*disconnect)(struct grmnet *g);
+	void (*connect)(struct grmnet *g);
+};
+
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port);
+int gbam_connect(struct grmnet *gr, u8 port_num,
+				 enum transport_type trans, u8 connection_idx);
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
+int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
+void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
+int gsmd_ctrl_setup(unsigned int count);
+
+#endif /* __U_RMNET_H*/
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
new file mode 100644
index 0000000..0256a75
--- /dev/null
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <mach/msm_smd.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+
+#include "u_rmnet.h"
+
+#define NR_CTRL_SMD_PORTS	3
+static int n_rmnet_ctrl_ports;
+static char *rmnet_ctrl_names[] = {"DATA40_CNTL", "DATA39_CNTL", "DATA38_CNTL"};
+static struct workqueue_struct *grmnet_ctrl_wq;
+
+#define SMD_CH_MAX_LEN	20
+#define CH_OPENED	0
+#define CH_READY	1
+struct smd_ch_info {
+	struct smd_channel	*ch;
+	char			*name;
+	unsigned long		flags;
+	wait_queue_head_t	wait;
+	unsigned		dtr;
+
+	struct list_head	tx_q;
+	unsigned long		tx_len;
+
+	struct work_struct	read_w;
+	struct work_struct	write_w;
+
+	struct rmnet_ctrl_port	*port;
+
+	int			cbits_tomodem;
+	/* stats */
+	unsigned long		to_modem;
+	unsigned long		to_host;
+};
+
+struct rmnet_ctrl_port {
+	struct smd_ch_info	ctrl_ch;
+	unsigned int		port_num;
+	struct grmnet		*port_usb;
+
+	spinlock_t		port_lock;
+	struct delayed_work	connect_w;
+};
+
+static struct rmnet_ctrl_ports {
+	struct rmnet_ctrl_port *port;
+	struct platform_driver pdrv;
+} ctrl_smd_ports[NR_CTRL_SMD_PORTS];
+
+
+/*---------------misc functions---------------- */
+
+static struct rmnet_ctrl_pkt *alloc_rmnet_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct rmnet_ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void free_rmnet_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
+{
+	kfree(pkt->buf);
+	kfree(pkt);
+}
+
+/*--------------------------------------------- */
+
+/*---------------control/smd channel functions---------------- */
+
+static void grmnet_ctrl_smd_read_w(struct work_struct *w)
+{
+	struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
+	struct rmnet_ctrl_port *port = c->port;
+	int sz;
+	size_t len;
+	void *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	while (c->ch) {
+		sz = smd_cur_packet_size(c->ch);
+		if (sz <= 0)
+			break;
+
+		if (smd_read_avail(c->ch) < sz)
+			break;
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+
+		buf = kmalloc(sz, GFP_KERNEL);
+		if (!buf)
+			return;
+
+		len = smd_read(c->ch, buf, sz);
+
+		/* send it to USB here */
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (port->port_usb && port->port_usb->send_cpkt_response) {
+			port->port_usb->send_cpkt_response(port->port_usb,
+							buf, len);
+			c->to_host++;
+		}
+		kfree(buf);
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void grmnet_ctrl_smd_write_w(struct work_struct *w)
+{
+	struct smd_ch_info *c = container_of(w, struct smd_ch_info, write_w);
+	struct rmnet_ctrl_port *port = c->port;
+	unsigned long flags;
+	struct rmnet_ctrl_pkt *cpkt;
+	int ret;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	while (c->ch) {
+		if (list_empty(&c->tx_q))
+			break;
+
+		cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
+
+		if (smd_write_avail(c->ch) < cpkt->len)
+			break;
+
+		list_del(&cpkt->list);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		ret = smd_write(c->ch, cpkt->buf, cpkt->len);
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (ret != cpkt->len) {
+			pr_err("%s: smd_write failed err:%d\n", __func__, ret);
+			free_rmnet_ctrl_pkt(cpkt);
+			break;
+		}
+		free_rmnet_ctrl_pkt(cpkt);
+		c->to_modem++;
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int
+grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno,
+	void *buf, size_t len)
+{
+	unsigned long		flags;
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	struct rmnet_ctrl_pkt *cpkt;
+
+	if (portno >= n_rmnet_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return -ENODEV;
+	}
+
+	port = ctrl_smd_ports[portno].port;
+
+	cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC);
+	if (IS_ERR(cpkt)) {
+		pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(cpkt->buf, buf, len);
+	cpkt->len = len;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	c = &port->ctrl_ch;
+
+	/* drop cpkt if ch is not open */
+	if (!test_bit(CH_OPENED, &c->flags)) {
+		free_rmnet_ctrl_pkt(cpkt);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return 0;
+	}
+
+	list_add_tail(&cpkt->list, &c->tx_q);
+	queue_work(grmnet_ctrl_wq, &c->write_w);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return 0;
+}
+
+#define RMNET_CTRL_DTR		0x01
+static void
+gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			set_bits = 0;
+	int			clear_bits = 0;
+	int			temp = 0;
+
+	if (portno >= n_rmnet_ctrl_ports) {
+		pr_err("%s: Invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gptr) {
+		pr_err("%s: grmnet is null\n", __func__);
+		return;
+	}
+
+	port = ctrl_smd_ports[portno].port;
+	cbits = cbits & RMNET_CTRL_DTR;
+	c = &port->ctrl_ch;
+
+	/* host driver will only send DTR, but to have generic
+	 * set and clear bit implementation using two separate
+	 * checks
+	 */
+	if (cbits & RMNET_CTRL_DTR)
+		set_bits |= TIOCM_DTR;
+	else
+		clear_bits |= TIOCM_DTR;
+
+	temp |= set_bits;
+	temp &= ~clear_bits;
+
+	if (temp == c->cbits_tomodem)
+		return;
+
+	c->cbits_tomodem = temp;
+
+	if (!test_bit(CH_OPENED, &c->flags))
+		return;
+
+	pr_debug("%s: ctrl_tomodem:%d ctrl_bits:%d setbits:%d clearbits:%d\n",
+			__func__, temp, cbits, set_bits, clear_bits);
+
+	smd_tiocmset(c->ch, set_bits, clear_bits);
+}
+
+static char *get_smd_event(unsigned event)
+{
+	switch (event) {
+	case SMD_EVENT_DATA:
+		return "DATA";
+	case SMD_EVENT_OPEN:
+		return "OPEN";
+	case SMD_EVENT_CLOSE:
+		return "CLOSE";
+	}
+
+	return "UNDEFINED";
+}
+
+static void grmnet_ctrl_smd_notify(void *p, unsigned event)
+{
+	struct rmnet_ctrl_port	*port = p;
+	struct smd_ch_info	*c = &port->ctrl_ch;
+	struct rmnet_ctrl_pkt	*cpkt;
+	unsigned long		flags;
+
+	pr_debug("%s: EVENT_(%s)\n", __func__, get_smd_event(event));
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		if (smd_read_avail(c->ch))
+			queue_work(grmnet_ctrl_wq, &c->read_w);
+		if (smd_write_avail(c->ch))
+			queue_work(grmnet_ctrl_wq, &c->write_w);
+		break;
+	case SMD_EVENT_OPEN:
+		set_bit(CH_OPENED, &c->flags);
+
+		if (port && port->port_usb && port->port_usb->connect)
+			port->port_usb->connect(port->port_usb);
+
+		break;
+	case SMD_EVENT_CLOSE:
+		clear_bit(CH_OPENED, &c->flags);
+
+		if (port && port->port_usb && port->port_usb->disconnect)
+			port->port_usb->disconnect(port->port_usb);
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		while (!list_empty(&c->tx_q)) {
+			cpkt = list_first_entry(&c->tx_q,
+					struct rmnet_ctrl_pkt, list);
+
+			list_del(&cpkt->list);
+			free_rmnet_ctrl_pkt(cpkt);
+		}
+		spin_unlock_irqrestore(&port->port_lock, flags);
+
+		break;
+	}
+}
+/*------------------------------------------------------------ */
+
+static void grmnet_ctrl_smd_connect_w(struct work_struct *w)
+{
+	struct rmnet_ctrl_port *port =
+			container_of(w, struct rmnet_ctrl_port, connect_w.work);
+	struct smd_ch_info *c = &port->ctrl_ch;
+	unsigned long flags;
+	int ret;
+
+	pr_debug("%s:\n", __func__);
+
+	if (!test_bit(CH_READY, &c->flags))
+		return;
+
+	ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			/* port not ready  - retry */
+			pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
+					__func__, c->name, ret);
+			queue_delayed_work(grmnet_ctrl_wq, &port->connect_w,
+				msecs_to_jiffies(250));
+		} else {
+			pr_err("%s: unable to open smd port:%s err:%d\n",
+					__func__, c->name, ret);
+		}
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+int gsmd_ctrl_connect(struct grmnet *gr, int port_num)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	unsigned long		flags;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (port_num >= n_rmnet_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	port = ctrl_smd_ports[port_num].port;
+	c = &port->ctrl_ch;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = gr;
+	gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem;
+	gr->notify_modem = gsmd_ctrl_send_cbits_tomodem;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, 0);
+
+	return 0;
+}
+
+void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
+{
+	struct rmnet_ctrl_port	*port;
+	unsigned long		flags;
+	struct smd_ch_info	*c;
+	struct rmnet_ctrl_pkt	*cpkt;
+
+	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
+
+	if (port_num >= n_rmnet_ctrl_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("%s: grmnet port is null\n", __func__);
+		return;
+	}
+
+	port = ctrl_smd_ports[port_num].port;
+	c = &port->ctrl_ch;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	gr->send_encap_cmd = 0;
+	gr->notify_modem = 0;
+	c->cbits_tomodem = 0;
+
+	while (!list_empty(&c->tx_q)) {
+		cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
+
+		list_del(&cpkt->list);
+		free_rmnet_ctrl_pkt(cpkt);
+	}
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (test_and_clear_bit(CH_OPENED, &c->flags))
+		/* send dtr zero */
+		smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
+
+	if (c->ch) {
+		smd_close(c->ch);
+		c->ch = NULL;
+	}
+}
+
+#define SMD_CH_MAX_LEN	20
+static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		c = &port->ctrl_ch;
+
+		if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
+			set_bit(CH_READY, &c->flags);
+
+			/* if usb is online, try opening smd_ch */
+			spin_lock_irqsave(&port->port_lock, flags);
+			if (port->port_usb)
+				queue_delayed_work(grmnet_ctrl_wq,
+							&port->connect_w, 0);
+			spin_unlock_irqrestore(&port->port_lock, flags);
+
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			i;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		c = &port->ctrl_ch;
+
+		if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
+			clear_bit(CH_READY, &c->flags);
+			clear_bit(CH_OPENED, &c->flags);
+			if (c->ch) {
+				smd_close(c->ch);
+				c->ch = NULL;
+			}
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+static void grmnet_ctrl_smd_port_free(int portno)
+{
+	struct rmnet_ctrl_port	*port = ctrl_smd_ports[portno].port;
+	struct platform_driver *pdrv = &ctrl_smd_ports[portno].pdrv;
+
+	if (port) {
+		kfree(port);
+		platform_driver_unregister(pdrv);
+	}
+}
+
+static int grmnet_ctrl_smd_port_alloc(int portno)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	struct platform_driver	*pdrv;
+
+	port = kzalloc(sizeof(struct rmnet_ctrl_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	spin_lock_init(&port->port_lock);
+	INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
+
+	c = &port->ctrl_ch;
+	c->name = rmnet_ctrl_names[portno];
+	c->port = port;
+	init_waitqueue_head(&c->wait);
+	INIT_LIST_HEAD(&c->tx_q);
+	INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
+	INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
+
+	ctrl_smd_ports[portno].port = port;
+
+	pdrv = &ctrl_smd_ports[portno].pdrv;
+	pdrv->probe = grmnet_ctrl_smd_ch_probe;
+	pdrv->remove = grmnet_ctrl_smd_ch_remove;
+	pdrv->driver.name = c->name;
+	pdrv->driver.owner = THIS_MODULE;
+
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+int gsmd_ctrl_setup(unsigned int count)
+{
+	int	i;
+	int	ret;
+
+	pr_debug("%s: requested ports:%d\n", __func__, count);
+
+	if (!count || count > NR_CTRL_SMD_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d\n",
+				__func__, count);
+		return -EINVAL;
+	}
+
+	grmnet_ctrl_wq = alloc_workqueue("gsmd_ctrl",
+				WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!grmnet_ctrl_wq) {
+		pr_err("%s: Unable to create workqueue grmnet_ctrl\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		n_rmnet_ctrl_ports++;
+		ret = grmnet_ctrl_smd_port_alloc(i);
+		if (ret) {
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			n_rmnet_ctrl_ports--;
+			goto free_ctrl_smd_ports;
+		}
+	}
+
+	return 0;
+
+free_ctrl_smd_ports:
+	for (i = 0; i < n_rmnet_ctrl_ports; i++)
+		grmnet_ctrl_smd_port_free(i);
+
+	destroy_workqueue(grmnet_ctrl_wq);
+
+	return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	char			*buf;
+	unsigned long		flags;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		if (!port)
+			continue;
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		c = &port->ctrl_ch;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"#PORT:%d port:%p ctrl_ch:%p#\n"
+				"to_usbhost: %lu\n"
+				"to_modem:   %lu\n"
+				"DTR:        %s\n"
+				"ch_open:    %d\n"
+				"ch_ready:   %d\n"
+				"read_avail: %d\n"
+				"write_avail:%d\n",
+				i, port, &port->ctrl_ch,
+				c->to_host, c->to_modem,
+				c->cbits_tomodem ? "HIGH" : "LOW",
+				test_bit(CH_OPENED, &c->flags),
+				test_bit(CH_READY, &c->flags),
+				c->ch ? smd_read_avail(c->ch) : 0,
+				c->ch ? smd_write_avail(c->ch) : 0);
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t gsmd_ctrl_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_port	*port;
+	struct smd_ch_info	*c;
+	int			i;
+	unsigned long		flags;
+
+	for (i = 0; i < n_rmnet_ctrl_ports; i++) {
+		port = ctrl_smd_ports[i].port;
+		if (!port)
+			continue;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+
+		c = &port->ctrl_ch;
+
+		c->to_host = 0;
+		c->to_modem = 0;
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+	return count;
+}
+
+const struct file_operations gsmd_ctrl_stats_ops = {
+	.read = gsmd_ctrl_read_stats,
+	.write = gsmd_ctrl_reset_stats,
+};
+
+struct dentry *smd_ctrl_dent;
+struct dentry *smd_ctrl_dfile;
+static void gsmd_ctrl_debugfs_init(void)
+{
+	smd_ctrl_dent = debugfs_create_dir("usb_rmnet_ctrl_smd", 0);
+	if (IS_ERR(smd_ctrl_dent))
+		return;
+
+	smd_ctrl_dfile = debugfs_create_file("status", 0444, smd_ctrl_dent, 0,
+			&gsmd_ctrl_stats_ops);
+	if (!smd_ctrl_dfile || IS_ERR(smd_ctrl_dfile))
+		debugfs_remove(smd_ctrl_dent);
+}
+
+static void gsmd_ctrl_debugfs_exit(void)
+{
+	debugfs_remove(smd_ctrl_dfile);
+	debugfs_remove(smd_ctrl_dent);
+}
+
+#else
+static void gsmd_ctrl_debugfs_init(void) { }
+static void gsmd_ctrl_debugfs_exit(void) { }
+#endif
+
+static int __init gsmd_ctrl_init(void)
+{
+	gsmd_ctrl_debugfs_init();
+
+	return 0;
+}
+module_init(gsmd_ctrl_init);
+
+static void __exit gsmd_ctrl_exit(void)
+{
+	gsmd_ctrl_debugfs_exit();
+}
+module_exit(gsmd_ctrl_exit);
+MODULE_DESCRIPTION("smd control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_sdio.c b/drivers/usb/gadget/u_sdio.c
new file mode 100644
index 0000000..8c4b4c7
--- /dev/null
+++ b/drivers/usb/gadget/u_sdio.c
@@ -0,0 +1,1169 @@
+/*
+ * u_sdio.c - utilities for USB gadget serial over sdio
+ *
+ * This code also borrows from drivers/usb/gadget/u_serial.c, which is
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program from the Code Aurora Forum is free software; you can
+ * redistribute it and/or modify it under the GNU General Public License
+ * version 2 and only version 2 as published by the Free Software Foundation.
+ * The original work available from [kernel.org] is subject to the notice below.
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+
+#include <mach/sdio_al.h>
+#include <mach/sdio_cmux.h>
+#include "u_serial.h"
+
+#define SDIO_RX_QUEUE_SIZE		8
+#define SDIO_RX_BUF_SIZE		2048
+
+#define SDIO_TX_QUEUE_SIZE		8
+#define SDIO_TX_BUF_SIZE		2048
+
+/* 1 - DUN, 2-NMEA/GPS */
+#define SDIO_N_PORTS	2
+static struct sdio_portmaster {
+	struct mutex lock;
+	struct gsdio_port *port;
+	struct platform_driver gsdio_ch;
+} sdio_ports[SDIO_N_PORTS];
+static unsigned n_sdio_ports;
+
+struct sdio_port_info {
+	/* data channel info */
+	char *data_ch_name;
+	struct sdio_channel *ch;
+
+	/* control channel info */
+	int ctrl_ch_id;
+};
+
+struct sdio_port_info sport_info[SDIO_N_PORTS] = {
+	{
+		.data_ch_name = "SDIO_DUN",
+		.ctrl_ch_id = 9,
+	},
+	{
+		.data_ch_name = "SDIO_NMEA",
+		.ctrl_ch_id = 10,
+	},
+};
+
+static struct workqueue_struct *gsdio_wq;
+
+struct gsdio_port {
+	unsigned			port_num;
+	spinlock_t			port_lock;
+
+	unsigned			n_read;
+	struct list_head		read_pool;
+	struct list_head		read_queue;
+	struct work_struct		push;
+	unsigned long			rp_len;
+	unsigned long			rq_len;
+
+	struct list_head		write_pool;
+	struct work_struct		pull;
+	unsigned long			wp_len;
+
+	struct work_struct		notify_modem;
+
+	struct gserial			*port_usb;
+	struct usb_cdc_line_coding	line_coding;
+
+	int				sdio_open;
+	int				sdio_probe;
+	int				ctrl_ch_err;
+	struct sdio_port_info		*sport_info;
+	struct delayed_work		sdio_open_work;
+
+#define SDIO_ACM_CTRL_RI		(1 << 3)
+#define SDIO_ACM_CTRL_DSR		(1 << 1)
+#define SDIO_ACM_CTRL_DCD		(1 << 0)
+	int				cbits_to_laptop;
+
+#define SDIO_ACM_CTRL_RTS	(1 << 1)	/* unused with full duplex */
+#define SDIO_ACM_CTRL_DTR	(1 << 0)	/* host is ready for data r/w */
+	int				cbits_to_modem;
+
+	/* pkt logging */
+	unsigned long			nbytes_tolaptop;
+	unsigned long			nbytes_tomodem;
+};
+
+void gsdio_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+struct usb_request *
+gsdio_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req) {
+		pr_err("%s: usb alloc request failed\n", __func__);
+		return NULL;
+	}
+
+	req->length = len;
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		pr_err("%s: request buf allocation failed\n", __func__);
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+
+	return req;
+}
+
+void gsdio_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		gsdio_free_req(ep, req);
+	}
+}
+
+int gsdio_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num, int size,
+		void (*cb)(struct usb_ep *ep, struct usb_request *))
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("%s: ep:%p head:%p num:%d size:%d cb:%p", __func__,
+			ep, head, num, size, cb);
+
+	for (i = 0; i < num; i++) {
+		req = gsdio_alloc_req(ep, size, GFP_ATOMIC);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+void gsdio_start_rx(struct gsdio_port *port)
+{
+	struct list_head	*pool;
+	struct usb_ep		*out;
+	int ret;
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	spin_lock_irq(&port->port_lock);
+
+	if (!port->port_usb) {
+		pr_debug("%s: usb is disconnected\n", __func__);
+		goto start_rx_end;
+	}
+
+	if (!port->sdio_open) {
+		pr_debug("%s: sdio is not open\n", __func__);
+		goto start_rx_end;
+	}
+
+	pool = &port->read_pool;
+	out = port->port_usb->out;
+
+	while (!list_empty(pool)) {
+		struct usb_request	*req;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = SDIO_RX_BUF_SIZE;
+		port->rp_len--;
+
+		spin_unlock_irq(&port->port_lock);
+		ret = usb_ep_queue(out, req, GFP_ATOMIC);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d\n",
+					__func__, port, port->port_num);
+			list_add_tail(&req->list, pool);
+			port->rp_len++;
+			break;
+		}
+
+		/* usb could have disconnected while we released spin lock */
+		if (!port->port_usb) {
+			pr_debug("%s: usb is disconnected\n", __func__);
+			goto start_rx_end;
+		}
+	}
+
+start_rx_end:
+	spin_unlock_irq(&port->port_lock);
+}
+
+int gsdio_write(struct gsdio_port *port, struct usb_request *req)
+{
+	unsigned	avail;
+	char		*packet;
+	unsigned	size = req->actual;
+	unsigned	n;
+	int		ret = 0;
+
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!req) {
+		pr_err("%s: usb request is null port#%d\n",
+				__func__, port->port_num);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: port:%p port#%d req:%p actual:%d n_read:%d\n",
+			__func__, port, port->port_num, req,
+			req->actual, port->n_read);
+
+	if (!port->sdio_open) {
+		pr_debug("%s: SDIO IO is not supported\n", __func__);
+		return -ENODEV;
+	}
+
+	avail = sdio_write_avail(port->sport_info->ch);
+
+	pr_debug("%s: sdio_write_avail:%d", __func__, avail);
+
+	if (!avail)
+		return -EBUSY;
+
+	if (!req->actual) {
+		pr_debug("%s: req->actual is already zero,update bytes read\n",
+				__func__);
+		port->n_read = 0;
+		return -ENODEV;
+	}
+
+	packet = req->buf;
+	n = port->n_read;
+	if (n) {
+		packet += n;
+		size -= n;
+	}
+
+	if (size > avail)
+		size = avail;
+
+	spin_unlock_irq(&port->port_lock);
+	ret = sdio_write(port->sport_info->ch, packet, size);
+	spin_lock_irq(&port->port_lock);
+	if (ret) {
+		pr_err("%s: port#%d sdio write failed err:%d",
+				__func__, port->port_num, ret);
+		/* try again later */
+		return ret;
+	}
+
+	port->nbytes_tomodem += size;
+
+	if (size + n == req->actual)
+		port->n_read = 0;
+	else
+		port->n_read += size;
+
+	return ret;
+}
+
+void gsdio_rx_push(struct work_struct *w)
+{
+	struct gsdio_port *port = container_of(w, struct gsdio_port, push);
+	struct list_head *q = &port->read_queue;
+	struct usb_ep		*out;
+	int ret;
+
+	pr_debug("%s: port:%p port#%d read_queue:%p", __func__,
+			port, port->port_num, q);
+
+	spin_lock_irq(&port->port_lock);
+
+	if (!port->port_usb) {
+		pr_debug("%s: usb cable is disconencted\n", __func__);
+		spin_unlock_irq(&port->port_lock);
+		return;
+	}
+
+	out = port->port_usb->out;
+
+	while (!list_empty(q)) {
+		struct usb_request *req;
+
+		req = list_first_entry(q, struct usb_request, list);
+
+		switch (req->status) {
+		case -ESHUTDOWN:
+			pr_debug("%s: req status shutdown portno#%d port:%p",
+					__func__, port->port_num, port);
+			goto rx_push_end;
+		default:
+			pr_warning("%s: port:%p port#%d"
+					" Unexpected Rx Status:%d\n", __func__,
+					port, port->port_num, req->status);
+			/* FALL THROUGH */
+		case 0:
+			/* normal completion */
+			break;
+		}
+
+		if (!port->sdio_open) {
+			pr_err("%s: sio channel is not open\n", __func__);
+			list_move(&req->list, &port->read_pool);
+			port->rp_len++;
+			port->rq_len--;
+			goto rx_push_end;
+		}
+
+
+		list_del(&req->list);
+		port->rq_len--;
+
+		ret = gsdio_write(port, req);
+		/* as gsdio_write drops spin_lock while writing data
+		 * to sdio usb cable may have been disconnected
+		 */
+		if (!port->port_usb) {
+			port->n_read = 0;
+			gsdio_free_req(out, req);
+			spin_unlock_irq(&port->port_lock);
+			return;
+		}
+
+		if (ret || port->n_read) {
+			list_add(&req->list, &port->read_queue);
+			port->rq_len++;
+			goto rx_push_end;
+		}
+
+		list_add(&req->list, &port->read_pool);
+		port->rp_len++;
+	}
+
+	if (port->sdio_open && !list_empty(q)) {
+		if (sdio_write_avail(port->sport_info->ch))
+			queue_work(gsdio_wq, &port->push);
+	}
+rx_push_end:
+	spin_unlock_irq(&port->port_lock);
+
+	/* start queuing out requests again to host */
+	gsdio_start_rx(port);
+}
+
+void gsdio_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsdio_port *port = ep->driver_data;
+	unsigned long flags;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	list_add_tail(&req->list, &port->read_queue);
+	port->rq_len++;
+	queue_work(gsdio_wq, &port->push);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return;
+}
+
+void gsdio_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsdio_port *port = ep->driver_data;
+	unsigned long flags;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	list_add(&req->list, &port->write_pool);
+	port->wp_len++;
+
+	switch (req->status) {
+	default:
+		pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
+				__func__, port, port->port_num,
+				ep->name, req->status);
+		/* FALL THROUGH */
+	case 0:
+		queue_work(gsdio_wq, &port->pull);
+		break;
+
+	case -ESHUTDOWN:
+		/* disconnect */
+		pr_debug("%s: %s shutdown\n", __func__, ep->name);
+		break;
+	}
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return;
+}
+
+void gsdio_read_pending(struct gsdio_port *port)
+{
+	struct sdio_channel *ch;
+	char buf[1024];
+	int avail;
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	ch = port->sport_info->ch;
+
+	if (!ch)
+		return;
+
+	while ((avail = sdio_read_avail(ch))) {
+		if (avail > 1024)
+			avail = 1024;
+		sdio_read(ch, buf, avail);
+
+		pr_debug("%s: flushed out %d bytes\n", __func__, avail);
+	}
+}
+
+void gsdio_tx_pull(struct work_struct *w)
+{
+	struct gsdio_port *port = container_of(w, struct gsdio_port, pull);
+	struct list_head *pool = &port->write_pool;
+
+	pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
+			port, port->port_num, pool);
+
+	if (!port->port_usb) {
+		pr_err("%s: usb disconnected\n", __func__);
+
+		/* take out all the pending data from sdio */
+		gsdio_read_pending(port);
+
+		return;
+	}
+
+	spin_lock_irq(&port->port_lock);
+
+	while (!list_empty(pool)) {
+		int avail;
+		struct usb_ep *in = port->port_usb->in;
+		struct sdio_channel *ch = port->sport_info->ch;
+		struct usb_request *req;
+		unsigned len = SDIO_TX_BUF_SIZE;
+		int ret;
+
+
+		req = list_entry(pool->next, struct usb_request, list);
+
+		if (!port->sdio_open) {
+			pr_debug("%s: SDIO channel is not open\n", __func__);
+			goto tx_pull_end;
+		}
+
+		avail = sdio_read_avail(ch);
+		if (!avail) {
+			/* REVISIT: for ZLP */
+			pr_debug("%s: read_avail:%d port:%p port#%d\n",
+					__func__, avail, port, port->port_num);
+			goto tx_pull_end;
+		}
+
+		if (avail > len)
+			avail = len;
+
+		list_del(&req->list);
+		port->wp_len--;
+
+		spin_unlock_irq(&port->port_lock);
+		ret = sdio_read(ch, req->buf, avail);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: port:%p port#%d sdio read failed err:%d",
+					__func__, port, port->port_num, ret);
+
+			/* check if usb is still active */
+			if (!port->port_usb) {
+				gsdio_free_req(in, req);
+			} else {
+				list_add(&req->list, pool);
+				port->wp_len++;
+			}
+			goto tx_pull_end;
+		}
+
+		req->length = avail;
+
+		spin_unlock_irq(&port->port_lock);
+		ret = usb_ep_queue(in, req, GFP_KERNEL);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d err:%d\n",
+					__func__, port, port->port_num, ret);
+
+			/* could be usb disconnected */
+			if (!port->port_usb) {
+				gsdio_free_req(in, req);
+			} else {
+				list_add(&req->list, pool);
+				port->wp_len++;
+			}
+			goto tx_pull_end;
+		}
+
+		port->nbytes_tolaptop += avail;
+	}
+tx_pull_end:
+	spin_unlock_irq(&port->port_lock);
+}
+
+int gsdio_start_io(struct gsdio_port *port)
+{
+	int			ret;
+	unsigned long		flags;
+
+	pr_debug("%s:\n", __func__);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return -ENODEV;
+	}
+
+	/* start usb out queue */
+	ret = gsdio_alloc_requests(port->port_usb->out,
+				&port->read_pool,
+				SDIO_RX_QUEUE_SIZE, SDIO_RX_BUF_SIZE,
+				gsdio_read_complete);
+	if (ret) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_err("%s: unable to allocate out reqs\n", __func__);
+		return ret;
+	}
+	port->rp_len = SDIO_RX_QUEUE_SIZE;
+
+	ret = gsdio_alloc_requests(port->port_usb->in,
+				&port->write_pool,
+				SDIO_TX_QUEUE_SIZE, SDIO_TX_BUF_SIZE,
+				gsdio_write_complete);
+	if (ret) {
+		gsdio_free_requests(port->port_usb->out, &port->read_pool);
+		port->rp_len = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_err("%s: unable to allocate in reqs\n", __func__);
+		return ret;
+	}
+	port->wp_len = SDIO_TX_QUEUE_SIZE;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	gsdio_start_rx(port);
+	queue_work(gsdio_wq, &port->pull);
+
+	return 0;
+}
+
+void gsdio_port_free(unsigned portno)
+{
+	struct gsdio_port *port = sdio_ports[portno].port;
+	struct platform_driver *pdriver = &sdio_ports[portno].gsdio_ch;
+
+	if (!port) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	platform_driver_unregister(pdriver);
+
+	kfree(port);
+}
+
+void gsdio_ctrl_wq(struct work_struct *w)
+{
+	struct gsdio_port *port;
+
+	port = container_of(w, struct gsdio_port, notify_modem);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	if (!port->sdio_open || port->ctrl_ch_err)
+		return;
+
+	sdio_cmux_tiocmset(port->sport_info->ctrl_ch_id,
+			port->cbits_to_modem, ~(port->cbits_to_modem));
+}
+
+void gsdio_ctrl_notify_modem(void *gptr, u8 portno, int ctrl_bits)
+{
+	struct gsdio_port *port;
+	int temp;
+	struct gserial *gser = gptr;
+
+	if (portno >= n_sdio_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = sdio_ports[portno].port;
+
+	temp = ctrl_bits & SDIO_ACM_CTRL_DTR ? TIOCM_DTR : 0;
+
+	if (port->cbits_to_modem == temp)
+		return;
+
+	 port->cbits_to_modem = temp;
+
+	/* TIOCM_DTR - 0x002 - bit(1) */
+	pr_debug("%s: port:%p port#%d ctrl_bits:%08x\n", __func__,
+		port, port->port_num, ctrl_bits);
+
+	if (!port->sdio_open) {
+		pr_err("%s: port:%p port#%d sdio not connected\n",
+				__func__, port, port->port_num);
+		return;
+	}
+
+	/* whenever DTR is high let laptop know that modem status */
+	if (port->cbits_to_modem && gser->send_modem_ctrl_bits)
+		gser->send_modem_ctrl_bits(gser, port->cbits_to_laptop);
+
+	queue_work(gsdio_wq, &port->notify_modem);
+}
+
+void gsdio_ctrl_modem_status(int ctrl_bits, void *_dev)
+{
+	struct gsdio_port *port = _dev;
+
+	/* TIOCM_CD - 0x040 - bit(6)
+	 * TIOCM_RI - 0x080 - bit(7)
+	 * TIOCM_DSR- 0x100 - bit(8)
+	 */
+	pr_debug("%s: port:%p port#%d event:%08x\n", __func__,
+		port, port->port_num, ctrl_bits);
+
+	port->cbits_to_laptop = 0;
+	ctrl_bits &= TIOCM_RI | TIOCM_CD | TIOCM_DSR;
+	if (ctrl_bits & TIOCM_RI)
+		port->cbits_to_laptop |= SDIO_ACM_CTRL_RI;
+	if (ctrl_bits & TIOCM_CD)
+		port->cbits_to_laptop |= SDIO_ACM_CTRL_DCD;
+	if (ctrl_bits & TIOCM_DSR)
+		port->cbits_to_laptop |= SDIO_ACM_CTRL_DSR;
+
+	if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
+		port->port_usb->send_modem_ctrl_bits(port->port_usb,
+					port->cbits_to_laptop);
+}
+
+void gsdio_ch_notify(void *_dev, unsigned event)
+{
+	struct gsdio_port *port = _dev;
+
+	pr_debug("%s: port:%p port#%d event:%s\n", __func__,
+		port, port->port_num,
+		event == 1 ? "READ AVAIL" : "WRITE_AVAIL");
+
+	if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
+		queue_work(gsdio_wq, &port->push);
+	if (event == SDIO_EVENT_DATA_READ_AVAIL)
+		queue_work(gsdio_wq, &port->pull);
+}
+
+static void gsdio_open_work(struct work_struct *w)
+{
+	struct gsdio_port *port =
+			container_of(w, struct gsdio_port, sdio_open_work.work);
+	struct sdio_port_info *pi = port->sport_info;
+	struct gserial *gser;
+	int ret;
+	int ctrl_bits;
+	int startio;
+
+	ret = sdio_open(pi->data_ch_name, &pi->ch, port, gsdio_ch_notify);
+	if (ret) {
+		pr_err("%s: port:%p port#%d unable to open sdio ch:%s\n",
+				__func__, port, port->port_num,
+				pi->data_ch_name);
+		return;
+	}
+
+	port->ctrl_ch_err = 0;
+	ret = sdio_cmux_open(pi->ctrl_ch_id, 0, 0,
+			gsdio_ctrl_modem_status, port);
+	if (ret) {
+		pr_err("%s: port:%p port#%d unable to open ctrl ch:%d\n",
+				__func__, port, port->port_num, pi->ctrl_ch_id);
+		port->ctrl_ch_err = 1;
+	}
+
+	/* check for latest status update from modem */
+	if (!port->ctrl_ch_err) {
+		ctrl_bits = sdio_cmux_tiocmget(pi->ctrl_ch_id);
+		gsdio_ctrl_modem_status(ctrl_bits, port);
+	}
+
+	pr_debug("%s: SDIO data:%s ctrl:%d are open\n", __func__,
+					pi->data_ch_name,
+					pi->ctrl_ch_id);
+
+	port->sdio_open = 1;
+
+	/* start tx if usb is open already */
+	spin_lock_irq(&port->port_lock);
+	startio = port->port_usb ? 1 : 0;
+	gser = port->port_usb;
+	spin_unlock_irq(&port->port_lock);
+
+	if (startio) {
+		pr_debug("%s: USB is already open, start io\n", __func__);
+		gsdio_start_io(port);
+		 if (gser->send_modem_ctrl_bits)
+			gser->send_modem_ctrl_bits(gser, port->cbits_to_laptop);
+	}
+}
+
+#define SDIO_CH_NAME_MAX_LEN	9
+#define SDIO_OPEN_DELAY		msecs_to_jiffies(10000)
+static int gsdio_ch_remove(struct platform_device *dev)
+{
+	struct gsdio_port	*port;
+	struct sdio_port_info	*pi;
+	int i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, dev->name);
+
+	for (i = 0; i < n_sdio_ports; i++) {
+		port = sdio_ports[i].port;
+		pi = port->sport_info;
+
+		if (!strncmp(pi->data_ch_name, dev->name,
+					SDIO_CH_NAME_MAX_LEN)) {
+			struct gserial *gser = port->port_usb;
+
+			port->sdio_open = 0;
+			port->sdio_probe = 0;
+			port->ctrl_ch_err = 1;
+
+			/* check if usb cable is connected */
+			if (!gser)
+				continue;
+
+			/* indicated call status to usb host */
+			gsdio_ctrl_modem_status(0, port);
+
+			usb_ep_fifo_flush(gser->in);
+			usb_ep_fifo_flush(gser->out);
+
+			cancel_work_sync(&port->push);
+			cancel_work_sync(&port->pull);
+
+			spin_lock_irqsave(&port->port_lock, flags);
+			gsdio_free_requests(gser->out, &port->read_pool);
+			gsdio_free_requests(gser->out, &port->read_queue);
+			gsdio_free_requests(gser->in, &port->write_pool);
+
+			port->rp_len = 0;
+			port->rq_len = 0;
+			port->wp_len = 0;
+			port->n_read = 0;
+			spin_unlock_irqrestore(&port->port_lock, flags);
+
+		}
+	}
+
+	return 0;
+}
+
+static int gsdio_ch_probe(struct platform_device *dev)
+{
+	struct gsdio_port	*port;
+	struct sdio_port_info	*pi;
+	int i;
+
+	pr_debug("%s: name:%s\n", __func__, dev->name);
+
+	for (i = 0; i < n_sdio_ports; i++) {
+		port = sdio_ports[i].port;
+		pi = port->sport_info;
+
+		pr_debug("%s: sdio_ch_name:%s dev_name:%s\n", __func__,
+				pi->data_ch_name, dev->name);
+
+		/* unfortunately cmux channle might not be ready even if
+		 * sdio channel is ready. as we dont have good notification
+		 * mechanism schedule a delayed work
+		 */
+		if (!strncmp(pi->data_ch_name, dev->name,
+					SDIO_CH_NAME_MAX_LEN)) {
+			port->sdio_probe = 1;
+			queue_delayed_work(gsdio_wq,
+				&port->sdio_open_work, SDIO_OPEN_DELAY);
+			return 0;
+		}
+	}
+
+	pr_info("%s: name:%s is not found\n", __func__, dev->name);
+
+	return -ENODEV;
+}
+
+int gsdio_port_alloc(unsigned portno,
+		struct usb_cdc_line_coding *coding,
+		struct sdio_port_info *pi)
+{
+	struct gsdio_port *port;
+	struct platform_driver *pdriver;
+
+	port = kzalloc(sizeof(struct gsdio_port), GFP_KERNEL);
+	if (!port) {
+		pr_err("%s: port allocation failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	port->port_num = portno;
+	spin_lock_init(&port->port_lock);
+	port->line_coding = *coding;
+
+	/* READ: read from usb and write into sdio */
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queue);
+	INIT_WORK(&port->push, gsdio_rx_push);
+
+	INIT_LIST_HEAD(&port->write_pool);
+	INIT_WORK(&port->pull, gsdio_tx_pull);
+
+	INIT_WORK(&port->notify_modem, gsdio_ctrl_wq);
+
+	INIT_DELAYED_WORK(&port->sdio_open_work, gsdio_open_work);
+
+	sdio_ports[portno].port = port;
+
+	port->sport_info = pi;
+	pdriver = &sdio_ports[portno].gsdio_ch;
+
+	pdriver->probe = gsdio_ch_probe;
+	pdriver->remove = gsdio_ch_remove;
+	pdriver->driver.name = pi->data_ch_name;
+	pdriver->driver.owner = THIS_MODULE;
+
+	pr_debug("%s: port:%p port#%d sdio_name: %s\n", __func__,
+			port, port->port_num, pi->data_ch_name);
+
+	platform_driver_register(pdriver);
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	return 0;
+}
+
+int gsdio_connect(struct gserial *gser, u8 portno)
+{
+	struct gsdio_port *port;
+	int ret = 0;
+	unsigned long flags;
+
+	if (portno >= n_sdio_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return -EINVAL;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return -EINVAL;
+	}
+
+	port = sdio_ports[portno].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = gser;
+	gser->notify_modem = gsdio_ctrl_notify_modem;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	ret = usb_ep_enable(gser->in);
+	if (ret) {
+		pr_err("%s: failed to enable in ep w/ err:%d\n",
+					__func__, ret);
+		port->port_usb = 0;
+		return ret;
+	}
+	gser->in->driver_data = port;
+
+	ret = usb_ep_enable(gser->out);
+	if (ret) {
+		pr_err("%s: failed to enable in ep w/ err:%d\n",
+					__func__, ret);
+		usb_ep_disable(gser->in);
+		port->port_usb = 0;
+		gser->in->driver_data = 0;
+		return ret;
+	}
+	gser->out->driver_data = port;
+
+	if (port->sdio_open) {
+		pr_debug("%s: sdio is already open, start io\n", __func__);
+		gsdio_start_io(port);
+		 if (gser->send_modem_ctrl_bits)
+			gser->send_modem_ctrl_bits(gser, port->cbits_to_laptop);
+	}
+
+	return 0;
+}
+
+void gsdio_disconnect(struct gserial *gser, u8 portno)
+{
+	unsigned long flags;
+	struct gsdio_port *port;
+
+	if (portno >= n_sdio_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = sdio_ports[portno].port;
+
+	/* send dtr zero to modem to notify disconnect */
+	port->cbits_to_modem = 0;
+	queue_work(gsdio_wq, &port->notify_modem);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	port->nbytes_tomodem = 0;
+	port->nbytes_tolaptop = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	/* disable endpoints, aborting down any active I/O */
+	usb_ep_disable(gser->out);
+
+	usb_ep_disable(gser->in);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	gsdio_free_requests(gser->out, &port->read_pool);
+	gsdio_free_requests(gser->out, &port->read_queue);
+	gsdio_free_requests(gser->in, &port->write_pool);
+
+	port->rp_len = 0;
+	port->rq_len = 0;
+	port->wp_len = 0;
+	port->n_read = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_sdio_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gsdio_port *port;
+	char *buf;
+	unsigned long flags;
+	int i = 0;
+	int temp = 0;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 1024, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	while (i < n_sdio_ports) {
+		port = sdio_ports[i].port;
+		spin_lock_irqsave(&port->port_lock, flags);
+		temp += scnprintf(buf + temp, PAGE_SIZE - temp,
+				"###PORT:%d port:%p###\n"
+				"nbytes_tolaptop: %lu\n"
+				"nbytes_tomodem:  %lu\n"
+				"cbits_to_modem:  %u\n"
+				"cbits_to_laptop: %u\n"
+				"read_pool_len:   %lu\n"
+				"read_queue_len:  %lu\n"
+				"write_pool_len:  %lu\n"
+				"n_read:          %u\n"
+				"sdio_open:       %d\n"
+				"sdio_probe:      %d\n",
+				i, port,
+				port->nbytes_tolaptop, port->nbytes_tomodem,
+				port->cbits_to_modem, port->cbits_to_laptop,
+				port->rp_len, port->rq_len, port->wp_len,
+				port->n_read,
+				port->sdio_open, port->sdio_probe);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		i++;
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t debug_sdio_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct gsdio_port *port;
+	unsigned long flags;
+	int i = 0;
+
+	while (i < n_sdio_ports) {
+		port = sdio_ports[i].port;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->nbytes_tolaptop = 0;
+		port->nbytes_tomodem = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		i++;
+	}
+
+	return count;
+}
+
+static int debug_sdio_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations debug_gsdio_ops = {
+	.open = debug_sdio_open,
+	.read = debug_sdio_read_stats,
+	.write = debug_sdio_reset_stats,
+};
+
+static void gsdio_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("usb_gsdio", 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("status", 0444, dent, 0, &debug_gsdio_ops);
+}
+#else
+static void gsdio_debugfs_init(void)
+{
+	return;
+}
+#endif
+
+/* connect, disconnect, alloc_requests, free_requests */
+int gsdio_setup(struct usb_gadget *g, unsigned count)
+{
+	struct usb_cdc_line_coding	coding;
+	int i;
+	int ret = 0;
+
+	pr_debug("%s: gadget:(%p) count:%d\n", __func__, g, count);
+
+	if (count == 0 || count > SDIO_N_PORTS) {
+		pr_err("%s: invalid number of ports count:%d max_ports:%d\n",
+				__func__, count, SDIO_N_PORTS);
+		return -EINVAL;
+	}
+
+	coding.dwDTERate = cpu_to_le32(9600);
+	coding.bCharFormat = 8;
+	coding.bParityType = USB_CDC_NO_PARITY;
+	coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+	gsdio_wq = create_singlethread_workqueue("k_gserial");
+	if (!gsdio_wq) {
+		pr_err("%s: unable to create workqueue gsdio_wq\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		mutex_init(&sdio_ports[i].lock);
+		ret = gsdio_port_alloc(i, &coding, sport_info + i);
+		n_sdio_ports++;
+		if (ret) {
+			n_sdio_ports--;
+			pr_err("%s: sdio logical port allocation failed\n",
+					__func__);
+			goto free_sdio_ports;
+		}
+
+#ifdef DEBUG
+		/* REVISIT: create one file per port
+		 * or do not create any file
+		 */
+		if (i == 0) {
+			ret = device_create_file(&g->dev, &dev_attr_input);
+			if (ret)
+				pr_err("%s: unable to create device file\n",
+						__func__);
+		}
+#endif
+
+	}
+
+	gsdio_debugfs_init();
+
+	return 0;
+
+free_sdio_ports:
+	for (i = 0; i < n_sdio_ports; i++)
+		gsdio_port_free(i);
+	destroy_workqueue(gsdio_wq);
+
+	return ret;
+}
+
+/* TODO: Add gserial_cleanup */
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 380a87f..de93049 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -26,6 +26,7 @@
 #include <linux/tty_flip.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <linux/debugfs.h>
 
 #include "u_serial.h"
 
@@ -78,9 +79,14 @@
  * next layer of buffering.  For TX that's a circular buffer; for RX
  * consider it a NOP.  A third layer is provided by the TTY code.
  */
-#define QUEUE_SIZE		16
+#define TX_QUEUE_SIZE		8
+#define TX_BUF_SIZE		4096
 #define WRITE_BUF_SIZE		8192		/* TX only */
 
+#define RX_QUEUE_SIZE		8
+#define RX_BUF_SIZE		4096
+
+
 /* circular buffer */
 struct gs_buf {
 	unsigned		buf_size;
@@ -110,7 +116,7 @@
 	int read_allocated;
 	struct list_head	read_queue;
 	unsigned		n_read;
-	struct tasklet_struct	push;
+	struct work_struct	push;
 
 	struct list_head	write_pool;
 	int write_started;
@@ -120,16 +126,22 @@
 
 	/* REVISIT this state ... */
 	struct usb_cdc_line_coding port_line_coding;	/* 8-N-1 etc */
+	unsigned long           nbytes_from_host;
+	unsigned long           nbytes_to_tty;
+	unsigned long           nbytes_from_tty;
+	unsigned long           nbytes_to_host;
 };
 
 /* increase N_PORTS if you need more */
-#define N_PORTS		4
+#define N_PORTS		8
 static struct portmaster {
 	struct mutex	lock;			/* protect open/close */
 	struct gs_port	*port;
 } ports[N_PORTS];
 static unsigned	n_ports;
 
+static struct workqueue_struct *gserial_wq;
+
 #define GS_CLOSE_TIMEOUT		15		/* seconds */
 
 
@@ -362,18 +374,39 @@
 	struct list_head	*pool = &port->write_pool;
 	struct usb_ep		*in = port->port_usb->in;
 	int			status = 0;
+	static long 		prev_len;
 	bool			do_tty_wake = false;
 
 	while (!list_empty(pool)) {
 		struct usb_request	*req;
 		int			len;
 
-		if (port->write_started >= QUEUE_SIZE)
+		if (port->write_started >= TX_QUEUE_SIZE)
 			break;
 
 		req = list_entry(pool->next, struct usb_request, list);
-		len = gs_send_packet(port, req->buf, in->maxpacket);
+		len = gs_send_packet(port, req->buf, TX_BUF_SIZE);
 		if (len == 0) {
+			/* Queue zero length packet explicitly to make it
+			 * work with UDCs which don't support req->zero flag
+			 */
+			if (prev_len && (prev_len % in->maxpacket == 0)) {
+				req->length = 0;
+				list_del(&req->list);
+				spin_unlock(&port->port_lock);
+				status = usb_ep_queue(in, req, GFP_ATOMIC);
+				spin_lock(&port->port_lock);
+				if (!port->port_usb) {
+					gs_free_req(in, req);
+					break;
+				}
+				if (status) {
+					printk(KERN_ERR "%s: %s err %d\n",
+					__func__, "queue", status);
+					list_add(&req->list, pool);
+				}
+				prev_len = 0;
+			}
 			wake_up_interruptible(&port->drain_wait);
 			break;
 		}
@@ -381,7 +414,6 @@
 
 		req->length = len;
 		list_del(&req->list);
-		req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
 
 		pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
 				port->port_num, len, *((u8 *)req->buf),
@@ -397,19 +429,25 @@
 		spin_unlock(&port->port_lock);
 		status = usb_ep_queue(in, req, GFP_ATOMIC);
 		spin_lock(&port->port_lock);
-
+		/*
+		 * If port_usb is NULL, gserial disconnect is called
+		 * while the spinlock is dropped and all requests are
+		 * freed. Free the current request here.
+		 */
+		if (!port->port_usb) {
+			do_tty_wake = false;
+			gs_free_req(in, req);
+			break;
+		}
 		if (status) {
 			pr_debug("%s: %s %s err %d\n",
 					__func__, "queue", in->name, status);
 			list_add(&req->list, pool);
 			break;
 		}
+		prev_len = req->length;
+		port->nbytes_from_tty += req->length;
 
-		port->write_started++;
-
-		/* abort immediately after disconnect */
-		if (!port->port_usb)
-			break;
 	}
 
 	if (do_tty_wake && port->port_tty)
@@ -428,6 +466,7 @@
 {
 	struct list_head	*pool = &port->read_pool;
 	struct usb_ep		*out = port->port_usb->out;
+	unsigned		started = 0;
 
 	while (!list_empty(pool)) {
 		struct usb_request	*req;
@@ -439,12 +478,12 @@
 		if (!tty)
 			break;
 
-		if (port->read_started >= QUEUE_SIZE)
+		if (port->read_started >= RX_QUEUE_SIZE)
 			break;
 
 		req = list_entry(pool->next, struct usb_request, list);
 		list_del(&req->list);
-		req->length = out->maxpacket;
+		req->length = RX_BUF_SIZE;
 
 		/* drop lock while we call out; the controller driver
 		 * may need to call us back (e.g. for disconnect)
@@ -452,7 +491,16 @@
 		spin_unlock(&port->port_lock);
 		status = usb_ep_queue(out, req, GFP_ATOMIC);
 		spin_lock(&port->port_lock);
-
+		/*
+		 * If port_usb is NULL, gserial disconnect is called
+		 * while the spinlock is dropped and all requests are
+		 * freed. Free the current request here.
+		 */
+		if (!port->port_usb) {
+			started = 0;
+			gs_free_req(out, req);
+			break;
+		}
 		if (status) {
 			pr_debug("%s: %s %s err %d\n",
 					__func__, "queue", out->name, status);
@@ -461,9 +509,6 @@
 		}
 		port->read_started++;
 
-		/* abort immediately after disconnect */
-		if (!port->port_usb)
-			break;
 	}
 	return port->read_started;
 }
@@ -478,9 +523,9 @@
  * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
  * can be buffered before the TTY layer's buffers (currently 64 KB).
  */
-static void gs_rx_push(unsigned long _port)
+static void gs_rx_push(struct work_struct *w)
 {
-	struct gs_port		*port = (void *)_port;
+	struct gs_port		*port = container_of(w, struct gs_port, push);
 	struct tty_struct	*tty;
 	struct list_head	*queue = &port->read_queue;
 	bool			disconnect = false;
@@ -533,6 +578,7 @@
 			}
 
 			count = tty_insert_flip_string(tty, packet, size);
+			port->nbytes_to_tty += count;
 			if (count)
 				do_push = true;
 			if (count != size) {
@@ -556,19 +602,18 @@
 	if (tty && do_push)
 		tty_flip_buffer_push(tty);
 
-
 	/* We want our data queue to become empty ASAP, keeping data
 	 * in the tty and ldisc (not here).  If we couldn't push any
 	 * this time around, there may be trouble unless there's an
 	 * implicit tty_unthrottle() call on its way...
 	 *
-	 * REVISIT we should probably add a timer to keep the tasklet
+	 * REVISIT we should probably add a timer to keep the work queue
 	 * from starving ... but it's not clear that case ever happens.
 	 */
 	if (!list_empty(queue) && tty) {
 		if (!test_bit(TTY_THROTTLED, &tty->flags)) {
 			if (do_push)
-				tasklet_schedule(&port->push);
+				queue_work(gserial_wq, &port->push);
 			else
 				pr_warning(PREFIX "%d: RX not scheduled?\n",
 					port->port_num);
@@ -585,19 +630,23 @@
 static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct gs_port	*port = ep->driver_data;
+	unsigned long flags;
 
 	/* Queue all received data until the tty layer is ready for it. */
-	spin_lock(&port->port_lock);
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->nbytes_from_host += req->actual;
 	list_add_tail(&req->list, &port->read_queue);
-	tasklet_schedule(&port->push);
-	spin_unlock(&port->port_lock);
+	queue_work(gserial_wq, &port->push);
+	spin_unlock_irqrestore(&port->port_lock, flags);
 }
 
 static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct gs_port	*port = ep->driver_data;
+	unsigned long flags;
 
-	spin_lock(&port->port_lock);
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->nbytes_to_host += req->actual;
 	list_add(&req->list, &port->write_pool);
 	port->write_started--;
 
@@ -609,7 +658,8 @@
 		/* FALL THROUGH */
 	case 0:
 		/* normal completion */
-		gs_start_tx(port);
+		if (port->port_usb)
+			gs_start_tx(port);
 		break;
 
 	case -ESHUTDOWN:
@@ -618,7 +668,7 @@
 		break;
 	}
 
-	spin_unlock(&port->port_lock);
+	spin_unlock_irqrestore(&port->port_lock, flags);
 }
 
 static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
@@ -636,19 +686,18 @@
 }
 
 static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
-		void (*fn)(struct usb_ep *, struct usb_request *),
+		int num, int size, void (*fn)(struct usb_ep *, struct usb_request *),
 		int *allocated)
 {
 	int			i;
 	struct usb_request	*req;
-	int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
 
 	/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
 	 * do quite that many this time, don't fail ... we just won't
 	 * be as speedy as we might otherwise be.
 	 */
-	for (i = 0; i < n; i++) {
-		req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+	for (i = 0; i < num; i++) {
+		req = gs_alloc_req(ep, size, GFP_ATOMIC);
 		if (!req)
 			return list_empty(head) ? -ENOMEM : 0;
 		req->complete = fn;
@@ -681,13 +730,13 @@
 	 * configurations may use different endpoints with a given port;
 	 * and high speed vs full speed changes packet sizes too.
 	 */
-	status = gs_alloc_requests(ep, head, gs_read_complete,
-		&port->read_allocated);
+	status = gs_alloc_requests(ep, head, RX_QUEUE_SIZE, RX_BUF_SIZE,
+			 gs_read_complete, &port->read_allocated);
 	if (status)
 		return status;
 
 	status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
-			gs_write_complete, &port->write_allocated);
+			TX_QUEUE_SIZE, TX_BUF_SIZE, gs_write_complete, &port->write_allocated);
 	if (status) {
 		gs_free_requests(ep, head, &port->read_allocated);
 		return status;
@@ -697,6 +746,8 @@
 	port->n_read = 0;
 	started = gs_start_rx(port);
 
+	if (!port->port_usb)
+		return -EIO;
 	/* unblock any pending writes into our circular buffer */
 	if (started) {
 		tty_wakeup(port->port_tty);
@@ -798,6 +849,13 @@
 	port->open_count = 1;
 	port->openclose = false;
 
+	/* low_latency means ldiscs work is carried in the same context
+	 * of tty_flip_buffer_push. The same can be called from IRQ with
+	 * low_latency = 0. But better to use a dedicated worker thread
+	 * to push the data.
+	 */
+	tty->low_latency = 1;
+
 	/* if connected, start the I/O stream */
 	if (port->port_usb) {
 		struct gserial	*gser = port->port_usb;
@@ -871,7 +929,7 @@
 
 	/* Iff we're disconnected, there can be no I/O in flight so it's
 	 * ok to free the circular buffer; else just scrub it.  And don't
-	 * let the push tasklet fire again until we're re-opened.
+	 * let the push work queue fire again until we're re-opened.
 	 */
 	if (gser == NULL)
 		gs_buf_free(&port->port_write_buf);
@@ -887,6 +945,22 @@
 			port->port_num, tty, file);
 
 	wake_up_interruptible(&port->close_wait);
+
+	/*
+	 * Freeing the previously queued requests as they are
+	 * allocated again as a part of gs_open()
+	 */
+	if (port->port_usb) {
+		spin_unlock_irq(&port->port_lock);
+		usb_ep_fifo_flush(gser->out);
+		usb_ep_fifo_flush(gser->in);
+		spin_lock_irq(&port->port_lock);
+		gs_free_requests(gser->out, &port->read_queue, NULL);
+		gs_free_requests(gser->out, &port->read_pool, NULL);
+		gs_free_requests(gser->in, &port->write_pool, NULL);
+	}
+	port->read_allocated = port->read_started =
+		port->write_allocated = port->write_started = 0;
 exit:
 	spin_unlock_irq(&port->port_lock);
 }
@@ -985,7 +1059,7 @@
 		 * rts/cts, or other handshaking with the host, but if the
 		 * read queue backs up enough we'll be NAKing OUT packets.
 		 */
-		tasklet_schedule(&port->push);
+		queue_work(gserial_wq, &port->push);
 		pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
 	}
 	spin_unlock_irqrestore(&port->port_lock, flags);
@@ -1009,6 +1083,77 @@
 	return status;
 }
 
+static int gs_tiocmget(struct tty_struct *tty)
+{
+	struct gs_port	*port = tty->driver_data;
+	struct gserial	*gser;
+	unsigned int result = 0;
+
+	spin_lock_irq(&port->port_lock);
+	gser = port->port_usb;
+	if (!gser) {
+		result = -ENODEV;
+		goto fail;
+	}
+
+	if (gser->get_dtr)
+		result |= (gser->get_dtr(gser) ? TIOCM_DTR : 0);
+
+	if (gser->get_rts)
+		result |= (gser->get_rts(gser) ? TIOCM_RTS : 0);
+
+	if (gser->serial_state & TIOCM_CD)
+		result |= TIOCM_CD;
+
+	if (gser->serial_state & TIOCM_RI)
+		result |= TIOCM_RI;
+fail:
+	spin_unlock_irq(&port->port_lock);
+	return result;
+}
+
+static int gs_tiocmset(struct tty_struct *tty,
+	unsigned int set, unsigned int clear)
+{
+	struct gs_port	*port = tty->driver_data;
+	struct gserial *gser;
+	int	status = 0;
+
+	spin_lock_irq(&port->port_lock);
+	gser = port->port_usb;
+	if (!gser) {
+		status = -ENODEV;
+		goto fail;
+	}
+
+	if (set & TIOCM_RI) {
+		if (gser->send_ring_indicator) {
+			gser->serial_state |= TIOCM_RI;
+			status = gser->send_ring_indicator(gser, 1);
+		}
+	}
+	if (clear & TIOCM_RI) {
+		if (gser->send_ring_indicator) {
+			gser->serial_state &= ~TIOCM_RI;
+			status = gser->send_ring_indicator(gser, 0);
+		}
+	}
+	if (set & TIOCM_CD) {
+		if (gser->send_carrier_detect) {
+			gser->serial_state |= TIOCM_CD;
+			status = gser->send_carrier_detect(gser, 1);
+		}
+	}
+	if (clear & TIOCM_CD) {
+		if (gser->send_carrier_detect) {
+			gser->serial_state &= ~TIOCM_CD;
+			status = gser->send_carrier_detect(gser, 0);
+		}
+	}
+fail:
+	spin_unlock_irq(&port->port_lock);
+	return status;
+}
 static const struct tty_operations gs_tty_ops = {
 	.open =			gs_open,
 	.close =		gs_close,
@@ -1019,6 +1164,8 @@
 	.chars_in_buffer =	gs_chars_in_buffer,
 	.unthrottle =		gs_unthrottle,
 	.break_ctl =		gs_break_ctl,
+	.tiocmget  =		gs_tiocmget,
+	.tiocmset  =		gs_tiocmset,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -1038,7 +1185,7 @@
 	init_waitqueue_head(&port->close_wait);
 	init_waitqueue_head(&port->drain_wait);
 
-	tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+	INIT_WORK(&port->push, gs_rx_push);
 
 	INIT_LIST_HEAD(&port->read_pool);
 	INIT_LIST_HEAD(&port->read_queue);
@@ -1052,6 +1199,116 @@
 	return 0;
 }
 
+
+#if defined(CONFIG_DEBUG_FS)
+
+#define BUF_SIZE	512
+
+static ssize_t debug_read_status(struct file *file, char __user *ubuf,
+					size_t count, loff_t *ppos)
+{
+	struct gs_port *ui_dev = file->private_data;
+	struct tty_struct       *tty;
+	struct gserial		*gser;
+	char *buf;
+	unsigned long flags;
+	int i = 0;
+	int ret;
+	int result = 0;
+
+	tty = ui_dev->port_tty;
+	gser = ui_dev->port_usb;
+
+	buf = kzalloc(sizeof(char) * BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&ui_dev->port_lock, flags);
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_from_host: %lu\n", ui_dev->nbytes_from_host);
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_to_tty: %lu\n", ui_dev->nbytes_to_tty);
+
+	i += scnprintf(buf + i, BUF_SIZE - i, "nbytes_with_usb_OUT_txr: %lu\n",
+			(ui_dev->nbytes_from_host - ui_dev->nbytes_to_tty));
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_from_tty: %lu\n", ui_dev->nbytes_from_tty);
+
+	i += scnprintf(buf + i, BUF_SIZE - i,
+		"nbytes_to_host: %lu\n", ui_dev->nbytes_to_host);
+
+	i += scnprintf(buf + i, BUF_SIZE - i, "nbytes_with_usb_IN_txr: %lu\n",
+			(ui_dev->nbytes_from_tty - ui_dev->nbytes_to_host));
+
+	if (tty)
+		i += scnprintf(buf + i, BUF_SIZE - i,
+			"tty_flags: %lu\n", tty->flags);
+
+	if (gser->get_dtr) {
+		result |= (gser->get_dtr(gser) ? TIOCM_DTR : 0);
+		i += scnprintf(buf + i, BUF_SIZE - i,
+			"DTR_status: %d\n", result);
+	}
+
+	spin_unlock_irqrestore(&ui_dev->port_lock, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, i);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t debug_write_reset(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	struct gs_port *ui_dev = file->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui_dev->port_lock, flags);
+	ui_dev->nbytes_from_host = ui_dev->nbytes_to_tty =
+			ui_dev->nbytes_from_tty = ui_dev->nbytes_to_host = 0;
+	spin_unlock_irqrestore(&ui_dev->port_lock, flags);
+
+	return count;
+}
+
+static int serial_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+const struct file_operations debug_rst_ops = {
+	.open = serial_debug_open,
+	.write = debug_write_reset,
+};
+
+const struct file_operations debug_adb_ops = {
+	.open = serial_debug_open,
+	.read = debug_read_status,
+};
+
+static void usb_debugfs_init(struct gs_port *ui_dev, int port_num)
+{
+	struct dentry *dent;
+	char buf[48];
+
+	snprintf(buf, 48, "usb_serial%d", port_num);
+	dent = debugfs_create_dir(buf, 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("readstatus", 0444, dent, ui_dev, &debug_adb_ops);
+	debugfs_create_file("reset", 0222, dent, ui_dev, &debug_rst_ops);
+}
+#else
+static void usb_debugfs_init(struct gs_port *ui_dev) {}
+#endif
+
 /**
  * gserial_setup - initialize TTY driver for one or more ports
  * @g: gadget to associate with these ports
@@ -1090,7 +1347,8 @@
 
 	gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 	gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
-	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
+				| TTY_DRIVER_RESET_TERMIOS;
 	gs_tty_driver->init_termios = tty_std_termios;
 
 	/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
@@ -1109,6 +1367,12 @@
 
 	tty_set_operations(gs_tty_driver, &gs_tty_ops);
 
+	gserial_wq = create_singlethread_workqueue("k_gserial");
+	if (!gserial_wq) {
+		status = -ENOMEM;
+		goto fail;
+	}
+
 	/* make devices be openable */
 	for (i = 0; i < count; i++) {
 		mutex_init(&ports[i].lock);
@@ -1123,6 +1387,7 @@
 	/* export the driver ... */
 	status = tty_register_driver(gs_tty_driver);
 	if (status) {
+		put_tty_driver(gs_tty_driver);
 		pr_err("%s: cannot register, err %d\n",
 				__func__, status);
 		goto fail;
@@ -1138,6 +1403,9 @@
 				__func__, i, PTR_ERR(tty_dev));
 	}
 
+	for (i = 0; i < count; i++)
+		usb_debugfs_init(ports[i].port, i);
+
 	pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
 			count, (count == 1) ? "" : "s");
 
@@ -1145,6 +1413,8 @@
 fail:
 	while (count--)
 		kfree(ports[count].port);
+	if (gserial_wq)
+		destroy_workqueue(gserial_wq);
 	put_tty_driver(gs_tty_driver);
 	gs_tty_driver = NULL;
 	return status;
@@ -1191,7 +1461,7 @@
 		ports[i].port = NULL;
 		mutex_unlock(&ports[i].lock);
 
-		tasklet_kill(&port->push);
+		cancel_work_sync(&port->push);
 
 		/* wait for old opens to finish */
 		wait_event(port->close_wait, gs_closed(port));
@@ -1202,6 +1472,7 @@
 	}
 	n_ports = 0;
 
+	destroy_workqueue(gserial_wq);
 	tty_unregister_driver(gs_tty_driver);
 	put_tty_driver(gs_tty_driver);
 	gs_tty_driver = NULL;
@@ -1340,5 +1611,8 @@
 	port->read_allocated = port->read_started =
 		port->write_allocated = port->write_started = 0;
 
+	port->nbytes_from_host = port->nbytes_to_tty =
+		port->nbytes_from_tty = port->nbytes_to_host = 0;
+
 	spin_unlock_irqrestore(&port->port_lock, flags);
 }
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index 9b0fe64..dadc507 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -38,11 +38,22 @@
 
 	/* REVISIT avoid this CDC-ACM support harder ... */
 	struct usb_cdc_line_coding port_line_coding;	/* 9600-8-N-1 etc */
+	u16				serial_state;
+
+	/* control signal callbacks*/
+	unsigned int (*get_dtr)(struct gserial *p);
+	unsigned int (*get_rts)(struct gserial *p);
 
 	/* notification callbacks */
 	void (*connect)(struct gserial *p);
 	void (*disconnect)(struct gserial *p);
 	int (*send_break)(struct gserial *p, int duration);
+	unsigned int (*send_carrier_detect)(struct gserial *p, unsigned int);
+	unsigned int (*send_ring_indicator)(struct gserial *p, unsigned int);
+	int (*send_modem_ctrl_bits)(struct gserial *p, int ctrl_bits);
+
+	/* notification changes to modem */
+	void (*notify_modem)(void *gser, u8 portno, int ctrl_bits);
 };
 
 /* utilities to allocate/free request and buffer */
@@ -57,6 +68,15 @@
 int gserial_connect(struct gserial *, u8 port_num);
 void gserial_disconnect(struct gserial *);
 
+/* sdio related functions */
+int gsdio_setup(struct usb_gadget *g, unsigned n_ports);
+int gsdio_connect(struct gserial *, u8 port_num);
+void gsdio_disconnect(struct gserial *, u8 portno);
+
+int gsmd_setup(struct usb_gadget *g, unsigned n_ports);
+int gsmd_connect(struct gserial *, u8 port_num);
+void gsmd_disconnect(struct gserial *, u8 portno);
+
 /* functions are bound to configurations by a config or gadget driver */
 int acm_bind_config(struct usb_configuration *c, u8 port_num);
 int gser_bind_config(struct usb_configuration *c, u8 port_num);
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
new file mode 100644
index 0000000..a5ceaff
--- /dev/null
+++ b/drivers/usb/gadget/u_smd.c
@@ -0,0 +1,979 @@
+/*
+ * u_smd.c - utilities for USB gadget serial over smd
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This code also borrows from drivers/usb/gadget/u_serial.c, which is
+ * Copyright (C) 2000 - 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.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 and
+ * only version 2 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <mach/msm_smd.h>
+#include <linux/debugfs.h>
+
+#include "u_serial.h"
+
+#define SMD_RX_QUEUE_SIZE		8
+#define SMD_RX_BUF_SIZE			2048
+
+#define SMD_TX_QUEUE_SIZE		8
+#define SMD_TX_BUF_SIZE			2048
+
+static struct workqueue_struct *gsmd_wq;
+
+#define SMD_N_PORTS	2
+#define CH_OPENED	0
+#define CH_READY	1
+struct smd_port_info {
+	struct smd_channel	*ch;
+	char			*name;
+	unsigned long		flags;
+};
+
+struct smd_port_info smd_pi[SMD_N_PORTS] = {
+	{
+		.name = "DS",
+	},
+	{
+		.name = "UNUSED",
+	},
+};
+
+struct gsmd_port {
+	unsigned		port_num;
+	spinlock_t		port_lock;
+
+	unsigned		n_read;
+	struct list_head	read_pool;
+	struct list_head	read_queue;
+	struct work_struct	push;
+
+	struct list_head	write_pool;
+	struct work_struct	pull;
+
+	struct gserial		*port_usb;
+
+	struct smd_port_info	*pi;
+	struct delayed_work	connect_work;
+
+	/* At present, smd does not notify
+	 * control bit change info from modem
+	 */
+	struct work_struct	update_modem_ctrl_sig;
+
+#define SMD_ACM_CTRL_DTR		0x01
+#define SMD_ACM_CTRL_RTS		0x02
+	unsigned		cbits_to_modem;
+
+#define SMD_ACM_CTRL_DCD		0x01
+#define SMD_ACM_CTRL_DSR		0x02
+#define SMD_ACM_CTRL_BRK		0x04
+#define SMD_ACM_CTRL_RI		0x08
+	unsigned		cbits_to_laptop;
+
+	/* pkt counters */
+	unsigned long		nbytes_tomodem;
+	unsigned long		nbytes_tolaptop;
+};
+
+static struct smd_portmaster {
+	struct mutex lock;
+	struct gsmd_port *port;
+	struct platform_driver pdrv;
+} smd_ports[SMD_N_PORTS];
+static unsigned n_smd_ports;
+
+static void gsmd_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void gsmd_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		gsmd_free_req(ep, req);
+	}
+}
+
+static struct usb_request *
+gsmd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req) {
+		pr_err("%s: usb alloc request failed\n", __func__);
+		return 0;
+	}
+
+	req->length = len;
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		pr_err("%s: request buf allocation failed\n", __func__);
+		usb_ep_free_request(ep, req);
+		return 0;
+	}
+
+	return req;
+}
+
+static int gsmd_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num, int size,
+		void (*cb)(struct usb_ep *ep, struct usb_request *))
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("%s: ep:%p head:%p num:%d size:%d cb:%p", __func__,
+			ep, head, num, size, cb);
+
+	for (i = 0; i < num; i++) {
+		req = gsmd_alloc_req(ep, size, GFP_ATOMIC);
+		if (!req) {
+			pr_debug("%s: req allocated:%d\n", __func__, i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void gsmd_start_rx(struct gsmd_port *port)
+{
+	struct list_head	*pool;
+	struct usb_ep		*out;
+	unsigned long	flags;
+	int ret;
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	if (!port->port_usb) {
+		pr_debug("%s: USB disconnected\n", __func__);
+		goto start_rx_end;
+	}
+
+	pool = &port->read_pool;
+	out = port->port_usb->out;
+
+	while (test_bit(CH_OPENED, &port->pi->flags) && !list_empty(pool)) {
+		struct usb_request	*req;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = SMD_RX_BUF_SIZE;
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		ret = usb_ep_queue(out, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d\n",
+					 __func__, port, port->port_num);
+			list_add_tail(&req->list, pool);
+			break;
+		}
+	}
+start_rx_end:
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void gsmd_rx_push(struct work_struct *w)
+{
+	struct gsmd_port *port = container_of(w, struct gsmd_port, push);
+	struct smd_port_info *pi = port->pi;
+	struct list_head *q;
+
+	pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
+
+	spin_lock_irq(&port->port_lock);
+
+	q = &port->read_queue;
+	while (pi->ch && !list_empty(q)) {
+		struct usb_request *req;
+		int avail;
+
+		req = list_first_entry(q, struct usb_request, list);
+
+		switch (req->status) {
+		case -ESHUTDOWN:
+			pr_debug("%s: req status shutdown portno#%d port:%p\n",
+					__func__, port->port_num, port);
+			goto rx_push_end;
+		default:
+			pr_warning("%s: port:%p port#%d"
+					" Unexpected Rx Status:%d\n", __func__,
+					port, port->port_num, req->status);
+		case 0:
+			/* normal completion */
+			break;
+		}
+
+		avail = smd_write_avail(pi->ch);
+		if (!avail)
+			goto rx_push_end;
+
+		if (req->actual) {
+			char		*packet = req->buf;
+			unsigned	size = req->actual;
+			unsigned	n;
+			int		count;
+
+			n = port->n_read;
+			if (n) {
+				packet += n;
+				size -= n;
+			}
+
+			count = smd_write(pi->ch, packet, size);
+			if (count < 0) {
+				pr_err("%s: smd write failed err:%d\n",
+						__func__, count);
+				goto rx_push_end;
+			}
+
+			if (count != size) {
+				port->n_read += count;
+				goto rx_push_end;
+			}
+
+			port->nbytes_tomodem += count;
+		}
+
+		port->n_read = 0;
+		list_move(&req->list, &port->read_pool);
+	}
+
+rx_push_end:
+	spin_unlock_irq(&port->port_lock);
+
+	gsmd_start_rx(port);
+}
+
+static void gsmd_read_pending(struct gsmd_port *port)
+{
+	int avail;
+
+	if (!port || !port->pi->ch)
+		return;
+
+	/* passing null buffer discards the data */
+	while ((avail = smd_read_avail(port->pi->ch)))
+		smd_read(port->pi->ch, 0, avail);
+
+	return;
+}
+
+static void gsmd_tx_pull(struct work_struct *w)
+{
+	struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
+	struct list_head *pool = &port->write_pool;
+	struct smd_port_info *pi = port->pi;
+	struct usb_ep *in;
+
+	pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
+			port, port->port_num, pool);
+
+	spin_lock_irq(&port->port_lock);
+
+	if (!port->port_usb) {
+		pr_debug("%s: usb is disconnected\n", __func__);
+		spin_unlock_irq(&port->port_lock);
+		gsmd_read_pending(port);
+		return;
+	}
+
+	in = port->port_usb->in;
+	while (pi->ch && !list_empty(pool)) {
+		struct usb_request *req;
+		int avail;
+		int ret;
+
+		avail = smd_read_avail(pi->ch);
+		if (!avail)
+			break;
+
+		avail = avail > SMD_TX_BUF_SIZE ? SMD_TX_BUF_SIZE : avail;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = smd_read(pi->ch, req->buf, avail);
+
+		spin_unlock_irq(&port->port_lock);
+		ret = usb_ep_queue(in, req, GFP_KERNEL);
+		spin_lock_irq(&port->port_lock);
+		if (ret) {
+			pr_err("%s: usb ep out queue failed"
+					"port:%p, port#%d err:%d\n",
+					__func__, port, port->port_num, ret);
+			/* could be usb disconnected */
+			if (!port->port_usb)
+				gsmd_free_req(in, req);
+			else
+				list_add(&req->list, pool);
+			goto tx_pull_end;
+		}
+
+		port->nbytes_tolaptop += req->length;
+	}
+
+tx_pull_end:
+	/* TBD: Check how code behaves on USB bus suspend */
+	if (port->port_usb && smd_read_avail(port->pi->ch) && !list_empty(pool))
+		queue_work(gsmd_wq, &port->pull);
+
+	spin_unlock_irq(&port->port_lock);
+
+	return;
+}
+
+static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsmd_port *port = ep->driver_data;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock(&port->port_lock);
+	if (!test_bit(CH_OPENED, &port->pi->flags) ||
+			req->status == -ESHUTDOWN) {
+		spin_unlock(&port->port_lock);
+		gsmd_free_req(ep, req);
+		return;
+	}
+
+	list_add_tail(&req->list, &port->read_queue);
+	queue_work(gsmd_wq, &port->push);
+	spin_unlock(&port->port_lock);
+
+	return;
+}
+
+static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gsmd_port *port = ep->driver_data;
+
+	pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
+
+	if (!port) {
+		pr_err("%s: port is null\n", __func__);
+		return;
+	}
+
+	spin_lock(&port->port_lock);
+	if (!test_bit(CH_OPENED, &port->pi->flags) ||
+			req->status == -ESHUTDOWN) {
+		spin_unlock(&port->port_lock);
+		gsmd_free_req(ep, req);
+		return;
+	}
+
+	if (req->status)
+		pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
+				__func__, port, port->port_num,
+				ep->name, req->status);
+
+	list_add(&req->list, &port->write_pool);
+	queue_work(gsmd_wq, &port->pull);
+	spin_unlock(&port->port_lock);
+
+	return;
+}
+
+static void gsmd_start_io(struct gsmd_port *port)
+{
+	int		ret = -ENODEV;
+
+	pr_debug("%s: port: %p\n", __func__, port);
+
+	spin_lock(&port->port_lock);
+
+	if (!port->port_usb)
+		goto start_io_out;
+
+	smd_tiocmset_from_cb(port->pi->ch,
+			port->cbits_to_modem,
+			~port->cbits_to_modem);
+
+	ret = gsmd_alloc_requests(port->port_usb->out,
+				&port->read_pool,
+				SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
+				gsmd_read_complete);
+	if (ret) {
+		pr_err("%s: unable to allocate out requests\n",
+				__func__);
+		goto start_io_out;
+	}
+
+	ret = gsmd_alloc_requests(port->port_usb->in,
+				&port->write_pool,
+				SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
+				gsmd_write_complete);
+	if (ret) {
+		gsmd_free_requests(port->port_usb->out, &port->read_pool);
+		pr_err("%s: unable to allocate IN requests\n",
+				__func__);
+		goto start_io_out;
+	}
+
+start_io_out:
+	spin_unlock(&port->port_lock);
+
+	if (ret)
+		return;
+
+	gsmd_start_rx(port);
+}
+
+static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
+{
+	unsigned int acm_sig = 0;
+
+	/* should this needs to be in calling functions ??? */
+	uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
+
+	if (uart_sig & TIOCM_RI)
+		acm_sig |= SMD_ACM_CTRL_RI;
+	if (uart_sig & TIOCM_CD)
+		acm_sig |= SMD_ACM_CTRL_DCD;
+	if (uart_sig & TIOCM_DSR)
+		acm_sig |= SMD_ACM_CTRL_DSR;
+
+	return acm_sig;
+}
+
+static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
+{
+	unsigned int uart_sig = 0;
+
+	/* should this needs to be in calling functions ??? */
+	acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
+
+	if (acm_sig & SMD_ACM_CTRL_DTR)
+		uart_sig |= TIOCM_DTR;
+	if (acm_sig & SMD_ACM_CTRL_RTS)
+		uart_sig |= TIOCM_RTS;
+
+	return uart_sig;
+}
+
+
+static void gsmd_stop_io(struct gsmd_port *port)
+{
+	struct usb_ep	*in;
+	struct usb_ep	*out;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return;
+	}
+	in = port->port_usb->in;
+	out = port->port_usb->out;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	usb_ep_fifo_flush(in);
+	usb_ep_fifo_flush(out);
+
+	spin_lock(&port->port_lock);
+	if (port->port_usb) {
+		gsmd_free_requests(out, &port->read_pool);
+		gsmd_free_requests(out, &port->read_queue);
+		gsmd_free_requests(in, &port->write_pool);
+		port->n_read = 0;
+		port->cbits_to_laptop = 0;
+	}
+
+	if (port->port_usb->send_modem_ctrl_bits)
+		port->port_usb->send_modem_ctrl_bits(
+					port->port_usb,
+					port->cbits_to_laptop);
+	spin_unlock(&port->port_lock);
+
+}
+
+static void gsmd_notify(void *priv, unsigned event)
+{
+	struct gsmd_port *port = priv;
+	struct smd_port_info *pi = port->pi;
+	int i;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		pr_debug("%s: Event data\n", __func__);
+		if (smd_read_avail(pi->ch))
+			queue_work(gsmd_wq, &port->pull);
+		if (smd_write_avail(pi->ch))
+			queue_work(gsmd_wq, &port->push);
+		break;
+	case SMD_EVENT_OPEN:
+		pr_debug("%s: Event Open\n", __func__);
+		set_bit(CH_OPENED, &pi->flags);
+		gsmd_start_io(port);
+		break;
+	case SMD_EVENT_CLOSE:
+		pr_debug("%s: Event Close\n", __func__);
+		clear_bit(CH_OPENED, &pi->flags);
+		gsmd_stop_io(port);
+		break;
+	case SMD_EVENT_STATUS:
+		i = smd_tiocmget(port->pi->ch);
+		port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
+		if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
+			port->port_usb->send_modem_ctrl_bits(port->port_usb,
+						port->cbits_to_laptop);
+		break;
+	}
+}
+
+static void gsmd_connect_work(struct work_struct *w)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	int ret;
+
+	port = container_of(w, struct gsmd_port, connect_work.work);
+	pi = port->pi;
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	if (!test_bit(CH_READY, &pi->flags))
+		return;
+
+	ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
+				&pi->ch, port, gsmd_notify);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			/* port not ready  - retry */
+			pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
+					__func__, pi->name, ret);
+			queue_delayed_work(gsmd_wq, &port->connect_work,
+				msecs_to_jiffies(250));
+		} else {
+			pr_err("%s: unable to open smd port:%s err:%d\n",
+					__func__, pi->name, ret);
+		}
+	}
+}
+
+static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
+{
+	struct gsmd_port *port;
+	int temp;
+	struct gserial *gser = gptr;
+
+	if (portno >= n_smd_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = smd_ports[portno].port;
+
+	temp = convert_acm_sigs_to_uart(ctrl_bits);
+
+	if (temp == port->cbits_to_modem)
+		return;
+
+	port->cbits_to_modem = temp;
+
+	/* usb could send control signal before smd is ready */
+	if (!test_bit(CH_OPENED, &port->pi->flags))
+		return;
+
+	/* if DTR is high, update latest modem info to laptop */
+	if (port->cbits_to_modem & TIOCM_DTR) {
+		unsigned i;
+
+		i = smd_tiocmget(port->pi->ch);
+		port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
+
+		if (gser->send_modem_ctrl_bits)
+			gser->send_modem_ctrl_bits(
+					port->port_usb,
+					port->cbits_to_laptop);
+	}
+
+	smd_tiocmset(port->pi->ch,
+			port->cbits_to_modem,
+			~port->cbits_to_modem);
+}
+
+int gsmd_connect(struct gserial *gser, u8 portno)
+{
+	unsigned long flags;
+	int ret;
+	struct gsmd_port *port;
+
+	pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
+
+	if (portno >= n_smd_ports) {
+		pr_err("%s: Invalid port no#%d", __func__, portno);
+		return -EINVAL;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return -EINVAL;
+	}
+
+	port = smd_ports[portno].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = gser;
+	gser->notify_modem = gsmd_notify_modem;
+	port->nbytes_tomodem = 0;
+	port->nbytes_tolaptop = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	ret = usb_ep_enable(gser->in);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+				__func__, gser->in);
+		port->port_usb = 0;
+		return ret;
+	}
+	gser->in->driver_data = port;
+
+	ret = usb_ep_enable(gser->out);
+	if (ret) {
+		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+				__func__, gser->out);
+		port->port_usb = 0;
+		gser->in->driver_data = 0;
+		return ret;
+	}
+	gser->out->driver_data = port;
+
+	queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0));
+
+	return 0;
+}
+
+void gsmd_disconnect(struct gserial *gser, u8 portno)
+{
+	unsigned long flags;
+	struct gsmd_port *port;
+
+	pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
+
+	if (portno >= n_smd_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, portno);
+		return;
+	}
+
+	if (!gser) {
+		pr_err("%s: gser is null\n", __func__);
+		return;
+	}
+
+	port = smd_ports[portno].port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	/* disable endpoints, aborting down any active I/O */
+	usb_ep_disable(gser->out);
+	usb_ep_disable(gser->in);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	gsmd_free_requests(gser->out, &port->read_pool);
+	gsmd_free_requests(gser->out, &port->read_queue);
+	gsmd_free_requests(gser->in, &port->write_pool);
+	port->n_read = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
+		/* lower the dtr */
+		port->cbits_to_modem = 0;
+		smd_tiocmset(port->pi->ch,
+				port->cbits_to_modem,
+				~port->cbits_to_modem);
+	}
+
+	if (port->pi->ch) {
+		smd_close(port->pi->ch);
+		port->pi->ch = NULL;
+	}
+}
+
+#define SMD_CH_MAX_LEN	20
+static int gsmd_ch_probe(struct platform_device *pdev)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	int i;
+	unsigned long flags;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+		pi = port->pi;
+
+		if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
+			set_bit(CH_READY, &pi->flags);
+			spin_lock_irqsave(&port->port_lock, flags);
+			if (port->port_usb)
+				queue_delayed_work(gsmd_wq, &port->connect_work,
+					msecs_to_jiffies(0));
+			spin_unlock_irqrestore(&port->port_lock, flags);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int gsmd_ch_remove(struct platform_device *pdev)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	int i;
+
+	pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+		pi = port->pi;
+
+		if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
+			clear_bit(CH_READY, &pi->flags);
+			clear_bit(CH_OPENED, &pi->flags);
+			if (pi->ch) {
+				smd_close(pi->ch);
+				pi->ch = NULL;
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+static void gsmd_port_free(int portno)
+{
+	struct gsmd_port *port = smd_ports[portno].port;
+
+	if (!port)
+		kfree(port);
+}
+
+static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
+{
+	struct gsmd_port *port;
+	struct platform_driver *pdrv;
+
+	port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+	port->pi = &smd_pi[portno];
+
+	spin_lock_init(&port->port_lock);
+
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queue);
+	INIT_WORK(&port->push, gsmd_rx_push);
+
+	INIT_LIST_HEAD(&port->write_pool);
+	INIT_WORK(&port->pull, gsmd_tx_pull);
+
+	INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
+
+	smd_ports[portno].port = port;
+	pdrv = &smd_ports[portno].pdrv;
+	pdrv->probe = gsmd_ch_probe;
+	pdrv->remove = gsmd_ch_remove;
+	pdrv->driver.name = port->pi->name;
+	pdrv->driver.owner = THIS_MODULE;
+	platform_driver_register(pdrv);
+
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+	char *buf;
+	unsigned long flags;
+	int temp = 0;
+	int i;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+		pi = port->pi;
+		spin_lock_irqsave(&port->port_lock, flags);
+		temp += scnprintf(buf + temp, 512 - temp,
+				"###PORT:%d###\n"
+				"nbytes_tolaptop: %lu\n"
+				"nbytes_tomodem:  %lu\n"
+				"cbits_to_modem:  %u\n"
+				"cbits_to_laptop: %u\n"
+				"n_read: %u\n"
+				"smd_read_avail: %d\n"
+				"smd_write_avail: %d\n"
+				"CH_OPENED: %d\n"
+				"CH_READY: %d\n",
+				i, port->nbytes_tolaptop, port->nbytes_tomodem,
+				port->cbits_to_modem, port->cbits_to_laptop,
+				port->n_read,
+				pi->ch ? smd_read_avail(pi->ch) : 0,
+				pi->ch ? smd_write_avail(pi->ch) : 0,
+				test_bit(CH_OPENED, &pi->flags),
+				test_bit(CH_READY, &pi->flags));
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+
+}
+
+static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct gsmd_port *port;
+	unsigned long flags;
+	int i;
+
+	for (i = 0; i < n_smd_ports; i++) {
+		port = smd_ports[i].port;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->nbytes_tolaptop = 0;
+		port->nbytes_tomodem = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	return count;
+}
+
+static int debug_smd_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations debug_gsmd_ops = {
+	.open = debug_smd_open,
+	.read = debug_smd_read_stats,
+	.write = debug_smd_reset_stats,
+};
+
+static void gsmd_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("usb_gsmd", 0);
+	if (IS_ERR(dent))
+		return;
+
+	debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
+}
+#else
+static void gsmd_debugfs_init(void) {}
+#endif
+
+int gsmd_setup(struct usb_gadget *g, unsigned count)
+{
+	struct usb_cdc_line_coding	coding;
+	int ret;
+	int i;
+
+	pr_debug("%s: g:%p count: %d\n", __func__, g, count);
+
+	if (!count || count > SMD_N_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
+				__func__, count, g);
+		return -EINVAL;
+	}
+
+	coding.dwDTERate = cpu_to_le32(9600);
+	coding.bCharFormat = 8;
+	coding.bParityType = USB_CDC_NO_PARITY;
+	coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+	gsmd_wq = create_singlethread_workqueue("k_gsmd");
+	if (!gsmd_wq) {
+		pr_err("%s: Unable to create workqueue gsmd_wq\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		mutex_init(&smd_ports[i].lock);
+		n_smd_ports++;
+		ret = gsmd_port_alloc(i, &coding);
+		if (ret) {
+			n_smd_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_smd_ports;
+		}
+	}
+
+	gsmd_debugfs_init();
+
+	return 0;
+free_smd_ports:
+	for (i = 0; i < n_smd_ports; i++)
+		gsmd_port_free(i);
+
+	destroy_workqueue(gsmd_wq);
+
+	return ret;
+}
+
+void gsmd_cleanup(struct usb_gadget *g, unsigned count)
+{
+	/* TBD */
+}
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f788eb8..4357867 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -63,6 +63,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ehci-hcd.
 
+config USB_EHCI_EHSET
+	bool "Embedded High-speed Host Electrical Test Support"
+	depends on USB_EHCI_HCD
+	---help---
+	  This option is required for EHSET Host Compliance Tests support on an
+	  embedded Hi-speed USB Host or OTG port.
+
+	  This enables the software support for the "Single Step Set Featue" test.
+	  Apart from this test, other EHSET tests TEST_SE0/J/K/PACKET are part
+	  of EHCI specification and their support already exists in the EHCI driver.
+
+	  If unsure, say N.
+
 config USB_EHCI_ROOT_HUB_TT
 	bool "Root Hub Transaction Translators"
 	depends on USB_EHCI_HCD
@@ -169,6 +182,24 @@
 	  This driver is not supported on boards like trout which
 	  has an external PHY.
 
+config USB_EHCI_MSM_HSIC
+	bool "Support for HSIC based MSM on-chip EHCI USB controller"
+	depends on USB_EHCI_HCD && ARCH_MSM
+	---help---
+	  Enables support for the HSIC (High Speed Inter-Chip) based
+	  USB Host controller present on the Qualcomm chipsets.
+
+	  HSIC is a supplement to USB 2.0 specification and is preferred
+	  for chip-to-chip interconnect (having maximum circuit length of
+	  10cm) as it removes the analog transceivers.
+
+config USB_EHCI_MSM_HOST4
+	bool "Support for MSM on-chip EHCI USB controller# 4"
+	depends on USB_EHCI_HCD && ARCH_MSM && ARCH_APQ8064
+	---help---
+	  Enables support for the EHCI Compliant USB Host controller# 4
+	  present on the Qualcomm chipsets.
+
 config USB_EHCI_TEGRA
        boolean "NVIDIA Tegra HCD support"
        depends on USB_EHCI_HCD && ARCH_TEGRA
@@ -247,6 +278,21 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called oxu210hp-hcd.
 
+config USB_EHCI_MSM_72K
+	bool "Support for Legacy Qualcomm on-chip EHCI USB controller"
+	depends on USB_EHCI_HCD && USB_MSM_OTG_72K && ARCH_MSM
+	---help---
+	  This driver enables support for USB host controller
+	  in pre 8660 qualcomm chipsets(8660, 7X30, 8X50 and 7X27).
+
+config USB_FS_HOST
+	bool "Support for Full Speed Host Mode"
+	depends on USB_EHCI_MSM_72K && ARCH_QSD8X50
+	default n
+	---help---
+	  Enables support for the full speed USB controller core present
+	  on the Qualcomm chipsets
+
 config USB_ISP116X_HCD
 	tristate "ISP116X HCD support"
 	depends on USB
@@ -588,6 +634,15 @@
 	  To compile this driver a module, choose M here: the module
 	  will be called "whci-hcd".
 
+config USB_PEHCI_HCD
+	tristate "ST-E ISP1763A Host Controller"
+	depends on USB
+	help
+	  Driver for ST-E isp1763A USB Host 2.0 Controllers.
+
+	  To compile this driver a module, choose M here: the module
+	  will be called "pehci".
+
 config USB_HWA_HCD
 	tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 0982bcc..7d35f5b 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -20,6 +20,7 @@
 endif
 
 obj-$(CONFIG_USB_WHCI_HCD)	+= whci/
+obj-$(CONFIG_USB_PEHCI_HCD)	+= pehci/
 
 obj-$(CONFIG_PCI)		+= pci-quirks.o
 
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 680e1a3..87bf3df 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -39,7 +39,7 @@
  * (host controller _Structural_ parameters)
  * see EHCI spec, Table 2-4 for each value
  */
-static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
+static void __maybe_unused dbg_hcs_params (struct ehci_hcd *ehci, char *label)
 {
 	u32	params = ehci_readl(ehci, &ehci->caps->hcs_params);
 
@@ -83,7 +83,7 @@
  * (host controller _Capability_ parameters)
  * see EHCI Spec, Table 2-5 for each value
  * */
-static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
+static void __maybe_unused dbg_hcc_params (struct ehci_hcd *ehci, char *label)
 {
 	u32	params = ehci_readl(ehci, &ehci->caps->hcc_params);
 
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 4a3bc5b..bdde862 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -503,7 +503,7 @@
 	spin_unlock_irq(&ehci->lock);
 }
 
-static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
+static void __maybe_unused ehci_port_power (struct ehci_hcd *ehci, int is_on)
 {
 	unsigned port;
 
@@ -579,8 +579,21 @@
 
 	/* root hub is shut down separately (first, when possible) */
 	spin_lock_irq (&ehci->lock);
-	if (ehci->async)
+	if (ehci->async) {
+		/*
+		 * TODO: Observed that ehci->async next ptr is not
+		 * NULL sometimes which leads to crash in mem_cleanup.
+		 * Root cause is not yet known why this messup is
+		 * happenning.
+		 * The follwing workaround fixes the crash caused
+		 * by this temporarily.
+		 * check if async next ptr is not NULL and unlink
+		 * explictly.
+		 */
+		if (ehci->async->qh_next.ptr != NULL)
+			start_unlink_async(ehci, ehci->async->qh_next.qh);
 		ehci_work (ehci);
+	}
 	spin_unlock_irq (&ehci->lock);
 	ehci_mem_cleanup (ehci);
 
@@ -726,7 +739,7 @@
 }
 
 /* start HC running; it's halted, ehci_init() has been run (once) */
-static int ehci_run (struct usb_hcd *hcd)
+static int __maybe_unused ehci_run (struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	u32			temp;
@@ -931,6 +944,12 @@
 			pstatus = ehci_readl(ehci,
 					 &ehci->regs->port_status[i]);
 
+			/*set RS bit in case of remote wakeup*/
+			if (ehci_is_TDI(ehci) && !(cmd & CMD_RUN) &&
+					(pstatus & PORT_SUSPEND))
+				ehci_writel(ehci, cmd | CMD_RUN,
+						&ehci->regs->command);
+
 			if (pstatus & PORT_OWNER)
 				continue;
 			if (!(test_bit(i, &ehci->suspended_ports) &&
@@ -1200,7 +1219,7 @@
 	spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
-static void
+static void __maybe_unused
 ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
@@ -1258,31 +1277,6 @@
 #define	PCI_DRIVER		ehci_pci_driver
 #endif
 
-#ifdef CONFIG_USB_EHCI_FSL
-#include "ehci-fsl.c"
-#define	PLATFORM_DRIVER		ehci_fsl_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_MXC
-#include "ehci-mxc.c"
-#define PLATFORM_DRIVER		ehci_mxc_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_SH
-#include "ehci-sh.c"
-#define PLATFORM_DRIVER		ehci_hcd_sh_driver
-#endif
-
-#ifdef CONFIG_MIPS_ALCHEMY
-#include "ehci-au1xxx.c"
-#define	PLATFORM_DRIVER		ehci_hcd_au1xxx_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_HCD_OMAP
-#include "ehci-omap.c"
-#define        PLATFORM_DRIVER         ehci_hcd_omap_driver
-#endif
-
 #ifdef CONFIG_PPC_PS3
 #include "ehci-ps3.c"
 #define	PS3_SYSTEM_BUS_DRIVER	ps3_ehci_driver
@@ -1298,100 +1292,253 @@
 #define XILINX_OF_PLATFORM_DRIVER	ehci_hcd_xilinx_of_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_FSL
+#include "ehci-fsl.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MXC
+#include "ehci-mxc.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_SH
+#include "ehci-sh.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_MIPS_ALCHEMY
+#include "ehci-au1xxx.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+#include "ehci-omap.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
 #ifdef CONFIG_PLAT_ORION
 #include "ehci-orion.c"
-#define	PLATFORM_DRIVER		ehci_orion_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_ARCH_IXP4XX
 #include "ehci-ixp4xx.c"
-#define	PLATFORM_DRIVER		ixp4xx_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_W90X900_EHCI
 #include "ehci-w90x900.c"
-#define	PLATFORM_DRIVER		ehci_hcd_w90x900_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_ARCH_AT91
 #include "ehci-atmel.c"
-#define	PLATFORM_DRIVER		ehci_atmel_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_OCTEON_EHCI
 #include "ehci-octeon.c"
-#define PLATFORM_DRIVER		ehci_octeon_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_CNS3XXX_EHCI
 #include "ehci-cns3xxx.c"
-#define PLATFORM_DRIVER		cns3xxx_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_ARCH_VT8500
 #include "ehci-vt8500.c"
-#define	PLATFORM_DRIVER		vt8500_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_PLAT_SPEAR
 #include "ehci-spear.c"
-#define PLATFORM_DRIVER		spear_ehci_hcd_driver
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+#include "ehci-msm72k.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_MSM
 #include "ehci-msm.c"
-#define PLATFORM_DRIVER		ehci_msm_driver
+#include "ehci-msm2.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
 #include "ehci-pmcmsp.c"
-#define	PLATFORM_DRIVER		ehci_hcd_msp_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_TEGRA
 #include "ehci-tegra.c"
-#define PLATFORM_DRIVER		tegra_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_S5P
 #include "ehci-s5p.c"
-#define PLATFORM_DRIVER		s5p_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_ATH79
+#include "ehci-ath79.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_SPARC_LEON
 #include "ehci-grlib.c"
-#define PLATFORM_DRIVER		ehci_grlib_driver
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+#include "ehci-msm-hsic.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_PXA168_EHCI
+#include "ehci-pxa168.c"
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_CPU_XLR
 #include "ehci-xls.c"
-#define PLATFORM_DRIVER		ehci_xls_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_MV
 #include "ehci-mv.c"
-#define        PLATFORM_DRIVER         ehci_mv_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_MACH_LOONGSON1
 #include "ehci-ls1x.c"
-#define PLATFORM_DRIVER		ehci_ls1x_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
 #ifdef CONFIG_USB_EHCI_HCD_PLATFORM
 #include "ehci-platform.c"
-#define PLATFORM_DRIVER		ehci_platform_driver
+#define PLATFORM_DRIVER_PRESENT
 #endif
 
-#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER_PRESENT) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
 #error "missing bus glue for ehci-hcd"
 #endif
 
+static struct platform_driver *plat_drivers[]  = {
+#ifdef CONFIG_USB_EHCI_FSL
+	&ehci_fsl_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MXC
+	&ehci_mxc_driver,
+#endif
+
+#ifdef CONFIG_CPU_SUBTYPE_SH7786
+	&ehci_hcd_sh_driver,
+#endif
+
+#ifdef CONFIG_SOC_AU1200
+	&ehci_hcd_au1xxx_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+	&ehci_hcd_omap_driver,
+#endif
+
+#ifdef CONFIG_PLAT_ORION
+	&ehci_orion_driver,
+#endif
+
+#ifdef CONFIG_ARCH_IXP4XX
+	&ixp4xx_ehci_driver,
+#endif
+
+#ifdef CONFIG_USB_W90X900_EHCI
+	&ehci_hcd_w90x900_driver,
+#endif
+
+#ifdef CONFIG_ARCH_AT91
+	&ehci_atmel_driver,
+#endif
+
+#ifdef CONFIG_USB_OCTEON_EHCI
+	&ehci_octeon_driver,
+#endif
+
+#ifdef CONFIG_USB_CNS3XXX_EHCI
+	&cns3xxx_ehci_driver,
+#endif
+
+#ifdef CONFIG_ARCH_VT8500
+	&vt8500_ehci_driver,
+#endif
+
+#ifdef CONFIG_PLAT_SPEAR
+	&spear_ehci_hcd_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
+	&ehci_hcd_msp_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_TEGRA
+	&tegra_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_S5P
+	&s5p_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_ATH79
+	&ehci_ath79_driver
+#endif
+
+#ifdef CONFIG_SPARC_LEON
+	&ehci_grlib_driver
+#endif
+
+#if defined(CONFIG_USB_EHCI_MSM_72K) || defined(CONFIG_USB_EHCI_MSM)
+	&ehci_msm_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+	&ehci_msm_hsic_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM
+	&ehci_msm2_driver,
+#endif
+
+#ifdef CONFIG_USB_PXA168_EHCI
+	&ehci_pxa168_driver,
+#endif
+
+#ifdef CONFIG_CPU_XLR
+	&ehci_xls_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MV
+	&ehci_mv_driver,
+#endif
+
+#ifdef CONFIG_MACH_LOONGSON1
+	&ehci_ls1x_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PLATFORM
+	&ehci_platform_driver,
+#endif
+};
+
+
 static int __init ehci_hcd_init(void)
 {
-	int retval = 0;
+	int i, retval = 0;
 
 	if (usb_disabled())
 		return -ENODEV;
@@ -1416,11 +1563,14 @@
 	}
 #endif
 
-#ifdef PLATFORM_DRIVER
-	retval = platform_driver_register(&PLATFORM_DRIVER);
-	if (retval < 0)
-		goto clean0;
-#endif
+	for (i = 0; i < ARRAY_SIZE(plat_drivers); i++) {
+		retval = platform_driver_register(plat_drivers[i]);
+		if (retval) {
+			while (--i >= 0)
+				platform_driver_unregister(plat_drivers[i]);
+			goto clean0;
+		}
+	}
 
 #ifdef PCI_DRIVER
 	retval = pci_register_driver(&PCI_DRIVER);
@@ -1463,10 +1613,9 @@
 	pci_unregister_driver(&PCI_DRIVER);
 clean1:
 #endif
-#ifdef PLATFORM_DRIVER
-	platform_driver_unregister(&PLATFORM_DRIVER);
+	for (i = 0; i < ARRAY_SIZE(plat_drivers); i++)
+		platform_driver_unregister(plat_drivers[i]);
 clean0:
-#endif
 #ifdef DEBUG
 	debugfs_remove(ehci_debug_root);
 	ehci_debug_root = NULL;
@@ -1479,15 +1628,17 @@
 
 static void __exit ehci_hcd_cleanup(void)
 {
+	int i;
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER);
 #endif
 #ifdef OF_PLATFORM_DRIVER
 	platform_driver_unregister(&OF_PLATFORM_DRIVER);
 #endif
-#ifdef PLATFORM_DRIVER
-	platform_driver_unregister(&PLATFORM_DRIVER);
-#endif
+
+	for (i = 0; i < ARRAY_SIZE(plat_drivers); i++)
+		platform_driver_unregister(plat_drivers[i]);
+
 #ifdef PCI_DRIVER
 	pci_unregister_driver(&PCI_DRIVER);
 #endif
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 38fe076..5cc70e0 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -254,6 +254,11 @@
 		if (t1 & PORT_OWNER)
 			set_bit(port, &ehci->owned_ports);
 		else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
+			/*clear RS bit before setting SUSP bit
+			* and wait for HCH to get set*/
+			if (ehci->susp_sof_bug)
+				ehci_halt(ehci);
+
 			t2 |= PORT_SUSPEND;
 			set_bit(port, &ehci->bus_suspended);
 		}
@@ -304,9 +309,11 @@
 	if (ehci->bus_suspended)
 		udelay(150);
 
-	/* turn off now-idle HC */
-	ehci_halt (ehci);
-	ehci->rh_state = EHCI_RH_SUSPENDED;
+	/*if this bit is set, controller is already haled*/
+	if (!ehci->susp_sof_bug)
+		ehci_halt(ehci); /* turn off now-idle HC */
+
+	hcd->state = HC_STATE_SUSPENDED;
 
 	if (ehci->reclaim)
 		end_unlink_async(ehci);
@@ -653,6 +660,151 @@
 }
 
 /*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_EHCI_EHSET
+
+#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
+
+static void usb_ehset_completion(struct urb *urb)
+{
+	struct completion  *done = urb->context;
+
+	complete(done);
+}
+static int submit_single_step_set_feature(
+	struct usb_hcd  *hcd,
+	struct urb      *urb,
+	int 		is_setup
+);
+
+/* Allocate a URB and initialize the various fields of it.
+ * This API is used by the single_step_set_feature test of
+ * EHSET where IN packet of the GetDescriptor request is
+ * sent after 15secs of the SETUP packet.
+ * Return NULL if failed.
+ */
+static struct urb *
+request_single_step_set_feature_urb(
+	struct usb_device 	*udev,
+	void 			*dr,
+	void 			*buf,
+	struct completion 	*done
+) {
+	struct urb *urb;
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+	struct usb_host_endpoint	*ep;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	urb->pipe = usb_rcvctrlpipe(udev, 0);
+	ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
+			[usb_pipeendpoint(urb->pipe)];
+	if (!ep) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	/* Initialize the various URB fields as these are used
+	 * by the HCD driver to queue it and as well as
+	 * when completion happens.
+	 */
+	urb->ep = ep;
+	urb->dev = udev;
+	urb->setup_packet = (void *)dr;
+	urb->transfer_buffer = buf;
+	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
+	urb->complete = usb_ehset_completion;
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK)
+				| URB_DIR_IN ;
+	usb_get_urb(urb);
+	atomic_inc(&urb->use_count);
+	atomic_inc(&urb->dev->urbnum);
+	urb->setup_dma = dma_map_single(
+			hcd->self.controller,
+			urb->setup_packet,
+			sizeof(struct usb_ctrlrequest),
+			DMA_TO_DEVICE);
+	urb->transfer_dma = dma_map_single(
+			hcd->self.controller,
+			urb->transfer_buffer,
+			urb->transfer_buffer_length,
+			DMA_FROM_DEVICE);
+	urb->context = done;
+	return urb;
+}
+
+static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+	int retval = -ENOMEM;
+	struct usb_ctrlrequest *dr;
+	struct urb *urb;
+	struct usb_device *udev ;
+	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
+	struct usb_device_descriptor *buf;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	/*Obtain udev of the rhub's child port */
+	udev = hcd->self.root_hub->children[port];
+	if (!udev) {
+		ehci_err(ehci, "No device attached to the RootHub\n");
+		return -ENODEV;
+	}
+	buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+	if (!dr) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	/* Fill Setup packet for GetDescriptor */
+	dr->bRequestType = USB_DIR_IN;
+	dr->bRequest = USB_REQ_GET_DESCRIPTOR;
+	dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+	dr->wIndex = 0;
+	dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
+	urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
+	if (!urb)
+		goto cleanup;
+
+	/* Now complete just the SETUP stage */
+	retval = submit_single_step_set_feature(hcd, urb, 1);
+	if (retval)
+		goto out1;
+	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
+		usb_kill_urb(urb);
+		retval = -ETIMEDOUT;
+		ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
+		goto out1;
+	}
+	msleep(15 * 1000);
+	/* Complete remaining DATA and status stages */
+	/* No need to free the URB, we can reuse the same */
+	urb->status = -EINPROGRESS;
+	usb_get_urb(urb);
+	atomic_inc(&urb->use_count);
+	atomic_inc(&urb->dev->urbnum);
+	retval = submit_single_step_set_feature(hcd, urb, 0);
+	if (!retval && !wait_for_completion_timeout(&done,
+						msecs_to_jiffies(2000))) {
+		usb_kill_urb(urb);
+		retval = -ETIMEDOUT;
+		ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
+	}
+out1:
+	usb_free_urb(urb);
+cleanup:
+	kfree(dr);
+	kfree(buf);
+	return retval;
+}
+#endif
+/*-------------------------------------------------------------------------*/
 
 static int ehci_hub_control (
 	struct usb_hcd	*hcd,
@@ -978,14 +1130,29 @@
 			if ((temp & PORT_PE) == 0
 					|| (temp & PORT_RESET) != 0)
 				goto error;
-
+			/*port gets suspended as part of bus suspend routine*/
+			if (!ehci->susp_sof_bug)
+				ehci_writel(ehci, temp | PORT_SUSPEND,
+						status_reg);
+#ifdef	CONFIG_USB_OTG
+			if (hcd->self.otg_port == (wIndex + 1) &&
+					hcd->self.b_hnp_enable) {
+				set_bit(wIndex, &ehci->suspended_ports);
+				otg_start_hnp(ehci->transceiver->otg);
+				break;
+			}
+#endif
 			/* After above check the port must be connected.
 			 * Set appropriate bit thus could put phy into low power
 			 * mode if we have hostpc feature
 			 */
 			temp &= ~PORT_WKCONN_E;
 			temp |= PORT_WKDISC_E | PORT_WKOC_E;
-			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+			if (ehci->susp_sof_bug)
+				ehci_writel(ehci, temp, status_reg);
+			else
+				ehci_writel(ehci, temp | PORT_SUSPEND,
+						status_reg);
 			if (hostpc_reg) {
 				spin_unlock_irqrestore(&ehci->lock, flags);
 				msleep(5);/* 5ms for HCD enter low pwr mode */
@@ -1041,26 +1208,38 @@
 		 * about the EHCI-specific stuff.
 		 */
 		case USB_PORT_FEAT_TEST:
-			if (!selector || selector > 5)
-				goto error;
-			ehci_quiesce(ehci);
+			if (selector && selector <= 5) {
+				ehci_quiesce(ehci);
 
 			/* Put all enabled ports into suspend */
-			while (ports--) {
-				u32 __iomem *sreg =
+				while (ports--) {
+					u32 __iomem *sreg =
 						&ehci->regs->port_status[ports];
 
-				temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
-				if (temp & PORT_PE)
-					ehci_writel(ehci, temp | PORT_SUSPEND,
+					temp = ehci_readl(ehci, sreg)
+					       	& ~PORT_RWC_BITS;
+					if (temp & PORT_PE)
+						ehci_writel(ehci,
+							temp | PORT_SUSPEND,
 							sreg);
+				}
+				ehci_halt(ehci);
+				temp = ehci_readl(ehci, status_reg);
+				temp |= selector << 16;
+				ehci_writel(ehci, temp, status_reg);
 			}
-			ehci_halt(ehci);
-			temp = ehci_readl(ehci, status_reg);
-			temp |= selector << 16;
-			ehci_writel(ehci, temp, status_reg);
+#ifdef CONFIG_USB_EHCI_EHSET
+			else if (selector
+				  == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
+				spin_unlock_irqrestore(&ehci->lock, flags);
+				retval = ehset_single_step_set_feature(hcd,
+								   wIndex);
+				spin_lock_irqsave(&ehci->lock, flags);
+			}
+#endif
+			else
+				goto error;
 			break;
-
 		default:
 			goto error;
 		}
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
new file mode 100644
index 0000000..fd3d1ca
--- /dev/null
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -0,0 +1,1154 @@
+/* ehci-msm-hsic.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <mach/msm_bus.h>
+
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/gpio.h>
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_xo.h>
+#include <linux/spinlock.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+struct msm_hsic_hcd {
+	struct ehci_hcd		ehci;
+	struct device		*dev;
+	struct clk		*ahb_clk;
+	struct clk		*core_clk;
+	struct clk		*alt_core_clk;
+	struct clk		*phy_clk;
+	struct clk		*cal_clk;
+	struct regulator	*hsic_vddcx;
+	bool			async_int;
+	atomic_t                in_lpm;
+	struct wake_lock	wlock;
+	int			peripheral_status_irq;
+	int			wakeup_irq;
+	int			wakeup_gpio;
+	bool			wakeup_irq_enabled;
+	atomic_t		pm_usage_cnt;
+	uint32_t		bus_perf_client;
+	uint32_t		wakeup_int_cnt;
+};
+
+static bool debug_bus_voting_enabled = true;
+static inline struct msm_hsic_hcd *hcd_to_hsic(struct usb_hcd *hcd)
+{
+	return (struct msm_hsic_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *hsic_to_hcd(struct msm_hsic_hcd *mehci)
+{
+	return container_of((void *) mehci, struct usb_hcd, hcd_priv);
+}
+
+#define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
+
+#define USB_PHY_VDD_DIG_VOL_SUSP_MIN	500000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN		1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX		1320000 /* uV */
+#define USB_PHY_VDD_DIG_LOAD		49360	/* uA */
+
+#define HSIC_DBG1_REG		0x38
+
+static int msm_hsic_init_vddcx(struct msm_hsic_hcd *mehci, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mehci->hsic_vddcx = devm_regulator_get(mehci->dev, "HSIC_VDDCX");
+	if (IS_ERR(mehci->hsic_vddcx)) {
+		dev_err(mehci->dev, "unable to get hsic vddcx\n");
+		return PTR_ERR(mehci->hsic_vddcx);
+	}
+
+	ret = regulator_set_voltage(mehci->hsic_vddcx,
+			USB_PHY_VDD_DIG_VOL_MIN,
+			USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mehci->dev, "unable to set the voltage"
+				"for hsic vddcx\n");
+		return ret;
+	}
+
+	ret = regulator_set_optimum_mode(mehci->hsic_vddcx,
+				USB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		pr_err("%s: Unable to set optimum mode of the regulator:"
+					"VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mehci->hsic_vddcx);
+	if (ret) {
+		dev_err(mehci->dev, "unable to enable hsic vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mehci->hsic_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mehci->hsic_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mehci->hsic_vddcx, 0,
+				USB_PHY_VDD_DIG_VOL_MIN);
+	return ret;
+
+}
+
+static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(mehci->dev, "ulpi_write: timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int msm_hsic_config_gpios(struct msm_hsic_hcd *mehci, int gpio_en)
+{
+	int rc = 0;
+	struct msm_hsic_host_platform_data *pdata;
+	static int gpio_status;
+
+	pdata = mehci->dev->platform_data;
+
+	if (!pdata || !pdata->strobe || !pdata->data)
+		return rc;
+
+	if (gpio_status == gpio_en)
+		return 0;
+
+	gpio_status = gpio_en;
+
+	if (!gpio_en)
+		goto free_gpio;
+
+	rc = gpio_request(pdata->strobe, "HSIC_STROBE_GPIO");
+	if (rc < 0) {
+		dev_err(mehci->dev, "gpio request failed for HSIC STROBE\n");
+		return rc;
+	}
+
+	rc = gpio_request(pdata->data, "HSIC_DATA_GPIO");
+	if (rc < 0) {
+		dev_err(mehci->dev, "gpio request failed for HSIC DATA\n");
+		goto free_strobe;
+		}
+
+	if (mehci->wakeup_gpio) {
+		rc = gpio_request(mehci->wakeup_gpio, "HSIC_WAKEUP_GPIO");
+		if (rc < 0) {
+			dev_err(mehci->dev, "gpio request failed for HSIC WAKEUP\n");
+			goto free_data;
+		}
+	}
+
+	return 0;
+
+free_gpio:
+	if (mehci->wakeup_gpio)
+		gpio_free(mehci->wakeup_gpio);
+free_data:
+	gpio_free(pdata->data);
+free_strobe:
+	gpio_free(pdata->strobe);
+
+	return rc;
+}
+
+static int msm_hsic_phy_clk_reset(struct msm_hsic_hcd *mehci)
+{
+	int ret;
+
+	clk_prepare_enable(mehci->alt_core_clk);
+
+	ret = clk_reset(mehci->core_clk, CLK_RESET_ASSERT);
+	if (ret) {
+		clk_disable_unprepare(mehci->alt_core_clk);
+		dev_err(mehci->dev, "usb phy clk assert failed\n");
+		return ret;
+	}
+	usleep_range(10000, 12000);
+	clk_disable_unprepare(mehci->alt_core_clk);
+
+	ret = clk_reset(mehci->core_clk, CLK_RESET_DEASSERT);
+	if (ret)
+		dev_err(mehci->dev, "usb phy clk deassert failed\n");
+
+	return ret;
+}
+
+static int msm_hsic_phy_reset(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	u32 val;
+	int ret;
+
+	ret = msm_hsic_phy_clk_reset(mehci);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+	dev_dbg(mehci->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+#define HSIC_GPIO150_PAD_CTL   (MSM_TLMM_BASE+0x20C0)
+#define HSIC_GPIO151_PAD_CTL   (MSM_TLMM_BASE+0x20C4)
+#define HSIC_CAL_PAD_CTL       (MSM_TLMM_BASE+0x20C8)
+#define HSIC_LV_MODE		0x04
+#define HSIC_PAD_CALIBRATION	0xA8
+#define HSIC_GPIO_PAD_VAL	0x0A0AAA10
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0;
+	int ret;
+	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
+
+	ret = msm_hsic_phy_reset(mehci);
+	if (ret) {
+		dev_err(mehci->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+	while (cnt < LINK_RESET_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= LINK_RESET_TIMEOUT_USEC)
+		return -ETIMEDOUT;
+
+	/* Reset PORTSC and select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+
+	/* TODO: Need to confirm if HSIC PHY also requires delay after RESET */
+	msleep(100);
+
+	/* HSIC PHY Initialization */
+
+	/* HSIC init sequence when HSIC signals (Strobe/Data) are
+	routed via GPIOs */
+	if (pdata && pdata->strobe && pdata->data) {
+
+		/* Enable LV_MODE in HSIC_CAL_PAD_CTL register */
+		writel_relaxed(HSIC_LV_MODE, HSIC_CAL_PAD_CTL);
+
+		/*set periodic calibration interval to ~2.048sec in
+		  HSIC_IO_CAL_REG */
+		ulpi_write(mehci, 0xFF, 0x33);
+
+		/* Enable periodic IO calibration in HSIC_CFG register */
+		ulpi_write(mehci, HSIC_PAD_CALIBRATION, 0x30);
+
+		/* Configure GPIO 150/151 pins for HSIC functionality mode */
+		ret = msm_hsic_config_gpios(mehci, 1);
+		if (ret) {
+			dev_err(mehci->dev, " gpio configuarion failed\n");
+			return ret;
+		}
+		/* Set LV_MODE=0x1 and DCC=0x2 in HSIC_GPIO150/151_PAD_CTL
+		   register */
+		writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_GPIO150_PAD_CTL);
+		writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_GPIO151_PAD_CTL);
+		/* Enable HSIC mode in HSIC_CFG register */
+		ulpi_write(mehci, 0x01, 0x31);
+	} else {
+		/* HSIC init sequence when HSIC signals (Strobe/Data) are routed
+		via dedicated I/O */
+
+		/* programmable length of connect signaling (33.2ns) */
+		ret = ulpi_write(mehci, 3, HSIC_DBG1_REG);
+		if (ret) {
+			pr_err("%s: Unable to program length of connect "
+			      "signaling\n", __func__);
+		}
+
+		/*set periodic calibration interval to ~2.048sec in
+		  HSIC_IO_CAL_REG */
+		ulpi_write(mehci, 0xFF, 0x33);
+
+		/* Enable HSIC mode in HSIC_CFG register */
+		ulpi_write(mehci, 0xA9, 0x30);
+	}
+
+	/*disable auto resume*/
+	ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL));
+
+	return 0;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0, ret;
+	u32 val;
+
+	if (atomic_read(&mehci->in_lpm)) {
+		dev_dbg(mehci->dev, "%s called in lpm\n", __func__);
+		return 0;
+	}
+
+	if (!(readl_relaxed(USB_PORTSC) & PORT_PE)) {
+		dev_dbg(mehci->dev, "%s:port is not enabled skip suspend\n",
+				__func__);
+		return -EAGAIN;
+	}
+
+	disable_irq(hcd->irq);
+
+	/* make sure we don't race against a remote wakeup */
+	if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) ||
+	    readl_relaxed(USB_PORTSC) & PORT_RESUME) {
+		dev_dbg(mehci->dev, "wakeup pending, aborting suspend\n");
+		enable_irq(hcd->irq);
+		return -EBUSY;
+	}
+
+	/*
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	val = readl_relaxed(USB_PORTSC);
+	val &= ~PORT_RWC_BITS;
+	val |= PORTSC_PHCD;
+	writel_relaxed(val, USB_PORTSC);
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
+		dev_err(mehci->dev, "Unable to suspend PHY\n");
+		msm_hsic_config_gpios(mehci, 0);
+		msm_hsic_reset(mehci);
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	clk_disable_unprepare(mehci->core_clk);
+	clk_disable_unprepare(mehci->phy_clk);
+	clk_disable_unprepare(mehci->cal_clk);
+	clk_disable_unprepare(mehci->ahb_clk);
+
+	ret = regulator_set_voltage(mehci->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_SUSP_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mehci->dev, "unable to set vddcx voltage: min:0.5v max:1.3v\n");
+
+	if (mehci->bus_perf_client && debug_bus_voting_enabled) {
+		ret = msm_bus_scale_client_update_request(
+				mehci->bus_perf_client, 0);
+		if (ret)
+			dev_err(mehci->dev, "%s: Failed to dvote for "
+				   "bus bandwidth %d\n", __func__, ret);
+	}
+
+	atomic_set(&mehci->in_lpm, 1);
+	enable_irq(hcd->irq);
+
+	mehci->wakeup_irq_enabled = 1;
+	enable_irq_wake(mehci->wakeup_irq);
+	enable_irq(mehci->wakeup_irq);
+
+	wake_unlock(&mehci->wlock);
+
+	dev_info(mehci->dev, "HSIC-USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_hsic_resume(struct msm_hsic_hcd *mehci)
+{
+	struct usb_hcd *hcd = hsic_to_hcd(mehci);
+	int cnt = 0, ret;
+	unsigned temp;
+
+	if (!atomic_read(&mehci->in_lpm)) {
+		dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__);
+		return 0;
+	}
+
+	if (mehci->wakeup_irq_enabled) {
+		disable_irq_wake(mehci->wakeup_irq);
+		disable_irq_nosync(mehci->wakeup_irq);
+		mehci->wakeup_irq_enabled = 0;
+	}
+
+	wake_lock(&mehci->wlock);
+
+	if (mehci->bus_perf_client && debug_bus_voting_enabled) {
+		ret = msm_bus_scale_client_update_request(
+				mehci->bus_perf_client, 1);
+		if (ret)
+			dev_err(mehci->dev, "%s: Failed to vote for "
+				   "bus bandwidth %d\n", __func__, ret);
+	}
+
+	ret = regulator_set_voltage(mehci->hsic_vddcx,
+				USB_PHY_VDD_DIG_VOL_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+	if (ret < 0)
+		dev_err(mehci->dev, "unable to set vddcx voltage: min:1v max:1.3v\n");
+
+	clk_prepare_enable(mehci->core_clk);
+	clk_prepare_enable(mehci->phy_clk);
+	clk_prepare_enable(mehci->cal_clk);
+	clk_prepare_enable(mehci->ahb_clk);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC);
+	temp &= ~(PORT_RWC_BITS | PORTSC_PHCD);
+	writel_relaxed(temp, USB_PORTSC);
+	while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+		if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) &&
+			(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+		/*
+		 * This is a fatal error. Reset the link and
+		 * PHY to make hsic working.
+		 */
+		dev_err(mehci->dev, "Unable to resume USB. Reset the hsic\n");
+		msm_hsic_config_gpios(mehci, 0);
+		msm_hsic_reset(mehci);
+	}
+
+skip_phy_resume:
+
+	usb_hcd_resume_root_hub(hcd);
+
+	atomic_set(&mehci->in_lpm, 0);
+
+	if (mehci->async_int) {
+		mehci->async_int = false;
+		pm_runtime_put_noidle(mehci->dev);
+		enable_irq(hcd->irq);
+	}
+
+	if (atomic_read(&mehci->pm_usage_cnt)) {
+		atomic_set(&mehci->pm_usage_cnt, 0);
+		pm_runtime_put_noidle(mehci->dev);
+	}
+
+	dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
+
+	return 0;
+}
+#endif
+
+static irqreturn_t msm_hsic_irq(struct usb_hcd *hcd)
+{
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	if (atomic_read(&mehci->in_lpm)) {
+		disable_irq_nosync(hcd->irq);
+		dev_dbg(mehci->dev, "phy async intr\n");
+		mehci->async_int = true;
+		pm_runtime_get(mehci->dev);
+		return IRQ_HANDLED;
+	}
+
+	return ehci_irq(hcd);
+}
+
+static int ehci_hsic_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	retval = ehci_reset(ehci);
+	if (retval)
+		return retval;
+
+	/* bursts of unspecified length. */
+	writel_relaxed(0, USB_AHBBURST);
+	/* Use the AHB transactor */
+	writel_relaxed(0x08, USB_AHBMODE);
+	/* Disable streaming mode and select host mode */
+	writel_relaxed(0x13, USB_USBMODE);
+
+	ehci_port_power(ehci, 1);
+	return 0;
+}
+
+static struct hc_driver msm_hsic_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Qualcomm EHCI Host Controller using HSIC",
+	.hcd_priv_size		= sizeof(struct msm_hsic_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= msm_hsic_irq,
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= ehci_hsic_reset,
+	.start			= ehci_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.clear_tt_buffer_complete	 = ehci_clear_tt_buffer_complete,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * PM support
+	 */
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+};
+
+static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto put_clocks;
+
+	/*core_clk is required for LINK protocol engine
+	 *clock rate appropriately set by target specific clock driver */
+	mehci->core_clk = clk_get(mehci->dev, "core_clk");
+	if (IS_ERR(mehci->core_clk)) {
+		dev_err(mehci->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mehci->core_clk);
+		return ret;
+	}
+
+	/* alt_core_clk is for LINK to be used during PHY RESET
+	 * clock rate appropriately set by target specific clock driver */
+	mehci->alt_core_clk = clk_get(mehci->dev, "alt_core_clk");
+	if (IS_ERR(mehci->alt_core_clk)) {
+		dev_err(mehci->dev, "failed to core_clk\n");
+		ret = PTR_ERR(mehci->alt_core_clk);
+		goto put_core_clk;
+	}
+
+	/* phy_clk is required for HSIC PHY operation
+	 * clock rate appropriately set by target specific clock driver */
+	mehci->phy_clk = clk_get(mehci->dev, "phy_clk");
+	if (IS_ERR(mehci->phy_clk)) {
+		dev_err(mehci->dev, "failed to get phy_clk\n");
+		ret = PTR_ERR(mehci->phy_clk);
+		goto put_alt_core_clk;
+	}
+
+	/* 10MHz cal_clk is required for calibration of I/O pads */
+	mehci->cal_clk = clk_get(mehci->dev, "cal_clk");
+	if (IS_ERR(mehci->cal_clk)) {
+		dev_err(mehci->dev, "failed to get cal_clk\n");
+		ret = PTR_ERR(mehci->cal_clk);
+		goto put_phy_clk;
+	}
+	clk_set_rate(mehci->cal_clk, 10000000);
+
+	/* ahb_clk is required for data transfers */
+	mehci->ahb_clk = clk_get(mehci->dev, "iface_clk");
+	if (IS_ERR(mehci->ahb_clk)) {
+		dev_err(mehci->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mehci->ahb_clk);
+		goto put_cal_clk;
+	}
+
+	clk_prepare_enable(mehci->core_clk);
+	clk_prepare_enable(mehci->phy_clk);
+	clk_prepare_enable(mehci->cal_clk);
+	clk_prepare_enable(mehci->ahb_clk);
+
+	return 0;
+
+put_clocks:
+	if (!atomic_read(&mehci->in_lpm)) {
+		clk_disable_unprepare(mehci->core_clk);
+		clk_disable_unprepare(mehci->phy_clk);
+		clk_disable_unprepare(mehci->cal_clk);
+		clk_disable_unprepare(mehci->ahb_clk);
+	}
+	clk_put(mehci->ahb_clk);
+put_cal_clk:
+	clk_put(mehci->cal_clk);
+put_phy_clk:
+	clk_put(mehci->phy_clk);
+put_alt_core_clk:
+	clk_put(mehci->alt_core_clk);
+put_core_clk:
+	clk_put(mehci->core_clk);
+
+	return ret;
+}
+static irqreturn_t hsic_peripheral_status_change(int irq, void *dev_id)
+{
+	struct msm_hsic_hcd *mehci = dev_id;
+
+	pr_debug("%s: mehci:%p dev_id:%p\n", __func__, mehci, dev_id);
+
+	if (mehci)
+		msm_hsic_config_gpios(mehci, 0);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_hsic_wakeup_irq(int irq, void *data)
+{
+	struct msm_hsic_hcd *mehci = data;
+
+	mehci->wakeup_int_cnt++;
+	dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt cnt: %u\n",
+			__func__, mehci->wakeup_int_cnt);
+
+	wake_lock(&mehci->wlock);
+
+	if (mehci->wakeup_irq_enabled) {
+		mehci->wakeup_irq_enabled = 0;
+		disable_irq_wake(irq);
+		disable_irq_nosync(irq);
+	}
+
+	if (!atomic_read(&mehci->pm_usage_cnt)) {
+		atomic_set(&mehci->pm_usage_cnt, 1);
+		pm_runtime_get(mehci->dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ehci_hsic_msm_bus_show(struct seq_file *s, void *unused)
+{
+	if (debug_bus_voting_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int ehci_hsic_msm_bus_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ehci_hsic_msm_bus_show, inode->i_private);
+}
+
+static ssize_t ehci_hsic_msm_bus_write(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	char buf[8];
+	int ret;
+	struct seq_file *s = file->private_data;
+	struct msm_hsic_hcd *mehci = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6)) {
+		/* Do not vote here. Let hsic driver decide when to vote */
+		debug_bus_voting_enabled = true;
+	} else {
+		debug_bus_voting_enabled = false;
+		if (mehci->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					mehci->bus_perf_client, 0);
+			if (ret)
+				dev_err(mehci->dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations ehci_hsic_msm_bus_fops = {
+	.open = ehci_hsic_msm_bus_open,
+	.read = seq_read,
+	.write = ehci_hsic_msm_bus_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int ehci_hsic_msm_wakeup_cnt_show(struct seq_file *s, void *unused)
+{
+	struct msm_hsic_hcd *mehci = s->private;
+
+	seq_printf(s, "%u\n", mehci->wakeup_int_cnt);
+
+	return 0;
+}
+
+static int ehci_hsic_msm_wakeup_cnt_open(struct inode *inode, struct file *f)
+{
+	return single_open(f, ehci_hsic_msm_wakeup_cnt_show, inode->i_private);
+}
+
+const struct file_operations ehci_hsic_msm_wakeup_cnt_fops = {
+	.open = ehci_hsic_msm_wakeup_cnt_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static struct dentry *ehci_hsic_msm_dbg_root;
+static int ehci_hsic_msm_debugfs_init(struct msm_hsic_hcd *mehci)
+{
+	struct dentry *ehci_hsic_msm_dentry;
+
+	ehci_hsic_msm_dbg_root = debugfs_create_dir("ehci_hsic_msm_dbg", NULL);
+
+	if (!ehci_hsic_msm_dbg_root || IS_ERR(ehci_hsic_msm_dbg_root))
+		return -ENODEV;
+
+	ehci_hsic_msm_dentry = debugfs_create_file("bus_voting",
+		S_IRUGO | S_IWUSR,
+		ehci_hsic_msm_dbg_root, mehci,
+		&ehci_hsic_msm_bus_fops);
+
+	if (!ehci_hsic_msm_dentry) {
+		debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+		return -ENODEV;
+	}
+
+	ehci_hsic_msm_dentry = debugfs_create_file("wakeup_cnt",
+		S_IRUGO,
+		ehci_hsic_msm_dbg_root, mehci,
+		&ehci_hsic_msm_wakeup_cnt_fops);
+
+	if (!ehci_hsic_msm_dentry) {
+		debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void ehci_hsic_msm_debugfs_cleanup(void)
+{
+	debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+}
+
+static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_hsic_hcd *mehci;
+	struct msm_hsic_host_platform_data *pdata;
+	int ret;
+
+	dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n");
+
+	/* After parent device's probe is executed, it will be put in suspend
+	 * mode. When child device's probe is called, driver core is not
+	 * resuming parent device due to which parent will be in suspend even
+	 * though child is active. Hence resume the parent device explicitly.
+	 */
+	if (pdev->dev.parent)
+		pm_runtime_get_sync(pdev->dev.parent);
+
+	hcd = usb_create_hcd(&msm_hsic_driver, &pdev->dev,
+				dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		return  -ENOMEM;
+	}
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = hcd->irq;
+		goto put_hcd;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_hcd;
+	}
+
+	mehci = hcd_to_hsic(hcd);
+	mehci->dev = &pdev->dev;
+
+	mehci->ehci.susp_sof_bug = 1;
+
+	res = platform_get_resource_byname(pdev,
+			IORESOURCE_IRQ,
+			"peripheral_status_irq");
+	if (res)
+		mehci->peripheral_status_irq = res->start;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup");
+	if (res) {
+		mehci->wakeup_gpio = res->start;
+		mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start);
+		dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
+	}
+
+	ret = msm_hsic_init_clocks(mehci, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize clocks\n");
+		ret = -ENODEV;
+		goto unmap;
+	}
+
+	ret = msm_hsic_init_vddcx(mehci, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+
+	ret = msm_hsic_reset(mehci);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize PHY\n");
+		goto deinit_vddcx;
+	}
+
+	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register HCD\n");
+		goto unconfig_gpio;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mehci->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mehci->wlock);
+
+	if (mehci->peripheral_status_irq) {
+		ret = request_threaded_irq(mehci->peripheral_status_irq,
+			NULL, hsic_peripheral_status_change,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+						| IRQF_SHARED,
+			"hsic_peripheral_status", mehci);
+		if (ret)
+			dev_err(&pdev->dev, "%s:request_irq:%d failed:%d",
+				__func__, mehci->peripheral_status_irq, ret);
+	}
+
+	/* configure wakeup irq */
+	if (mehci->wakeup_irq) {
+		ret = request_irq(mehci->wakeup_irq, msm_hsic_wakeup_irq,
+				IRQF_TRIGGER_HIGH,
+				"msm_hsic_wakeup", mehci);
+		if (!ret) {
+			disable_irq_nosync(mehci->wakeup_irq);
+		} else {
+			dev_err(&pdev->dev, "request_irq(%d) failed: %d\n",
+					mehci->wakeup_irq, ret);
+			mehci->wakeup_irq = 0;
+		}
+	}
+
+	ret = ehci_hsic_msm_debugfs_init(mehci);
+	if (ret)
+		dev_dbg(&pdev->dev, "mode debugfs file is"
+			"not available\n");
+
+	pdata = mehci->dev->platform_data;
+	if (pdata && pdata->bus_scale_table) {
+		mehci->bus_perf_client =
+		    msm_bus_scale_register_client(pdata->bus_scale_table);
+		/* Configure BUS performance parameters for MAX bandwidth */
+		if (mehci->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					mehci->bus_perf_client, 1);
+			if (ret)
+				dev_err(&pdev->dev, "%s: Failed to vote for "
+					   "bus bandwidth %d\n", __func__, ret);
+		} else {
+			dev_err(&pdev->dev, "%s: Failed to register BUS "
+						"scaling client!!\n", __func__);
+		}
+	}
+
+	/*
+	 * This pdev->dev is assigned parent of root-hub by USB core,
+	 * hence, runtime framework automatically calls this driver's
+	 * runtime APIs based on root-hub's state.
+	 */
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	/* Decrement the parent device's counter after probe.
+	 * As child is active, parent will not be put into
+	 * suspend mode.
+	 */
+	if (pdev->dev.parent)
+		pm_runtime_put_sync(pdev->dev.parent);
+
+	return 0;
+
+unconfig_gpio:
+	msm_hsic_config_gpios(mehci, 0);
+deinit_vddcx:
+	msm_hsic_init_vddcx(mehci, 0);
+deinit_clocks:
+	msm_hsic_init_clocks(mehci, 0);
+unmap:
+	iounmap(hcd->regs);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	if (mehci->peripheral_status_irq)
+		free_irq(mehci->peripheral_status_irq, mehci);
+
+	if (mehci->wakeup_irq) {
+		if (mehci->wakeup_irq_enabled)
+			disable_irq_wake(mehci->wakeup_irq);
+		free_irq(mehci->wakeup_irq, mehci);
+	}
+
+	if (mehci->bus_perf_client)
+		msm_bus_scale_unregister_client(mehci->bus_perf_client);
+
+	ehci_hsic_msm_debugfs_cleanup();
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	usb_remove_hcd(hcd);
+	msm_hsic_config_gpios(mehci, 0);
+	msm_hsic_init_vddcx(mehci, 0);
+
+	msm_hsic_init_clocks(mehci, 0);
+	wake_lock_destroy(&mehci->wlock);
+	iounmap(hcd->regs);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_pm_suspend(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	dev_dbg(dev, "ehci-msm-hsic PM suspend\n");
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+
+	ret = msm_hsic_suspend(mehci);
+
+	if (ret && device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	return ret;
+}
+
+static int msm_hsic_pm_resume(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	ret = msm_hsic_resume(mehci);
+	if (ret)
+		return ret;
+
+	/* Bring the device to full powered state upon system resume */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hsic_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "EHCI runtime idle\n");
+	return 0;
+}
+
+static int msm_hsic_runtime_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	dev_dbg(dev, "EHCI runtime suspend\n");
+	return msm_hsic_suspend(mehci);
+}
+
+static int msm_hsic_runtime_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+	dev_dbg(dev, "EHCI runtime resume\n");
+	return msm_hsic_resume(mehci);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
+	SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
+				msm_hsic_runtime_idle)
+};
+#endif
+
+static struct platform_driver ehci_msm_hsic_driver = {
+	.probe	= ehci_hsic_msm_probe,
+	.remove	= __devexit_p(ehci_hsic_msm_remove),
+	.driver = {
+		.name = "msm_hsic_host",
+#ifdef CONFIG_PM
+		.pm = &msm_hsic_dev_pm_ops,
+#endif
+	},
+};
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 9803a55..e8e4e10 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -1,6 +1,6 @@
 /* ehci-msm.c - HSUSB Host Controller Driver Implementation
  *
- * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
  *
  * Partly derived from ehci-fsl.c and ehci-hcd.c
  * Copyright (c) 2000-2004 by David Brownell
@@ -49,7 +49,7 @@
 	/* bursts of unspecified length. */
 	writel(0, USB_AHBBURST);
 	/* Use the AHB transactor */
-	writel(0, USB_AHBMODE);
+	writel_relaxed(0x08, USB_AHBMODE);
 	/* Disable streaming mode and select host mode */
 	writel(0x13, USB_USBMODE);
 
@@ -158,12 +158,8 @@
 		goto put_transceiver;
 	}
 
+	hcd_to_ehci(hcd)->transceiver = phy;
 	device_init_wakeup(&pdev->dev, 1);
-	/*
-	 * OTG device parent of HCD takes care of putting
-	 * hardware into low power mode.
-	 */
-	pm_runtime_no_callbacks(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
 	return 0;
@@ -186,6 +182,7 @@
 	pm_runtime_disable(&pdev->dev);
 	pm_runtime_set_suspended(&pdev->dev);
 
+	hcd_to_ehci(hcd)->transceiver = NULL;
 	otg_set_host(phy->otg, NULL);
 	usb_put_transceiver(phy);
 
@@ -194,7 +191,31 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime idle\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime suspend\n");
+	/*
+	 * Notify OTG about suspend.  It takes care of
+	 * putting the hardware in LPM.
+	 */
+	return usb_phy_set_suspend(phy, 1);
+}
+
+static int ehci_msm_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime resume\n");
+	return usb_phy_set_suspend(phy, 0);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
 static int ehci_msm_pm_suspend(struct device *dev)
 {
 	struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -202,6 +223,9 @@
 
 	dev_dbg(dev, "ehci-msm PM suspend\n");
 
+	if (!hcd->rh_registered)
+		return 0;
+
 	/*
 	 * EHCI helper function has also the same check before manipulating
 	 * port wakeup flags.  We do check here the same condition before
@@ -215,7 +239,7 @@
 				wakeup);
 	}
 
-	return 0;
+	return usb_phy_set_suspend(phy, 1);
 }
 
 static int ehci_msm_pm_resume(struct device *dev)
@@ -223,18 +247,20 @@
 	struct usb_hcd *hcd = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "ehci-msm PM resume\n");
+
+	if (!hcd->rh_registered)
+		return 0;
+
 	ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
 
-	return 0;
+	return usb_phy_set_suspend(phy, 0);
 }
-#else
-#define ehci_msm_pm_suspend	NULL
-#define ehci_msm_pm_resume	NULL
 #endif
 
 static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
-	.suspend         = ehci_msm_pm_suspend,
-	.resume          = ehci_msm_pm_resume,
+	SET_SYSTEM_SLEEP_PM_OPS(ehci_msm_pm_suspend, ehci_msm_pm_resume)
+	SET_RUNTIME_PM_OPS(ehci_msm_runtime_suspend, ehci_msm_runtime_resume,
+				ehci_msm_runtime_idle)
 };
 
 static struct platform_driver ehci_msm_driver = {
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
new file mode 100644
index 0000000..4657283
--- /dev/null
+++ b/drivers/usb/host/ehci-msm2.c
@@ -0,0 +1,1090 @@
+/* ehci-msm2.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/usb/ulpi.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+#include <mach/clk.h>
+#include <mach/msm_xo.h>
+#include <mach/msm_iomap.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+#define PDEV_NAME_LEN 20
+
+struct msm_hcd {
+	struct ehci_hcd				ehci;
+	struct device				*dev;
+	struct clk				*iface_clk;
+	struct clk				*core_clk;
+	struct clk				*alt_core_clk;
+	struct regulator			*hsusb_vddcx;
+	struct regulator			*hsusb_3p3;
+	struct regulator			*hsusb_1p8;
+	struct regulator			*vbus;
+	struct msm_xo_voter			*xo_handle;
+	bool					async_int;
+	bool					vbus_on;
+	atomic_t				in_lpm;
+	struct wake_lock			wlock;
+};
+
+static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
+{
+	return (struct msm_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *mhcd_to_hcd(struct msm_hcd *mhcd)
+{
+	return container_of((void *) mhcd, struct usb_hcd, hcd_priv);
+}
+
+#define HSUSB_PHY_3P3_VOL_MIN		3050000 /* uV */
+#define HSUSB_PHY_3P3_VOL_MAX		3300000 /* uV */
+#define HSUSB_PHY_3P3_HPM_LOAD		50000	/* uA */
+
+#define HSUSB_PHY_1P8_VOL_MIN		1800000 /* uV */
+#define HSUSB_PHY_1P8_VOL_MAX		1800000 /* uV */
+#define HSUSB_PHY_1P8_HPM_LOAD		50000	/* uA */
+
+#define HSUSB_PHY_VDD_DIG_VOL_MIN	1045000	/* uV */
+#define HSUSB_PHY_VDD_DIG_VOL_MAX	1320000	/* uV */
+#define HSUSB_PHY_VDD_DIG_LOAD		49360	/* uA */
+
+static int msm_ehci_init_vddcx(struct msm_hcd *mhcd, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mhcd->hsusb_vddcx = devm_regulator_get(mhcd->dev, "HSUSB_VDDCX");
+	if (IS_ERR(mhcd->hsusb_vddcx)) {
+		dev_err(mhcd->dev, "unable to get ehci vddcx\n");
+		return PTR_ERR(mhcd->hsusb_vddcx);
+	}
+
+	ret = regulator_set_voltage(mhcd->hsusb_vddcx,
+			HSUSB_PHY_VDD_DIG_VOL_MIN,
+			HSUSB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mhcd->dev, "unable to set the voltage"
+				"for ehci vddcx\n");
+		return ret;
+	}
+
+	ret = regulator_set_optimum_mode(mhcd->hsusb_vddcx,
+				HSUSB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		dev_err(mhcd->dev, "%s: Unable to set optimum mode of the"
+				" regulator: VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mhcd->hsusb_vddcx);
+	if (ret) {
+		dev_err(mhcd->dev, "unable to enable ehci vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mhcd->hsusb_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mhcd->hsusb_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mhcd->hsusb_vddcx, 0,
+				HSUSB_PHY_VDD_DIG_VOL_MIN);
+	return ret;
+
+}
+
+static int msm_ehci_ldo_init(struct msm_hcd *mhcd, int init)
+{
+	int rc = 0;
+
+	if (!init)
+		goto put_1p8;
+
+	mhcd->hsusb_3p3 = devm_regulator_get(mhcd->dev, "HSUSB_3p3");
+	if (IS_ERR(mhcd->hsusb_3p3)) {
+		dev_err(mhcd->dev, "unable to get hsusb 3p3\n");
+		return PTR_ERR(mhcd->hsusb_3p3);
+	}
+
+	rc = regulator_set_voltage(mhcd->hsusb_3p3,
+			HSUSB_PHY_3P3_VOL_MIN, HSUSB_PHY_3P3_VOL_MAX);
+	if (rc) {
+		dev_err(mhcd->dev, "unable to set voltage level for"
+				"hsusb 3p3\n");
+		return rc;
+	}
+	mhcd->hsusb_1p8 = devm_regulator_get(mhcd->dev, "HSUSB_1p8");
+	if (IS_ERR(mhcd->hsusb_1p8)) {
+		dev_err(mhcd->dev, "unable to get hsusb 1p8\n");
+		rc = PTR_ERR(mhcd->hsusb_1p8);
+		goto put_3p3_lpm;
+	}
+	rc = regulator_set_voltage(mhcd->hsusb_1p8,
+			HSUSB_PHY_1P8_VOL_MIN, HSUSB_PHY_1P8_VOL_MAX);
+	if (rc) {
+		dev_err(mhcd->dev, "unable to set voltage level for"
+				"hsusb 1p8\n");
+		goto put_1p8;
+	}
+
+	return 0;
+
+put_1p8:
+	regulator_set_voltage(mhcd->hsusb_1p8, 0, HSUSB_PHY_1P8_VOL_MAX);
+put_3p3_lpm:
+	regulator_set_voltage(mhcd->hsusb_3p3, 0, HSUSB_PHY_3P3_VOL_MAX);
+
+	return rc;
+}
+
+#ifdef CONFIG_PM_SLEEP
+#define HSUSB_PHY_SUSP_DIG_VOL_P50  500000
+#define HSUSB_PHY_SUSP_DIG_VOL_P75  750000
+static int msm_ehci_config_vddcx(struct msm_hcd *mhcd, int high)
+{
+	struct msm_usb_host_platform_data *pdata;
+	int max_vol = HSUSB_PHY_VDD_DIG_VOL_MAX;
+	int min_vol;
+	int ret;
+
+	pdata = mhcd->dev->platform_data;
+
+	if (high)
+		min_vol = HSUSB_PHY_VDD_DIG_VOL_MIN;
+	else if (pdata && pdata->dock_connect_irq &&
+			!irq_read_line(pdata->dock_connect_irq))
+		min_vol = HSUSB_PHY_SUSP_DIG_VOL_P75;
+	else
+		min_vol = HSUSB_PHY_SUSP_DIG_VOL_P50;
+
+	ret = regulator_set_voltage(mhcd->hsusb_vddcx, min_vol, max_vol);
+	if (ret) {
+		dev_err(mhcd->dev, "%s: unable to set the voltage of regulator"
+			" HSUSB_VDDCX\n", __func__);
+		return ret;
+	}
+
+	dev_dbg(mhcd->dev, "%s: min_vol:%d max_vol:%d\n", __func__, min_vol,
+								max_vol);
+
+	return ret;
+}
+#else
+static int msm_ehci_config_vddcx(struct msm_hcd *mhcd, int high)
+{
+	return 0;
+}
+#endif
+
+static void msm_ehci_vbus_power(struct msm_hcd *mhcd, bool on)
+{
+	int ret;
+
+	if (!mhcd->vbus) {
+		pr_err("vbus is NULL.");
+		return;
+	}
+
+	if (mhcd->vbus_on == on)
+		return;
+
+	if (on) {
+		ret = regulator_enable(mhcd->vbus);
+		if (ret) {
+			pr_err("unable to enable vbus\n");
+			return;
+		}
+		mhcd->vbus_on = true;
+	} else {
+		ret = regulator_disable(mhcd->vbus);
+		if (ret) {
+			pr_err("unable to disable vbus\n");
+			return;
+		}
+		mhcd->vbus_on = false;
+	}
+}
+
+static irqreturn_t msm_ehci_dock_connect_irq(int irq, void *data)
+{
+	const struct msm_usb_host_platform_data *pdata;
+	struct msm_hcd *mhcd = data;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+
+	pdata = mhcd->dev->platform_data;
+
+	if (atomic_read(&mhcd->in_lpm))
+		usb_hcd_resume_root_hub(hcd);
+
+	if (irq_read_line(pdata->dock_connect_irq)) {
+		dev_dbg(mhcd->dev, "%s:Dock removed disable vbus\n", __func__);
+		msm_ehci_vbus_power(mhcd, 0);
+	} else {
+		dev_dbg(mhcd->dev, "%s:Dock connected enable vbus\n", __func__);
+		msm_ehci_vbus_power(mhcd, 1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int msm_ehci_init_vbus(struct msm_hcd *mhcd, int init)
+{
+	int rc = 0;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	const struct msm_usb_host_platform_data *pdata;
+
+	pdata = mhcd->dev->platform_data;
+
+	if (!init) {
+		if (pdata && pdata->dock_connect_irq)
+			free_irq(pdata->dock_connect_irq, mhcd);
+		return rc;
+	}
+
+	mhcd->vbus = devm_regulator_get(mhcd->dev, "vbus");
+	if (IS_ERR(mhcd->vbus)) {
+		pr_err("Unable to get vbus\n");
+		return -ENODEV;
+	}
+
+	if (pdata) {
+		hcd->power_budget = pdata->power_budget;
+
+		if (pdata->dock_connect_irq) {
+			rc = request_threaded_irq(pdata->dock_connect_irq, NULL,
+					msm_ehci_dock_connect_irq,
+					IRQF_TRIGGER_FALLING |
+					IRQF_TRIGGER_RISING |
+					IRQF_ONESHOT, "msm_ehci_host", mhcd);
+			if (!rc)
+				enable_irq_wake(pdata->dock_connect_irq);
+		}
+	}
+	return rc;
+}
+
+static int msm_ehci_ldo_enable(struct msm_hcd *mhcd, int on)
+{
+	int ret = 0;
+
+	if (IS_ERR(mhcd->hsusb_1p8)) {
+		dev_err(mhcd->dev, "%s: HSUSB_1p8 is not initialized\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	if (IS_ERR(mhcd->hsusb_3p3)) {
+		dev_err(mhcd->dev, "%s: HSUSB_3p3 is not initialized\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	if (on) {
+		ret = regulator_set_optimum_mode(mhcd->hsusb_1p8,
+						HSUSB_PHY_1P8_HPM_LOAD);
+		if (ret < 0) {
+			dev_err(mhcd->dev, "%s: Unable to set HPM of the"
+				" regulator: HSUSB_1p8\n", __func__);
+			return ret;
+		}
+
+		ret = regulator_enable(mhcd->hsusb_1p8);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to enable the hsusb"
+						" 1p8\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(mhcd->hsusb_3p3,
+						HSUSB_PHY_3P3_HPM_LOAD);
+		if (ret < 0) {
+			dev_err(mhcd->dev, "%s: Unable to set HPM of the "
+				"regulator: HSUSB_3p3\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			regulator_disable(mhcd->hsusb_1p8);
+			return ret;
+		}
+
+		ret = regulator_enable(mhcd->hsusb_3p3);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to enable the "
+					"hsusb 3p3\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_3p3, 0);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			regulator_disable(mhcd->hsusb_1p8);
+			return ret;
+		}
+
+	} else {
+		ret = regulator_disable(mhcd->hsusb_1p8);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to disable the "
+					"hsusb 1p8\n", __func__);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+		if (ret < 0)
+			dev_err(mhcd->dev, "%s: Unable to set LPM of the "
+				"regulator: HSUSB_1p8\n", __func__);
+
+		ret = regulator_disable(mhcd->hsusb_3p3);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to disable the "
+					"hsusb 3p3\n", __func__);
+			return ret;
+		}
+		ret = regulator_set_optimum_mode(mhcd->hsusb_3p3, 0);
+		if (ret < 0)
+			dev_err(mhcd->dev, "%s: Unable to set LPM of the "
+					"regulator: HSUSB_3p3\n", __func__);
+	}
+
+	dev_dbg(mhcd->dev, "reg (%s)\n", on ? "HPM" : "LPM");
+
+	return ret < 0 ? ret : 0;
+}
+
+
+#define ULPI_IO_TIMEOUT_USECS	(10 * 1000)
+static int msm_ulpi_read(struct msm_hcd *mhcd, u32 reg)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+
+	/* initiate read operation */
+	writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS);
+	while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(mhcd->dev, "msm_ulpi_read: timeout %08x\n",
+				readl_relaxed(USB_ULPI_VIEWPORT));
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT));
+}
+
+
+static int msm_ulpi_write(struct msm_hcd *mhcd, u32 val, u32 reg)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS);
+	while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(mhcd->dev, "msm_ulpi_write: timeout\n");
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+static int msm_ehci_link_clk_reset(struct msm_hcd *mhcd, bool assert)
+{
+	int ret;
+
+	if (assert) {
+		ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_ASSERT);
+		if (ret)
+			dev_err(mhcd->dev, "usb alt_core_clk assert failed\n");
+	} else {
+		ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_DEASSERT);
+		if (ret)
+			dev_err(mhcd->dev, "usb alt_core_clk deassert failed\n");
+	}
+
+	return ret;
+}
+
+static int msm_ehci_phy_reset(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	u32 val;
+	int ret;
+	int retries;
+
+	ret = msm_ehci_link_clk_reset(mhcd, 1);
+	if (ret)
+		return ret;
+
+	udelay(1);
+
+	ret = msm_ehci_link_clk_reset(mhcd, 0);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	for (retries = 3; retries > 0; retries--) {
+		ret = msm_ulpi_write(mhcd, ULPI_FUNC_CTRL_SUSPENDM,
+				ULPI_CLR(ULPI_FUNC_CTRL));
+		if (!ret)
+			break;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	/* Wakeup the PHY with a reg-access for calibration */
+	for (retries = 3; retries > 0; retries--) {
+		ret = msm_ulpi_read(mhcd, ULPI_DEBUG);
+		if (ret != -ETIMEDOUT)
+			break;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	dev_info(mhcd->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_hsusb_reset(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	int ret;
+
+	clk_prepare_enable(mhcd->alt_core_clk);
+	ret = msm_ehci_phy_reset(mhcd);
+	if (ret) {
+		dev_err(mhcd->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+
+	timeout = jiffies + usecs_to_jiffies(LINK_RESET_TIMEOUT_USEC);
+	while (readl_relaxed(USB_USBCMD) & USBCMD_RESET) {
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		udelay(1);
+	}
+
+	/* select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+
+	msleep(100);
+
+	writel_relaxed(0x0, USB_AHBBURST);
+	writel_relaxed(0x08, USB_AHBMODE);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+	clk_disable_unprepare(mhcd->alt_core_clk);
+
+	/*rising edge interrupts with Dp rise and fall enabled*/
+	msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_RISE);
+	msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_FALL);
+
+	/*Clear the PHY interrupts by reading the PHY interrupt latch register*/
+	msm_ulpi_read(mhcd, ULPI_USB_INT_LATCH);
+
+	return 0;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_ehci_suspend(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	int ret;
+	u32 portsc;
+
+	if (atomic_read(&mhcd->in_lpm)) {
+		dev_dbg(mhcd->dev, "%s called in lpm\n", __func__);
+		return 0;
+	}
+
+	disable_irq(hcd->irq);
+
+	/* Set the PHCD bit, only if it is not set by the controller.
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	portsc = readl_relaxed(USB_PORTSC);
+	if (!(portsc & PORTSC_PHCD)) {
+		writel_relaxed(portsc | PORTSC_PHCD,
+				USB_PORTSC);
+
+		timeout = jiffies + usecs_to_jiffies(PHY_SUSPEND_TIMEOUT_USEC);
+		while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) {
+			if (time_after(jiffies, timeout)) {
+				dev_err(mhcd->dev, "Unable to suspend PHY\n");
+				msm_hsusb_reset(mhcd);
+				break;
+			}
+			udelay(1);
+		}
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	clk_disable_unprepare(mhcd->iface_clk);
+	clk_disable_unprepare(mhcd->core_clk);
+
+	/* usb phy does not require TCXO clock, hence vote for TCXO disable */
+	ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(mhcd->dev, "%s failed to devote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
+
+	msm_ehci_config_vddcx(mhcd, 0);
+
+	atomic_set(&mhcd->in_lpm, 1);
+	enable_irq(hcd->irq);
+	wake_unlock(&mhcd->wlock);
+
+	dev_info(mhcd->dev, "EHCI USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_ehci_resume(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	unsigned temp;
+	int ret;
+
+	if (!atomic_read(&mhcd->in_lpm)) {
+		dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__);
+		return 0;
+	}
+
+	wake_lock(&mhcd->wlock);
+
+	/* Vote for TCXO when waking up the phy */
+	ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(mhcd->dev, "%s failed to vote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
+
+	clk_prepare_enable(mhcd->core_clk);
+	clk_prepare_enable(mhcd->iface_clk);
+
+	msm_ehci_config_vddcx(mhcd, 1);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD;
+	writel_relaxed(temp, USB_PORTSC);
+
+	timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC);
+	while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) ||
+			!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) {
+		if (time_after(jiffies, timeout)) {
+			/*This is a fatal error. Reset the link and PHY*/
+			dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n");
+			msm_hsusb_reset(mhcd);
+			break;
+		}
+		udelay(1);
+	}
+
+skip_phy_resume:
+
+	atomic_set(&mhcd->in_lpm, 0);
+
+	if (mhcd->async_int) {
+		mhcd->async_int = false;
+		pm_runtime_put_noidle(mhcd->dev);
+		enable_irq(hcd->irq);
+	}
+
+	dev_info(mhcd->dev, "EHCI USB exited from low power mode\n");
+
+	return 0;
+}
+#endif
+
+static irqreturn_t msm_ehci_irq(struct usb_hcd *hcd)
+{
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	if (atomic_read(&mhcd->in_lpm)) {
+		disable_irq_nosync(hcd->irq);
+		mhcd->async_int = true;
+		pm_runtime_get(mhcd->dev);
+		return IRQ_HANDLED;
+	}
+
+	return ehci_irq(hcd);
+}
+
+static int msm_ehci_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	retval = ehci_reset(ehci);
+	if (retval)
+		return retval;
+
+	/* bursts of unspecified length. */
+	writel_relaxed(0, USB_AHBBURST);
+	/* Use the AHB transactor */
+	writel_relaxed(0x08, USB_AHBMODE);
+	/* Disable streaming mode and select host mode */
+	writel_relaxed(0x13, USB_USBMODE);
+
+	ehci_port_power(ehci, 1);
+	return 0;
+}
+
+static struct hc_driver msm_hc2_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Qualcomm EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct msm_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= msm_ehci_irq,
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= msm_ehci_reset,
+	.start			= ehci_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.clear_tt_buffer_complete	 = ehci_clear_tt_buffer_complete,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * PM support
+	 */
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+};
+
+static int msm_ehci_init_clocks(struct msm_hcd *mhcd, u32 init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto put_clocks;
+
+	/* 60MHz alt_core_clk is for LINK to be used during PHY RESET  */
+	mhcd->alt_core_clk = clk_get(mhcd->dev, "alt_core_clk");
+	if (IS_ERR(mhcd->alt_core_clk)) {
+		dev_err(mhcd->dev, "failed to get alt_core_clk\n");
+		ret = PTR_ERR(mhcd->alt_core_clk);
+		return ret;
+	}
+	clk_set_rate(mhcd->alt_core_clk, 60000000);
+
+	/* iface_clk is required for data transfers */
+	mhcd->iface_clk = clk_get(mhcd->dev, "iface_clk");
+	if (IS_ERR(mhcd->iface_clk)) {
+		dev_err(mhcd->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mhcd->iface_clk);
+		goto put_alt_core_clk;
+	}
+
+	/* Link's protocol engine is based on pclk which must
+	 * be running >55Mhz and frequency should also not change.
+	 * Hence, vote for maximum clk frequency on its source
+	 */
+	mhcd->core_clk = clk_get(mhcd->dev, "core_clk");
+	if (IS_ERR(mhcd->core_clk)) {
+		dev_err(mhcd->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mhcd->core_clk);
+		goto put_iface_clk;
+	}
+	clk_set_rate(mhcd->core_clk, INT_MAX);
+
+	clk_prepare_enable(mhcd->core_clk);
+	clk_prepare_enable(mhcd->iface_clk);
+
+	return 0;
+
+put_clocks:
+	clk_disable_unprepare(mhcd->iface_clk);
+	clk_disable_unprepare(mhcd->core_clk);
+	clk_put(mhcd->core_clk);
+put_iface_clk:
+	clk_put(mhcd->iface_clk);
+put_alt_core_clk:
+	clk_put(mhcd->alt_core_clk);
+
+	return ret;
+}
+
+static int __devinit ehci_msm2_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_hcd *mhcd;
+	const struct msm_usb_host_platform_data *pdata;
+	char pdev_name[PDEV_NAME_LEN];
+	int ret;
+
+	dev_dbg(&pdev->dev, "ehci_msm2 probe\n");
+
+	hcd = usb_create_hcd(&msm_hc2_driver, &pdev->dev,
+				dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		return  -ENOMEM;
+	}
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = hcd->irq;
+		goto put_hcd;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_hcd;
+	}
+
+	mhcd = hcd_to_mhcd(hcd);
+	mhcd->dev = &pdev->dev;
+
+	snprintf(pdev_name, PDEV_NAME_LEN, "%s.%d", pdev->name, pdev->id);
+	mhcd->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, pdev_name);
+	if (IS_ERR(mhcd->xo_handle)) {
+		dev_err(&pdev->dev, "%s not able to get the handle "
+			"to vote for TCXO D0 buffer\n", __func__);
+		ret = PTR_ERR(mhcd->xo_handle);
+		goto unmap;
+	}
+
+	ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO "
+			"D0 buffer%d\n", __func__, ret);
+		goto free_xo_handle;
+	}
+
+	ret = msm_ehci_init_clocks(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize clocks\n");
+		ret = -ENODEV;
+		goto devote_xo_handle;
+	}
+
+	ret = msm_ehci_init_vddcx(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+
+	ret = msm_ehci_config_vddcx(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
+		goto deinit_vddcx;
+	}
+
+	ret = msm_ehci_ldo_init(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
+		goto deinit_vddcx;
+	}
+
+	ret = msm_ehci_ldo_enable(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+		goto deinit_ldo;
+	}
+
+	ret = msm_ehci_init_vbus(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to get vbus\n");
+		goto disable_ldo;
+	}
+
+	ret = msm_hsusb_reset(mhcd);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb PHY initialization failed\n");
+		goto vbus_deinit;
+	}
+
+	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register HCD\n");
+		goto vbus_deinit;
+	}
+
+	pdata = mhcd->dev->platform_data;
+	if (pdata && (!pdata->dock_connect_irq ||
+				!irq_read_line(pdata->dock_connect_irq)))
+		msm_ehci_vbus_power(mhcd, 1);
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mhcd->wlock);
+	/*
+	 * This pdev->dev is assigned parent of root-hub by USB core,
+	 * hence, runtime framework automatically calls this driver's
+	 * runtime APIs based on root-hub's state.
+	 */
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+vbus_deinit:
+	msm_ehci_init_vbus(mhcd, 0);
+disable_ldo:
+	msm_ehci_ldo_enable(mhcd, 0);
+deinit_ldo:
+	msm_ehci_ldo_init(mhcd, 0);
+deinit_vddcx:
+	msm_ehci_init_vddcx(mhcd, 0);
+deinit_clocks:
+	msm_ehci_init_clocks(mhcd, 0);
+devote_xo_handle:
+	msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
+free_xo_handle:
+	msm_xo_put(mhcd->xo_handle);
+unmap:
+	iounmap(hcd->regs);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int __devexit ehci_msm2_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	usb_remove_hcd(hcd);
+
+	msm_xo_put(mhcd->xo_handle);
+	msm_ehci_vbus_power(mhcd, 0);
+	msm_ehci_init_vbus(mhcd, 0);
+	msm_ehci_ldo_enable(mhcd, 0);
+	msm_ehci_ldo_init(mhcd, 0);
+	msm_ehci_init_vddcx(mhcd, 0);
+
+	msm_ehci_init_clocks(mhcd, 0);
+	wake_lock_destroy(&mhcd->wlock);
+	iounmap(hcd->regs);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ehci_msm2_pm_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "ehci-msm2 PM suspend\n");
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+
+	return msm_ehci_suspend(mhcd);
+
+}
+
+static int ehci_msm2_pm_resume(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "ehci-msm2 PM resume\n");
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	ret = msm_ehci_resume(mhcd);
+	if (ret)
+		return ret;
+
+	/* Bring the device to full powered state upon system resume */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm2_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "EHCI runtime idle\n");
+
+	return 0;
+}
+
+static int ehci_msm2_runtime_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "EHCI runtime suspend\n");
+	return msm_ehci_suspend(mhcd);
+}
+
+static int ehci_msm2_runtime_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "EHCI runtime resume\n");
+	return msm_ehci_resume(mhcd);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops ehci_msm2_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ehci_msm2_pm_suspend, ehci_msm2_pm_resume)
+	SET_RUNTIME_PM_OPS(ehci_msm2_runtime_suspend, ehci_msm2_runtime_resume,
+				ehci_msm2_runtime_idle)
+};
+#endif
+
+static struct platform_driver ehci_msm2_driver = {
+	.probe	= ehci_msm2_probe,
+	.remove	= __devexit_p(ehci_msm2_remove),
+	.driver = {
+		.name = "msm_ehci_host",
+#ifdef CONFIG_PM
+		.pm = &ehci_msm2_dev_pm_ops,
+#endif
+	},
+};
diff --git a/drivers/usb/host/ehci-msm72k.c b/drivers/usb/host/ehci-msm72k.c
new file mode 100644
index 0000000..816e408
--- /dev/null
+++ b/drivers/usb/host/ehci-msm72k.c
@@ -0,0 +1,812 @@
+/* ehci-msm.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+
+#include <mach/board.h>
+#include <mach/rpc_hsusb.h>
+#include <mach/msm_hsusb.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/msm_otg.h>
+#include <mach/clk.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+
+#include <mach/msm72k_otg.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+struct msmusb_hcd {
+	struct ehci_hcd ehci;
+	struct clk *alt_core_clk;
+	struct clk *iface_clk;
+	unsigned in_lpm;
+	struct work_struct lpm_exit_work;
+	spinlock_t lock;
+	struct wake_lock wlock;
+	unsigned int clk_enabled;
+	struct msm_usb_host_platform_data *pdata;
+	unsigned running;
+	struct usb_phy *xceiv;
+	struct work_struct otg_work;
+	unsigned flags;
+	struct msm_otg_ops otg_ops;
+};
+
+static inline struct msmusb_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
+{
+	return (struct msmusb_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *mhcd_to_hcd(struct msmusb_hcd *mhcd)
+{
+	return container_of((void *) mhcd, struct usb_hcd, hcd_priv);
+}
+
+static void msm_xusb_pm_qos_update(struct msmusb_hcd *mhcd, int vote)
+{
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	/* if otg driver is available, it would take
+	 * care of voting for appropriate pclk source
+	 */
+	if (mhcd->xceiv)
+		return;
+
+	if (vote)
+		clk_prepare_enable(pdata->ebi1_clk);
+	else
+		clk_disable_unprepare(pdata->ebi1_clk);
+}
+
+static void msm_xusb_enable_clks(struct msmusb_hcd *mhcd)
+{
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	if (mhcd->clk_enabled)
+		return;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		/* OTG driver takes care of clock management */
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		clk_prepare_enable(mhcd->alt_core_clk);
+		clk_prepare_enable(mhcd->iface_clk);
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X )\n", __func__,
+						pdata->phy_info);
+		return;
+	}
+	mhcd->clk_enabled = 1;
+}
+
+static void msm_xusb_disable_clks(struct msmusb_hcd *mhcd)
+{
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	if (!mhcd->clk_enabled)
+		return;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		/* OTG driver takes care of clock management */
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		clk_disable_unprepare(mhcd->alt_core_clk);
+		clk_disable_unprepare(mhcd->iface_clk);
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X )\n", __func__,
+						pdata->phy_info);
+		return;
+	}
+	mhcd->clk_enabled = 0;
+
+}
+
+static int usb_wakeup_phy(struct usb_hcd *hcd)
+{
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+	int ret = -ENODEV;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		ret = msm_fsusb_resume_phy();
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int usb_suspend_phy(struct usb_hcd *hcd)
+{
+	int ret = 0;
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		ret = msm_fsusb_set_remote_wakeup();
+		ret = msm_fsusb_suspend_phy();
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+		ret = -ENODEV;
+		break;
+	}
+
+	return ret;
+}
+
+static int usb_lpm_enter(struct usb_hcd *hcd)
+{
+	struct device *dev = container_of((void *)hcd, struct device,
+							platform_data);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	disable_irq(hcd->irq);
+	if (mhcd->in_lpm) {
+		pr_info("%s: already in lpm. nothing to do\n", __func__);
+		enable_irq(hcd->irq);
+		return 0;
+	}
+
+	if (HC_IS_RUNNING(hcd->state)) {
+		pr_info("%s: can't enter into lpm. controller is runnning\n",
+			__func__);
+		enable_irq(hcd->irq);
+		return -1;
+	}
+
+	pr_info("%s: lpm enter procedure started\n", __func__);
+
+	mhcd->in_lpm = 1;
+
+	if (usb_suspend_phy(hcd)) {
+		mhcd->in_lpm = 0;
+		enable_irq(hcd->irq);
+		pr_info("phy suspend failed\n");
+		pr_info("%s: lpm enter procedure end\n", __func__);
+		return -1;
+	}
+
+	msm_xusb_disable_clks(mhcd);
+
+	if (mhcd->xceiv && mhcd->xceiv->set_suspend)
+		mhcd->xceiv->set_suspend(mhcd->xceiv, 1);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+	enable_irq(hcd->irq);
+	pr_info("%s: lpm enter procedure end\n", __func__);
+	return 0;
+}
+#endif
+
+void usb_lpm_exit_w(struct work_struct *work)
+{
+	struct msmusb_hcd *mhcd = container_of((void *) work,
+			struct msmusb_hcd, lpm_exit_work);
+
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+
+	struct device *dev = container_of((void *)hcd, struct device,
+							platform_data);
+	msm_xusb_enable_clks(mhcd);
+
+
+	if (usb_wakeup_phy(hcd)) {
+		pr_err("fatal error: cannot bring phy out of lpm\n");
+		return;
+	}
+
+	/* If resume signalling finishes before lpm exit, PCD is not set in
+	 * USBSTS register. Drive resume signal to the downstream device now
+	 * so that EHCI can process the upcoming port change interrupt.*/
+
+	writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+
+	if (mhcd->xceiv && mhcd->xceiv->set_suspend)
+		mhcd->xceiv->set_suspend(mhcd->xceiv, 0);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+	enable_irq(hcd->irq);
+}
+
+static void usb_lpm_exit(struct usb_hcd *hcd)
+{
+	unsigned long flags;
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (!mhcd->in_lpm) {
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return;
+	}
+	mhcd->in_lpm = 0;
+	disable_irq_nosync(hcd->irq);
+	schedule_work(&mhcd->lpm_exit_work);
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+}
+
+static irqreturn_t ehci_msm_irq(struct usb_hcd *hcd)
+{
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_otg *otg = container_of(mhcd->xceiv, struct msm_otg, phy);
+
+	/*
+	 * OTG scheduled a work to get Integrated PHY out of LPM,
+	 * WAIT till then */
+	if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED)
+		if (atomic_read(&otg->in_lpm))
+			return IRQ_HANDLED;
+
+	return ehci_irq(hcd);
+}
+
+#ifdef CONFIG_PM
+
+static int ehci_msm_bus_suspend(struct usb_hcd *hcd)
+{
+	int ret;
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct device *dev = hcd->self.controller;
+
+	ret = ehci_bus_suspend(hcd);
+	if (ret) {
+		pr_err("ehci_bus suspend faield\n");
+		return ret;
+	}
+	if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED)
+		ret = usb_phy_set_suspend(mhcd->xceiv, 1);
+	else
+		ret = usb_lpm_enter(hcd);
+
+	pm_runtime_put_noidle(dev);
+	pm_runtime_suspend(dev);
+	wake_unlock(&mhcd->wlock);
+	return ret;
+}
+
+static int ehci_msm_bus_resume(struct usb_hcd *hcd)
+{
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct device *dev = hcd->self.controller;
+
+	wake_lock(&mhcd->wlock);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_resume(dev);
+
+	if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED) {
+		usb_phy_set_suspend(mhcd->xceiv, 0);
+	} else { /* PMIC serial phy */
+		usb_lpm_exit(hcd);
+		if (cancel_work_sync(&(mhcd->lpm_exit_work)))
+			usb_lpm_exit_w(&mhcd->lpm_exit_work);
+	}
+
+	return ehci_bus_resume(hcd);
+
+}
+
+#else
+
+#define ehci_msm_bus_suspend NULL
+#define ehci_msm_bus_resume NULL
+
+#endif	/* CONFIG_PM */
+
+static int ehci_msm_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_reset(ehci);
+
+	/* SW workaround for USB stability issues*/
+	writel(0x0, USB_AHB_MODE);
+	writel(0x0, USB_AHB_BURST);
+
+	return retval;
+}
+
+#define PTS_VAL(x) (PHY_TYPE(x) == USB_PHY_SERIAL_PMIC) ? PORTSC_PTS_SERIAL : \
+							PORTSC_PTS_ULPI
+
+static int ehci_msm_run(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci  = hcd_to_ehci(hcd);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	int             retval = 0;
+	int     	port   = HCS_N_PORTS(ehci->hcs_params);
+	u32 __iomem     *reg_ptr;
+	u32             hcc_params;
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	hcd->uses_new_polling = 1;
+	set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+	/* set hostmode */
+	reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
+	ehci_writel(ehci, (USBMODE_VBUS | USBMODE_SDIS), reg_ptr);
+
+	/* port configuration - phy, port speed, port power, port enable */
+	while (port--)
+		ehci_writel(ehci, (PTS_VAL(pdata->phy_info) | PORT_POWER |
+				PORT_PE), &ehci->regs->port_status[port]);
+
+	ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+	ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+	hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
+	if (HCC_64BIT_ADDR(hcc_params))
+		ehci_writel(ehci, 0, &ehci->regs->segment);
+
+	ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+	ehci->command |= CMD_RUN;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+	ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+
+	hcd->state = HC_STATE_RUNNING;
+
+	/*Enable appropriate Interrupts*/
+	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
+
+	return retval;
+}
+
+static struct hc_driver msm_hc_driver = {
+	.description		= hcd_name,
+	.product_desc 		= "Qualcomm On-Chip EHCI Host Controller",
+	.hcd_priv_size 		= sizeof(struct msmusb_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq 			= ehci_msm_irq,
+	.flags 			= HCD_USB2,
+
+	.reset 			= ehci_msm_reset,
+	.start 			= ehci_msm_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.bus_suspend		= ehci_msm_bus_suspend,
+	.bus_resume		= ehci_msm_bus_resume,
+	.relinquish_port	= ehci_relinquish_port,
+
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static void msm_hsusb_request_host(void *handle, int request)
+{
+	struct msmusb_hcd *mhcd = handle;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+	struct msm_otg *otg = container_of(mhcd->xceiv, struct msm_otg, phy);
+#ifdef CONFIG_USB_OTG
+	struct usb_device *udev = hcd->self.root_hub;
+#endif
+	struct device *dev = hcd->self.controller;
+
+	switch (request) {
+#ifdef CONFIG_USB_OTG
+	case REQUEST_HNP_SUSPEND:
+		/* disable Root hub auto suspend. As hardware is configured
+		 * for peripheral mode, mark hardware is not available.
+		 */
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) {
+			pm_runtime_disable(&udev->dev);
+			/* Mark root hub as disconnected. This would
+			 * protect suspend/resume via sysfs.
+			 */
+			udev->state = USB_STATE_NOTATTACHED;
+			clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+			hcd->state = HC_STATE_HALT;
+			pm_runtime_put_noidle(dev);
+			pm_runtime_suspend(dev);
+		}
+		break;
+	case REQUEST_HNP_RESUME:
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) {
+			pm_runtime_get_noresume(dev);
+			pm_runtime_resume(dev);
+			disable_irq(hcd->irq);
+			ehci_msm_reset(hcd);
+			ehci_msm_run(hcd);
+			set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+			pm_runtime_enable(&udev->dev);
+			udev->state = USB_STATE_CONFIGURED;
+			enable_irq(hcd->irq);
+		}
+		break;
+#endif
+	case REQUEST_RESUME:
+		usb_hcd_resume_root_hub(hcd);
+		break;
+	case REQUEST_START:
+		if (mhcd->running)
+			break;
+		pm_runtime_get_noresume(dev);
+		pm_runtime_resume(dev);
+		wake_lock(&mhcd->wlock);
+		msm_xusb_pm_qos_update(mhcd, 1);
+		msm_xusb_enable_clks(mhcd);
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED)
+			if (otg->set_clk)
+				otg->set_clk(mhcd->xceiv, 1);
+		if (pdata->vbus_power)
+			pdata->vbus_power(pdata->phy_info, 1);
+		if (pdata->config_gpio)
+			pdata->config_gpio(1);
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+		mhcd->running = 1;
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED)
+			if (otg->set_clk)
+				otg->set_clk(mhcd->xceiv, 0);
+		break;
+	case REQUEST_STOP:
+		if (!mhcd->running)
+			break;
+		mhcd->running = 0;
+		/* come out of lpm before deregistration */
+		if (PHY_TYPE(pdata->phy_info) == USB_PHY_SERIAL_PMIC) {
+			usb_lpm_exit(hcd);
+			if (cancel_work_sync(&(mhcd->lpm_exit_work)))
+				usb_lpm_exit_w(&mhcd->lpm_exit_work);
+		}
+		usb_remove_hcd(hcd);
+		if (pdata->config_gpio)
+			pdata->config_gpio(0);
+		if (pdata->vbus_power)
+			pdata->vbus_power(pdata->phy_info, 0);
+		msm_xusb_disable_clks(mhcd);
+		wake_lock_timeout(&mhcd->wlock, HZ/2);
+		msm_xusb_pm_qos_update(mhcd, 0);
+		pm_runtime_put_noidle(dev);
+		pm_runtime_suspend(dev);
+		break;
+	}
+}
+
+static void msm_hsusb_otg_work(struct work_struct *work)
+{
+	struct msmusb_hcd *mhcd;
+
+	mhcd = container_of(work, struct msmusb_hcd, otg_work);
+	msm_hsusb_request_host((void *)mhcd, mhcd->flags);
+}
+static void msm_hsusb_start_host(struct usb_bus *bus, int start)
+{
+	struct usb_hcd *hcd = bus_to_hcd(bus);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	mhcd->flags = start;
+	if (in_interrupt())
+		schedule_work(&mhcd->otg_work);
+	else
+		msm_hsusb_request_host((void *)mhcd, mhcd->flags);
+
+}
+
+static int msm_xusb_init_phy(struct msmusb_hcd *mhcd)
+{
+	int ret = -ENODEV;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		ret = 0;
+	case USB_PHY_SERIAL_PMIC:
+		msm_xusb_enable_clks(mhcd);
+		writel(0, USB_USBINTR);
+		ret = msm_fsusb_rpc_init(&mhcd->otg_ops);
+		if (!ret)
+			msm_fsusb_init_phy();
+		msm_xusb_disable_clks(mhcd);
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+	}
+
+	return ret;
+}
+
+static int msm_xusb_rpc_close(struct msmusb_hcd *mhcd)
+{
+	int retval = -ENODEV;
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		if (!mhcd->xceiv)
+			retval = msm_hsusb_rpc_close();
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		retval = msm_fsusb_reset_phy();
+		msm_fsusb_rpc_deinit();
+		break;
+	default:
+		pr_err("%s: undefined phy type ( %X ) \n", __func__,
+						pdata->phy_info);
+	}
+	return retval;
+}
+
+static int msm_xusb_init_host(struct platform_device *pdev,
+			      struct msmusb_hcd *mhcd)
+{
+	int ret = 0;
+	struct msm_otg *otg;
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		msm_hsusb_rpc_connect();
+
+		if (pdata->vbus_init)
+			pdata->vbus_init(1);
+
+		/* VBUS might be present. Turn off vbus */
+		if (pdata->vbus_power)
+			pdata->vbus_power(pdata->phy_info, 0);
+
+		INIT_WORK(&mhcd->otg_work, msm_hsusb_otg_work);
+		mhcd->xceiv = usb_get_transceiver();
+		if (!mhcd->xceiv)
+			return -ENODEV;
+		otg = container_of(mhcd->xceiv, struct msm_otg, phy);
+		hcd->regs = otg->regs;
+		otg->start_host = msm_hsusb_start_host;
+
+		ret = otg_set_host(mhcd->xceiv->otg, &hcd->self);
+		ehci->transceiver = mhcd->xceiv;
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+
+		if (!hcd->regs)
+			return -EFAULT;
+		/* get usb clocks */
+		mhcd->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk");
+		if (IS_ERR(mhcd->alt_core_clk)) {
+			iounmap(hcd->regs);
+			return PTR_ERR(mhcd->alt_core_clk);
+		}
+
+		mhcd->iface_clk = clk_get(&pdev->dev, "iface_clk");
+		if (IS_ERR(mhcd->iface_clk)) {
+			iounmap(hcd->regs);
+			clk_put(mhcd->alt_core_clk);
+			return PTR_ERR(mhcd->iface_clk);
+		}
+		mhcd->otg_ops.request = msm_hsusb_request_host;
+		mhcd->otg_ops.handle = (void *) mhcd;
+		ret = msm_xusb_init_phy(mhcd);
+		if (ret < 0) {
+			iounmap(hcd->regs);
+			clk_put(mhcd->alt_core_clk);
+			clk_put(mhcd->iface_clk);
+		}
+		break;
+	default:
+		pr_err("phy type is bad\n");
+	}
+	return ret;
+}
+
+static int __devinit ehci_msm_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_usb_host_platform_data *pdata;
+	int retval;
+	struct msmusb_hcd *mhcd;
+
+	hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+	if (!hcd)
+		return  -ENOMEM;
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		usb_put_hcd(hcd);
+		return hcd->irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		usb_put_hcd(hcd);
+		return -ENODEV;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+
+	mhcd = hcd_to_mhcd(hcd);
+	spin_lock_init(&mhcd->lock);
+	mhcd->in_lpm = 0;
+	mhcd->running = 0;
+	device_init_wakeup(&pdev->dev, 1);
+
+	pdata = pdev->dev.platform_data;
+	if (PHY_TYPE(pdata->phy_info) == USB_PHY_UNDEFINED) {
+		usb_put_hcd(hcd);
+		return -ENODEV;
+	}
+	hcd->power_budget = pdata->power_budget;
+	mhcd->pdata = pdata;
+	INIT_WORK(&mhcd->lpm_exit_work, usb_lpm_exit_w);
+
+	wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	pdata->ebi1_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(pdata->ebi1_clk))
+		pdata->ebi1_clk = NULL;
+	else
+		clk_set_rate(pdata->ebi1_clk, INT_MAX);
+
+	retval = msm_xusb_init_host(pdev, mhcd);
+
+	if (retval < 0) {
+		wake_lock_destroy(&mhcd->wlock);
+		usb_put_hcd(hcd);
+		clk_put(pdata->ebi1_clk);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	return retval;
+}
+
+static void msm_xusb_uninit_host(struct msmusb_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata = mhcd->pdata;
+
+	switch (PHY_TYPE(pdata->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		if (pdata->vbus_init)
+			pdata->vbus_init(0);
+		hcd_to_ehci(hcd)->transceiver = NULL;
+		otg_set_host(mhcd->xceiv->otg, NULL);
+		usb_put_transceiver(mhcd->xceiv);
+		cancel_work_sync(&mhcd->otg_work);
+		break;
+	case USB_PHY_SERIAL_PMIC:
+		iounmap(hcd->regs);
+		clk_put(mhcd->alt_core_clk);
+		clk_put(mhcd->iface_clk);
+		msm_fsusb_reset_phy();
+		msm_fsusb_rpc_deinit();
+		break;
+	default:
+		pr_err("phy type is bad\n");
+	}
+}
+static int __exit ehci_msm_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
+	struct msm_usb_host_platform_data *pdata;
+	int retval = 0;
+
+	pdata = pdev->dev.platform_data;
+	device_init_wakeup(&pdev->dev, 0);
+
+	msm_hsusb_request_host((void *)mhcd, REQUEST_STOP);
+	msm_xusb_uninit_host(mhcd);
+	retval = msm_xusb_rpc_close(mhcd);
+
+	wake_lock_destroy(&mhcd->wlock);
+	usb_put_hcd(hcd);
+	clk_put(pdata->ebi1_clk);
+
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	return retval;
+}
+
+static int ehci_msm_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
+	.runtime_suspend = ehci_msm_runtime_suspend,
+	.runtime_resume = ehci_msm_runtime_resume,
+	.runtime_idle = ehci_msm_runtime_idle
+};
+
+static struct platform_driver ehci_msm_driver = {
+	.probe	= ehci_msm_probe,
+	.remove	= __exit_p(ehci_msm_remove),
+	.driver	= {.name = "msm_hsusb_host",
+		    .pm = &ehci_msm_dev_pm_ops, },
+};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 36ca507..4c59eab 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1155,6 +1155,111 @@
 }
 
 /*-------------------------------------------------------------------------*/
+/* This function creates the qtds and submits them for the
+ * SINGLE_STEP_SET_FEATURE Test.
+ * This is done in two parts: first SETUP req for GetDesc is sent then
+ * 15 seconds later, the IN stage for GetDesc starts to req data from dev
+ *
+ * is_setup : i/p arguement decides which of the two stage needs to be
+ * performed; TRUE - SETUP and FALSE - IN+STATUS
+ * Returns 0 if success
+ */
+#ifdef CONFIG_USB_EHCI_EHSET
+static int
+submit_single_step_set_feature(
+	struct usb_hcd  *hcd,
+	struct urb      *urb,
+	int 		is_setup
+) {
+	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
+	struct list_head	qtd_list;
+	struct list_head	*head ;
+
+	struct ehci_qtd		*qtd, *qtd_prev;
+	dma_addr_t		buf;
+	int			len, maxpacket;
+	u32			token;
+
+	INIT_LIST_HEAD(&qtd_list);
+	head = &qtd_list;
+
+	/*
+	 * URBs map to sequences of QTDs:  one logical transaction
+	 */
+	qtd = ehci_qtd_alloc(ehci, GFP_KERNEL);
+	if (unlikely(!qtd))
+		return -1;
+	list_add_tail(&qtd->qtd_list, head);
+	qtd->urb = urb;
+
+	token = QTD_STS_ACTIVE;
+	token |= (EHCI_TUNE_CERR << 10);
+
+	len = urb->transfer_buffer_length;
+	/* Check if the request is to perform just the SETUP stage (getDesc)
+	 * as in SINGLE_STEP_SET_FEATURE test, DATA stage (IN) happens
+	 * 15 secs after the setup
+	 */
+	if (is_setup) {
+		/* SETUP pid */
+		qtd_fill(ehci, qtd, urb->setup_dma,
+				sizeof(struct usb_ctrlrequest),
+				token | (2 /* "setup" */ << 8), 8);
+
+		submit_async(ehci, urb, &qtd_list, GFP_ATOMIC);
+		return 0; /*Return now; we shall come back after 15 seconds*/
+	}
+
+	/*---------------------------------------------------------------------
+	 * IN: data transfer stage:  buffer setup : start the IN txn phase for
+	 * the get_Desc SETUP which was sent 15seconds back
+	 */
+	token ^= QTD_TOGGLE;   /*We need to start IN with DATA-1 Pid-sequence*/
+	buf = urb->transfer_dma;
+
+	token |= (1 /* "in" */ << 8);  /*This is IN stage*/
+
+	maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, 0));
+
+	qtd_fill(ehci, qtd, buf, len, token, maxpacket);
+
+	/* Our IN phase shall always be a short read; so keep the queue running
+	* and let it advance to the next qtd which zero length OUT status */
+
+	qtd->hw_alt_next = EHCI_LIST_END(ehci);
+
+	/*----------------------------------------------------------------------
+	 * STATUS stage for GetDesc control request
+	 */
+	token ^= 0x0100;	/* "in" <--> "out"  */
+	token |= QTD_TOGGLE;	/* force DATA1 */
+
+	qtd_prev = qtd;
+	qtd = ehci_qtd_alloc(ehci, GFP_ATOMIC);
+	if (unlikely(!qtd))
+		goto cleanup;
+	qtd->urb = urb;
+	qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+	list_add_tail(&qtd->qtd_list, head);
+
+	/* dont fill any data in such packets */
+	qtd_fill(ehci, qtd, 0, 0, token, 0);
+
+	/* by default, enable interrupt on urb completion */
+	if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT)))
+		qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
+
+	submit_async(ehci, urb, &qtd_list, GFP_KERNEL);
+
+	return 0;
+
+cleanup:
+	qtd_list_free(ehci, urb, head);
+	return -1;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
 
 /* the async qh for the qtds being reclaimed are now unlinked from the HC */
 
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 2694ed6..c69104d 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -149,6 +149,7 @@
 	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
 	unsigned		has_synopsys_hc_bug:1; /* Synopsys HC */
 	unsigned		frame_index_bug:1; /* MosChip (AKA NetMos) */
+	unsigned		susp_sof_bug:1; /*Chip Idea HC*/
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
@@ -748,6 +749,23 @@
 
 #endif
 
+/*
+ * Writing to dma coherent memory on ARM may be delayed via L2
+ * writing buffer, so introduce the helper which can flush L2 writing
+ * buffer into memory immediately, especially used to flush ehci
+ * descriptor to memory.
+ * */
+#ifdef	CONFIG_ARM_DMA_MEM_BUFFERABLE
+static inline void ehci_sync_mem(void)
+{
+	mb();
+}
+#else
+static inline void ehci_sync_mem(void)
+{
+}
+#endif
+
 /*-------------------------------------------------------------------------*/
 
 #ifdef CONFIG_PCI
diff --git a/drivers/usb/host/pehci/Makefile b/drivers/usb/host/pehci/Makefile
new file mode 100644
index 0000000..8c0d17f
--- /dev/null
+++ b/drivers/usb/host/pehci/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the pehci driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_PEHCI_HCD) += hal/ host/
+
diff --git a/drivers/usb/host/pehci/hal/Makefile b/drivers/usb/host/pehci/hal/Makefile
new file mode 100644
index 0000000..91408e5
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the pehci driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_PEHCI_HCD) += hal_msm.o
+
diff --git a/drivers/usb/host/pehci/hal/hal_intf.h b/drivers/usb/host/pehci/hal/hal_intf.h
new file mode 100644
index 0000000..2d66e57
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/hal_intf.h
@@ -0,0 +1,313 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : hal
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a hardware abstraction layer header file.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef HAL_INTF_H
+#define HAL_INTF_H
+
+
+/* Specify package here instead of including package.h */
+/* #include "package.h" */
+#define HCD_PACKAGE
+
+#define NON_PCI
+//#define PXA300
+
+//#define MSEC_INT_BASED
+#ifdef MSEC_INT_BASED
+#define THREAD_BASED 
+#endif
+
+#ifndef DATABUS_WIDTH_16
+#define DATABUS_WIDTH_16
+#endif
+
+#ifdef	DATABUS_WIDTH_16
+/*DMA SUPPORT */
+/* #define	ENABLE_PLX_DMA */
+//#undef	ENABLE_PLX_DMA//PXA300
+#endif
+
+//#define	EDGE_INTERRUPT
+//#define 	POL_HIGH_INTERRUPT
+
+#define	DMA_BUF_SIZE	(4096 * 2)
+
+#define ISP1763_CHIPID  0x176320
+
+/* Values for id_flags filed of isp1763_driver_t */
+#define ISP1763_HC				0	/* Host Controller Driver */
+#define ISP1763_DC				1	/* Device Controller Driver */
+#define ISP1763_OTG				2	/* Otg Controller Driver */
+#define ISP1763_LAST_DEV			(ISP1763_OTG + 1)
+#define ISP1763_1ST_DEV				(ISP1763_HC)
+
+#ifdef PXA300
+#define HC_SPARAMS_REG					(0x04<<1)	/* Structural Parameters Register */
+#define HC_CPARAMS_REG					(0x08<<1)	/* Capability Parameters Register */
+
+#define HC_USBCMD_REG						(0x8C<<1)	/* USB Command Register */
+#define HC_USBSTS_REG						(0x90<<1)	/* USB Status Register */
+#define HC_INTERRUPT_REG_EHCI				(0x94<<1)	/* INterrupt Enable Register */
+#define HC_FRINDEX_REG						(0x98<<1)	/* Frame Index Register */
+
+#define HC_CONFIGFLAG_REG					(0x9C<<1)	/* Conigured Flag  Register */
+#define HC_PORTSC1_REG					(0xA0<<1)	/* Port Status Control for Port1 */
+
+/*ISO Transfer Registers */
+#define HC_ISO_PTD_DONEMAP_REG			(0xA4<<1)	/* ISO PTD Done Map Register */
+#define HC_ISO_PTD_SKIPMAP_REG			(0xA6<<1)	/* ISO PTD Skip Map Register */
+#define HC_ISO_PTD_LASTPTD_REG				(0xA8<<1)	/* ISO PTD Last PTD Register */
+
+/*INT Transfer Registers */
+#define HC_INT_PTD_DONEMAP_REG			(0xAA<<1)	/* INT PTD Done Map Register */
+#define HC_INT_PTD_SKIPMAP_REG				(0xAC<<1)	/* INT PTD Skip Map Register */
+#define HC_INT_PTD_LASTPTD_REG				(0xAE<<1)	/* INT PTD Last PTD Register  */
+
+/*ATL Transfer Registers */
+#define HC_ATL_PTD_DONEMAP_REG			(0xB0<<1)	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_SKIPMAP_REG				(0xB2<<1)	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_LASTPTD_REG				(0xB4<<1)	/* ATL PTD Last PTD Register  */
+
+/*General Purpose Registers */
+#define HC_HW_MODE_REG					(0x0C<<1)	/* H/W Mode Register  */
+#define HC_CHIP_ID_REG						(0x70<<1)	/* Chip ID Register */
+#define HC_SCRATCH_REG					(0x78<<1)	/* Scratch Register */
+#define HC_RESET_REG						(0xB8<<1)	/* HC Reset Register */
+#define HC_HWMODECTRL_REG				(0xB6<<1)
+#define HC_UNLOCK_DEVICE					(0x7C<<1)
+
+/* Interrupt Registers */
+#define HC_INTERRUPT_REG					(0xD4<<1)	/* Interrupt Register */
+#define HC_INTENABLE_REG					(0xD6<<1)	/* Interrupt enable Register */
+#define HC_ISO_IRQ_MASK_OR_REG			(0xD8<<1)	/* ISO Mask OR Register */
+#define HC_INT_IRQ_MASK_OR_REG			(0xDA<<1)	/* INT Mask OR Register */
+#define HC_ATL_IRQ_MASK_OR_REG			(0xDC<<1)	/* ATL Mask OR Register */
+#define HC_ISO_IRQ_MASK_AND_REG			(0xDE<<1)	/* ISO Mask AND Register */
+#define HC_INT_IRQ_MASK_AND_REG			(0xE0<<1)	/* INT Mask AND Register */
+#define HC_ATL_IRQ_MASK_AND_REG			(0xE2<<1)	/* ATL Mask AND Register */
+
+/*power control reg */
+#define HC_POWER_DOWN_CONTROL_REG		(0xD0<<1)
+
+/*RAM Registers */
+#define HC_DMACONFIG_REG					(0xBC<<1)	/* DMA Config Register */
+#define HC_MEM_READ_REG					(0xC4<<1)	/* Memory Register */
+#define HC_DATA_REG						(0xC6<<1)	/* Data Register */
+
+#define OTG_CTRL_SET_REG					(0xE4<<1)
+#define OTG_CTRL_CLEAR_REG					(0xE6<<1)
+#define OTG_SOURCE_REG					(0xE8<<1)
+
+#define OTG_INTR_EN_F_SET_REG				(0xF0<<1)
+#define OTG_INTR_EN_R_SET_REG				(0xF4<<1)	/* OTG Interrupt Enable Rise register */
+
+#else
+#define HC_SPARAMS_REG					0x04	/* Structural Parameters Register */
+#define HC_CPARAMS_REG					0x08	/* Capability Parameters Register */
+
+#define HC_USBCMD_REG					0x8C	/* USB Command Register */
+#define HC_USBSTS_REG					0x90	/* USB Status Register */
+#define HC_INTERRUPT_REG_EHCI			0x94	/* INterrupt Enable Register */
+#define HC_FRINDEX_REG					0x98	/* Frame Index Register */
+
+#define HC_CONFIGFLAG_REG				0x9C	/* Conigured Flag  Register */
+#define HC_PORTSC1_REG					0xA0	/* Port Status Control for Port1 */
+
+/*ISO Transfer Registers */
+#define HC_ISO_PTD_DONEMAP_REG			0xA4	/* ISO PTD Done Map Register */
+#define HC_ISO_PTD_SKIPMAP_REG			0xA6	/* ISO PTD Skip Map Register */
+#define HC_ISO_PTD_LASTPTD_REG			0xA8	/* ISO PTD Last PTD Register */
+
+/*INT Transfer Registers */
+#define HC_INT_PTD_DONEMAP_REG			0xAA	/* INT PTD Done Map Register */
+#define HC_INT_PTD_SKIPMAP_REG			0xAC	/* INT PTD Skip Map Register */
+#define HC_INT_PTD_LASTPTD_REG			0xAE	/* INT PTD Last PTD Register  */
+
+/*ATL Transfer Registers */
+#define HC_ATL_PTD_DONEMAP_REG			0xB0	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_SKIPMAP_REG			0xB2	/* ATL PTD Last PTD Register  */
+#define HC_ATL_PTD_LASTPTD_REG			0xB4	/* ATL PTD Last PTD Register  */
+
+/*General Purpose Registers */
+#define HC_HW_MODE_REG					0x0C //0xB6	/* H/W Mode Register  */
+#define HC_CHIP_ID_REG					0x70	/* Chip ID Register */
+#define HC_SCRATCH_REG					0x78	/* Scratch Register */
+#define HC_RESET_REG					0xB8	/* HC Reset Register */
+#define HC_HWMODECTRL_REG				0xB6 //0x0C /* H/W Mode control Register  */
+#define HC_UNLOCK_DEVICE				0x7C
+
+/* Interrupt Registers */
+#define HC_INTERRUPT_REG				0xD4	/* Interrupt Register */
+#define HC_INTENABLE_REG				0xD6	/* Interrupt enable Register */
+#define HC_ISO_IRQ_MASK_OR_REG			0xD8	/* ISO Mask OR Register */
+#define HC_INT_IRQ_MASK_OR_REG			0xDA	/* INT Mask OR Register */
+#define HC_ATL_IRQ_MASK_OR_REG			0xDC	/* ATL Mask OR Register */
+#define HC_ISO_IRQ_MASK_AND_REG			0xDE	/* ISO Mask AND Register */
+#define HC_INT_IRQ_MASK_AND_REG			0xE0	/* INT Mask AND Register */
+#define HC_ATL_IRQ_MASK_AND_REG			0xE2	/* ATL Mask AND Register */
+
+/*power control reg */
+#define HC_POWER_DOWN_CONTROL_REG		0xD0
+
+/*RAM Registers */
+#define HC_DMACONFIG_REG				0xBC	/* DMA Config Register */
+#define HC_MEM_READ_REG					0xC4	/* Memory Register */
+#define HC_DATA_REG						0xC6	/* Data Register */
+
+#define OTG_CTRL_SET_REG				0xE4
+#define OTG_CTRL_CLEAR_REG				0xE6
+#define OTG_SOURCE_REG					0xE8
+
+#define OTG_INTR_EN_F_SET_REG			0xF0	/* OTG Interrupt Enable Fall register */
+#define OTG_INTR_EN_R_SET_REG			0xF4	/* OTG Interrupt Enable Rise register */
+
+#endif
+
+#define	OTG_CTRL_DPPULLUP				0x0001
+#define	OTG_CTRL_DPPULLDOWN				0x0002
+#define	OTG_CTRL_DMPULLDOWN				0x0004
+#define	OTG_CTRL_VBUS_DRV				0x0010
+#define	OTG_CTRL_VBUS_DISCHRG			0x0020
+#define	OTG_CTRL_VBUS_CHRG				0x0040
+#define	OTG_CTRL_SW_SEL_HC_DC			0x0080
+#define	OTG_CTRL_BDIS_ACON_EN			0x0100
+#define	OTG_CTRL_OTG_SE0_EN				0x0200
+#define	OTG_CTRL_OTG_DISABLE			0x0400
+#define	OTG_CTRL_VBUS_DRV_PORT2			0x1000
+#define	OTG_CTRL_SW_SEL_HC_2			0x8000
+
+/*interrupt count and buffer status register*/
+
+
+#ifdef PXA300
+#define HC_BUFFER_STATUS_REG			(0xBA<<1)
+#define HC_INT_THRESHOLD_REG			(0xC8<<1)
+#else
+#define HC_BUFFER_STATUS_REG			0xBA
+#define HC_INT_THRESHOLD_REG			0xC8
+#endif
+
+#define HC_OTG_INTERRUPT				0x400
+
+#ifdef PXA300
+#define DC_CHIPID						(0x70<<1)
+#else
+#define DC_CHIPID						0x70
+#endif
+
+
+#ifdef PXA300
+#define FPGA_CONFIG_REG				(0x100<<1)
+#else
+#define FPGA_CONFIG_REG					0x100
+#endif
+
+#define HC_HW_MODE_GOBAL_INTR_ENABLE	0x01
+#define HC_HW_MODE_INTR_EDGE			0x02
+#define HC_HW_MODE_INTR_POLARITY_HIGH	0x04
+#define HC_HW_MODE_LOCK				0x08
+#define HC_HW_MODE_DATABUSWIDTH_8	0x10
+#define HC_HW_MODE_DREQ_POL_HIGH		0x20
+#define HC_HW_MODE_DACK_POL_HIGH		0x40
+#define HC_HW_MODE_COMN_INT			0x80
+
+struct isp1763_driver;
+typedef struct _isp1763_id {
+	u16 idVendor;
+	u16 idProduct;
+	u32 driver_info;
+} isp1763_id;
+
+typedef struct isp1763_dev {
+	/*added for pci device */
+#ifdef  NON_PCI 
+		struct platform_device *dev;
+#else /*PCI*/
+	struct pci_dev *pcidev;
+#endif
+	struct isp1763_driver *driver;	/* which driver has allocated this device */
+	void *driver_data;	/* data private to the host controller driver */
+	void *otg_driver_data;	/*data private for otg controler */
+	unsigned char index;	/* local controller (HC/DC/OTG) */
+	unsigned int irq;	/*Interrupt Channel allocated for this device */
+	void (*handler) (struct isp1763_dev * dev, void *isr_data);	/* Interrupt Serrvice Routine */
+	void *isr_data;		/* isr data of the driver */
+	unsigned long int_reg;	/* Interrupt register */
+	unsigned long alt_int_reg;	/* Interrupt register 2 */
+	unsigned long start;
+	unsigned long length;
+	struct resource *mem_res;
+	unsigned long io_base;	/* Start Io address space for this device */
+	unsigned long io_len;	/* IO address space length for this device */
+
+	unsigned long chip_id;	/* Chip Id */
+
+	char name[80];		/* device name */
+	int active;		/* device status */
+
+	/* DMA resources should come here */
+	unsigned long dma;
+	u8 *baseaddress;	/*base address for i/o ops */
+	u8 *dmabase;
+	isp1763_id *id;
+} isp1763_dev_t;
+
+
+typedef struct isp1763_driver {
+	char *name;
+	unsigned long index;	/* HC or DC or OTG */
+	isp1763_id *id;		/*device ids */
+	int (*probe) (struct isp1763_dev * dev, isp1763_id * id);	/* New device inserted */
+	void (*remove) (struct isp1763_dev * dev);	/* Device removed (NULL if not a hot-plug capable driver) */
+	
+	void (*suspend) (struct isp1763_dev * dev);	/* Device suspended */
+	void (*resume) (struct isp1763_dev * dev);	/* Device woken up */
+	void (*remotewakeup) (struct isp1763_dev *dev);  /* Remote Wakeup */
+	void (*powerup) (struct isp1763_dev *dev);  /* Device poweup mode */
+	void (*powerdown)	(struct isp1763_dev *dev); /* Device power down mode */
+} isp_1763_driver_t;
+
+struct usb_device *phci_register_otg_device(struct isp1763_dev *dev);
+
+/*otg exported function from host*/
+int phci_suspend_otg_port(struct isp1763_dev *dev, u32 command);
+int phci_enumerate_otg_port(struct isp1763_dev *dev, u32 command);
+
+extern int isp1763_register_driver(struct isp1763_driver *drv);
+extern void isp1763_unregister_driver(struct isp1763_driver *drv);
+extern int isp1763_request_irq(void (*handler)(struct isp1763_dev * dev, void *isr_data),
+		      struct isp1763_dev *dev, void *isr_data);
+extern void isp1763_free_irq(struct isp1763_dev *dev, void *isr_data);
+
+extern u32 isp1763_reg_read32(isp1763_dev_t * dev, u16 reg, u32 data);
+extern u16 isp1763_reg_read16(isp1763_dev_t * dev, u16 reg, u16 data);
+extern u8 isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data);
+extern void isp1763_reg_write32(isp1763_dev_t * dev, u16 reg, u32 data);
+extern void isp1763_reg_write16(isp1763_dev_t * dev, u16 reg, u16 data);
+extern void isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data);
+extern int isp1763_mem_read(isp1763_dev_t * dev, u32 start_add,
+		     u32 end_add, u32 * buffer, u32 length, u16 dir);
+extern int isp1763_mem_write(isp1763_dev_t * dev, u32 start_add,
+		      u32 end_add, u32 * buffer, u32 length, u16 dir);
+#endif /* __HAL_INTF_H__ */
diff --git a/drivers/usb/host/pehci/hal/hal_msm.c b/drivers/usb/host/pehci/hal/hal_msm.c
new file mode 100644
index 0000000..35c0203
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/hal_msm.c
@@ -0,0 +1,748 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux HCD Controller driver : hal
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is the main hardware abstraction layer file. Hardware initialization, interupt
+* processing and read/write routines are handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <mach/board.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+/*--------------------------------------------------------------*
+ *               linux system include files
+ *--------------------------------------------------------------*/
+#include "hal_msm.h"
+#include "../hal/hal_intf.h"
+#include "../hal/isp1763.h"
+
+
+/*--------------------------------------------------------------*
+ *               Local variable Definitions
+ *--------------------------------------------------------------*/
+struct isp1763_dev isp1763_loc_dev[ISP1763_LAST_DEV];
+
+
+/*--------------------------------------------------------------*
+ *               Local # Definitions
+ *--------------------------------------------------------------*/
+#define         PCI_ACCESS_RETRY_COUNT  20
+#define         ISP1763_DRIVER_NAME     "isp1763_usb"
+
+/*--------------------------------------------------------------*
+ *               Local Function
+ *--------------------------------------------------------------*/
+
+static int __devexit isp1763_remove(struct platform_device *pdev);
+static int __devinit isp1763_probe(struct platform_device *pdev);
+
+
+/*--------------------------------------------------------------*
+ *               Platform Driver Interface Functions
+ *--------------------------------------------------------------*/
+
+static struct platform_driver isp1763_usb_driver = {
+	.remove = __exit_p(isp1763_remove),
+	.driver = {
+		.name = ISP1763_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+
+/*--------------------------------------------------------------*
+ *               ISP1763 Read write routine
+ *--------------------------------------------------------------*/
+/*
+ * EBI2 on 8660 ignores the first bit and shifts the address by
+ * one bit to the right.
+ * Hence, shift left all the register addresses before accessing
+ * them over EBI2.
+ * This logic applies only for the register read/writes, for
+ * read/write from ISP memory this conversion is not needed
+ * as the ISP obtains the memory address from 'memory' register
+ */
+
+/* Write a 32 bit Register of isp1763 */
+void
+isp1763_reg_write32(struct isp1763_dev *dev, u16 reg, u32 data)
+{
+	/* Write the 32bit to the register address given to us */
+
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	writew((u16) data, dev->baseaddress + ((reg)));
+	writew((u16) (data >> 16), dev->baseaddress + (((reg + 4))));
+#else
+	writeb((u8) data, dev->baseaddress + (reg));
+	writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
+	writeb((u8) (data >> 16), dev->baseaddress + ((reg + 2)));
+	writeb((u8) (data >> 24), dev->baseaddress + ((reg + 3)));
+#endif
+
+}
+EXPORT_SYMBOL(isp1763_reg_write32);
+
+
+/* Read a 32 bit Register of isp1763 */
+u32
+isp1763_reg_read32(struct isp1763_dev *dev, u16 reg, u32 data)
+{
+
+#ifdef DATABUS_WIDTH_16
+	u16 wvalue1, wvalue2;
+#else
+	u8 bval1, bval2, bval3, bval4;
+#endif
+	data = 0;
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	wvalue1 = readw(dev->baseaddress + ((reg)));
+	wvalue2 = readw(dev->baseaddress + (((reg + 4))));
+	data |= wvalue2;
+	data <<= 16;
+	data |= wvalue1;
+#else
+
+	bval1 = readb(dev->baseaddress + (reg));
+	bval2 = readb(dev->baseaddress + (reg + 1));
+	bval3 = readb(dev->baseaddress + (reg + 2));
+	bval4 = readb(dev->baseaddress + (reg + 3));
+	data = 0;
+	data |= bval4;
+	data <<= 8;
+	data |= bval3;
+	data <<= 8;
+	data |= bval2;
+	data <<= 8;
+	data |= bval1;
+
+#endif
+
+	return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read32);
+
+
+/* Read a 16 bit Register of isp1763 */
+u16
+isp1763_reg_read16(struct isp1763_dev * dev, u16 reg, u16 data)
+{
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	data = readw(dev->baseaddress + ((reg)));
+#else
+	u8 bval1, bval2;
+	bval1 = readb(dev->baseaddress + (reg));
+	if (reg == HC_DATA_REG){
+		bval2 = readb(dev->baseaddress + (reg));
+	} else {
+		bval2 = readb(dev->baseaddress + ((reg + 1)));
+	}
+	data = 0;
+	data |= bval2;
+	data <<= 8;
+	data |= bval1;
+
+#endif
+	return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read16);
+
+/* Write a 16 bit Register of isp1763 */
+void
+isp1763_reg_write16(struct isp1763_dev *dev, u16 reg, u16 data)
+{
+	reg <<= 1;
+#ifdef DATABUS_WIDTH_16
+	writew(data, dev->baseaddress + ((reg)));
+#else
+	writeb((u8) data, dev->baseaddress + (reg));
+	if (reg == HC_DATA_REG){
+		writeb((u8) (data >> 8), dev->baseaddress + (reg));
+	}else{
+		writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
+	}
+
+#endif
+}
+EXPORT_SYMBOL(isp1763_reg_write16);
+
+/* Read a 8 bit Register of isp1763 */
+u8
+isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data)
+{
+	reg <<= 1;
+	data = readb((dev->baseaddress + (reg)));
+	return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read8);
+
+/* Write a 8 bit Register of isp1763 */
+void
+isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data)
+{
+	reg <<= 1;
+	writeb(data, (dev->baseaddress + (reg)));
+}
+EXPORT_SYMBOL(isp1763_reg_write8);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_mem_read
+ *
+ * Memory read using PIO method.
+ *
+ *  Input: struct isp1763_driver *drv  -->  Driver structure.
+ *                      u32 start_add     --> Starting address of memory
+ *              u32 end_add     ---> End address
+ *
+ *              u32 * buffer      --> Buffer pointer.
+ *              u32 length       ---> Length
+ *              u16 dir          ---> Direction ( Inc or Dec)
+ *
+ *  Output     int Length  ----> Number of bytes read
+ *
+ *  Called by: system function
+ *
+ *
+ *--------------------------------------------------------------*/
+/* Memory read function PIO */
+
+int
+isp1763_mem_read(struct isp1763_dev *dev, u32 start_add,
+	u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+	u8 *one = (u8 *) buffer;
+	u16 *two = (u16 *) buffer;
+	u32 a = (u32) length;
+	u32 w;
+	u32 w2;
+
+	if (buffer == 0) {
+		printk("Buffer address zero\n");
+		return 0;
+	}
+
+
+	isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+	/* This delay requirement comes from the ISP1763A programming guide */
+	ndelay(100);
+last:
+	w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+	w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
+	w2 <<= 16;
+	w = w | w2;
+	if (a == 1) {
+		*one = (u8) w;
+		return 0;
+	}
+	if (a == 2) {
+		*two = (u16) w;
+		return 0;
+	}
+
+	if (a == 3) {
+		*two = (u16) w;
+		two += 1;
+		w >>= 16;
+		*two = (u8) (w);
+		return 0;
+
+	}
+	while (a > 0) {
+		*buffer = w;
+		a -= 4;
+		if (a <= 0) {
+			break;
+		}
+		if (a < 4) {
+			buffer += 1;
+			one = (u8 *) buffer;
+			two = (u16 *) buffer;
+			goto last;
+		}
+		buffer += 1;
+		w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+		w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
+		w2 <<= 16;
+		w = w | w2;
+	}
+	return ((a < 0) || (a == 0)) ? 0 : (-1);
+
+}
+EXPORT_SYMBOL(isp1763_mem_read);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_mem_write
+ *
+ * Memory write using PIO method.
+ *
+ *  Input: struct isp1763_driver *drv  -->  Driver structure.
+ *                      u32 start_add     --> Starting address of memory
+ *              u32 end_add     ---> End address
+ *
+ *              u32 * buffer      --> Buffer pointer.
+ *              u32 length       ---> Length
+ *              u16 dir          ---> Direction ( Inc or Dec)
+ *
+ *  Output     int Length  ----> Number of bytes read
+ *
+ *  Called by: system function
+ *
+ *
+ *--------------------------------------------------------------*/
+
+/* Memory read function IO */
+
+int
+isp1763_mem_write(struct isp1763_dev *dev,
+	u32 start_add, u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+	int a = length;
+	u8 one = (u8) (*buffer);
+	u16 two = (u16) (*buffer);
+
+
+	isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+	/* This delay requirement comes from the ISP1763A programming guide */
+	ndelay(100);
+
+	if (a == 1) {
+		isp1763_reg_write16(dev, HC_DATA_REG, one);
+		return 0;
+	}
+	if (a == 2) {
+		isp1763_reg_write16(dev, HC_DATA_REG, two);
+		return 0;
+	}
+
+	while (a > 0) {
+		isp1763_reg_write16(dev, HC_DATA_REG, (u16) (*buffer));
+		if (a >= 3)
+			isp1763_reg_write16(dev, HC_DATA_REG,
+					    (u16) ((*buffer) >> 16));
+		start_add += 4;
+		a -= 4;
+		if (a <= 0)
+			break;
+		buffer += 1;
+
+	}
+
+	return ((a < 0) || (a == 0)) ? 0 : (-1);
+
+}
+EXPORT_SYMBOL(isp1763_mem_write);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_register_driver
+ *
+ * This function is used by top driver (OTG, HCD, DCD) to register
+ * their communication functions (probe, remove, suspend, resume) using
+ * the drv data structure.
+ * This function will call the probe function of the driver if the ISP1763
+ * corresponding to the driver is enabled
+ *
+ *  Input: struct isp1763_driver *drv  --> Driver structure.
+ *  Output result
+ *         0= complete
+ *         1= error.
+ *
+ *  Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+int
+isp1763_register_driver(struct isp1763_driver *drv)
+{
+	struct isp1763_dev *dev;
+	int result = -EINVAL;
+
+	hal_entry("%s: Entered\n", __FUNCTION__);
+	info("isp1763_register_driver(drv=%p)\n", drv);
+
+	if (!drv) {
+		return -EINVAL;
+	}
+
+	dev = &isp1763_loc_dev[drv->index];
+	if (!dev->baseaddress)
+		return -EINVAL;
+
+	dev->active = 1;	/* set the driver as active*/
+
+	if (drv->probe) {
+		result = drv->probe(dev, drv->id);
+	} else {
+		printk("%s no probe function for indes %d \n", __FUNCTION__,
+			(int)drv->index);
+	}
+
+	if (result >= 0) {
+		pr_debug(KERN_INFO __FILE__ ": Registered Driver %s\n",
+			drv->name);
+		dev->driver = drv;
+	}
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return result;
+}				/* End of isp1763_register_driver */
+EXPORT_SYMBOL(isp1763_register_driver);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module dtatils: isp1763_unregister_driver
+ *
+ * This function is used by top driver (OTG, HCD, DCD) to de-register
+ * their communication functions (probe, remove, suspend, resume) using
+ * the drv data structure.
+ * This function will check whether the driver is registered or not and
+ * call the remove function of the driver if registered
+ *
+ *  Input: struct isp1763_driver *drv  --> Driver structure.
+ *  Output result
+ *         0= complete
+ *         1= error.
+ *
+ *  Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+void
+isp1763_unregister_driver(struct isp1763_driver *drv)
+{
+	struct isp1763_dev *dev;
+	hal_entry("%s: Entered\n", __FUNCTION__);
+
+	info("isp1763_unregister_driver(drv=%p)\n", drv);
+	dev = &isp1763_loc_dev[drv->index];
+	if (dev->driver == drv) {
+		/* driver registered is same as the requestig driver */
+		drv->remove(dev);
+		dev->driver = NULL;
+		info(": De-registered Driver %s\n", drv->name);
+		return;
+	}
+	hal_entry("%s: Exit\n", __FUNCTION__);
+}				/* End of isp1763_unregister_driver */
+EXPORT_SYMBOL(isp1763_unregister_driver);
+
+
+/*--------------------------------------------------------------*
+ *               ISP1763 Platform driver interface routine.
+ *--------------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------*
+ *
+ *  Module dtatils: isp1763_module_init
+ *
+ *  This  is the module initialization function. It registers to
+ *  driver for a isp1763 platform device. And also resets the
+ *  internal data structures.
+ *
+ *  Input: void
+ *  Output result
+ *         0= complete
+ *         1= error.
+ *
+ *  Called by: system function module_init
+ *
+ *
+ *
+ -------------------------------------------------------------------*/
+static int __init
+isp1763_module_init(void)
+{
+	int result = 0;
+	hal_entry("%s: Entered\n", __FUNCTION__);
+	pr_debug(KERN_NOTICE "+isp1763_module_init\n");
+	memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
+
+	result = platform_driver_probe(&isp1763_usb_driver, isp1763_probe);
+
+	pr_debug(KERN_NOTICE "-isp1763_module_init\n");
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return result;
+}
+
+/*--------------------------------------------------------------*
+ *
+ *  Module dtatils: isp1763_module_cleanup
+ *
+ * This  is the module cleanup function. It de-registers the
+ * Platform driver and resets the internal data structures.
+ *
+ *  Input: void
+ *  Output void
+ *
+ *  Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------*/
+
+static void __exit
+isp1763_module_cleanup(void)
+{
+	pr_debug("Hal Module Cleanup\n");
+	platform_driver_unregister(&isp1763_usb_driver);
+
+	memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
+}
+
+void dummy_mem_read(struct isp1763_dev *dev)
+{
+	u32 w = 0;
+	isp1763_reg_write16(dev, HC_MEM_READ_REG, 0x0400);
+	w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+
+	pr_debug("dummy_read DONE: %x\n", w);
+	msleep(10);
+}
+/*--------------------------------------------------------------*
+ *
+ *  Module dtatils: isp1763_probe
+ *
+ * probe function of ISP1763
+ * This function is called from module_init if the corresponding platform
+ * device is present. This function initializes the information
+ * for the Host Controller with the assigned resources and tests the register
+ * access to the controller and do a software reset and makes it ready
+ * for the driver to play with. It also calls setup_gpio passed from pdata
+ * to setup GPIOs (e.g. used for IRQ and RST lines).
+ *
+ *  Input:
+ *              struct platform_device *dev   ----> Platform Device structure
+ *  Output void
+ *
+ *  Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------**/
+
+static int __devinit
+isp1763_probe(struct platform_device *pdev)
+{
+	u32 reg_data = 0;
+	struct isp1763_dev *loc_dev;
+	int status = 1;
+	u32 hwmodectrl = 0;
+	u16 us_reset_hc = 0;
+	u32 chipid = 0;
+	struct isp1763_platform_data *pdata = pdev->dev.platform_data;
+
+	hal_entry("%s: Entered\n", __FUNCTION__);
+
+	hal_init(("isp1763_probe(dev=%p)\n", dev));
+
+	loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
+	loc_dev->dev = pdev;
+
+	/* Get the Host Controller IO and INT resources */
+	loc_dev->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!loc_dev->mem_res) {
+		pr_err("%s: failed to get platform resource mem\n", __func__);
+		return -ENODEV;
+	}
+
+	loc_dev->baseaddress = ioremap_nocache(loc_dev->mem_res->start,
+					resource_size(loc_dev->mem_res));
+	if (!loc_dev->baseaddress) {
+		pr_err("%s: ioremap failed\n", __func__);
+		status = -ENOMEM;
+		goto put_mem_res;
+	}
+	pr_info("%s: ioremap done at: %x\n", __func__,
+					(int)loc_dev->baseaddress);
+	loc_dev->irq = platform_get_irq(pdev, 0);
+	if (!loc_dev->irq) {
+		pr_err("%s: platform_get_irq failed\n", __func__);
+		status = -ENODEV;
+		goto free_regs;
+	}
+
+	loc_dev->index = ISP1763_HC;	/*zero */
+	loc_dev->length = resource_size(loc_dev->mem_res);
+
+	hal_init(("isp1763 HC MEM Base= %p irq = %d\n",
+		loc_dev->baseaddress, loc_dev->irq));
+
+	/* Setup GPIOs and isssue RESET_N to Controller */
+	if (pdata->setup_gpio)
+		if (pdata->setup_gpio(1))
+			pr_err("%s: Failed to setup GPIOs for isp1763\n",
+								 __func__);
+	if (pdata->reset_gpio) {
+		gpio_set_value(pdata->reset_gpio, 0);
+		msleep(10);
+		gpio_set_value(pdata->reset_gpio, 1);
+	} else {
+		pr_err("%s: Failed to issue RESET_N to isp1763\n", __func__);
+	}
+
+	dummy_mem_read(loc_dev);
+
+	chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+	pr_info("START: chip id:%x\n", chipid);
+
+	/*reset the host controller  */
+	pr_debug("RESETTING\n");
+	us_reset_hc |= 0x1;
+	isp1763_reg_write16(loc_dev, 0xB8, us_reset_hc);
+	msleep(20);
+	us_reset_hc = 0;
+	us_reset_hc |= 0x2;
+	isp1763_reg_write16(loc_dev, 0xB8, us_reset_hc);
+
+	chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+	pr_info("after HC reset, chipid:%x\n", chipid);
+
+	msleep(20);
+	hwmodectrl = isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+	pr_debug("Mode Ctrl Value b4 setting buswidth: %x\n", hwmodectrl);
+#ifdef DATABUS_WIDTH_16
+	hwmodectrl &= 0xFFEF;	/*enable the 16 bit bus */
+#else
+	pr_debug("Setting 8-BIT mode\n");
+	hwmodectrl |= 0x0010;	/*enable the 8 bit bus */
+#endif
+	isp1763_reg_write16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+	pr_debug("writing 0x%x to hw mode reg\n", hwmodectrl);
+
+	hwmodectrl = isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+	msleep(100);
+
+	pr_debug("Mode Ctrl Value after setting buswidth: %x\n", hwmodectrl);
+
+
+	chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+	pr_debug("after setting HW MODE to 8bit, chipid:%x\n", chipid);
+
+
+
+	hal_init(("isp1763 DC MEM Base= %lx irq = %d\n",
+		loc_dev->io_base, loc_dev->irq));
+	reg_data = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, reg_data);
+	pr_debug("Scratch register is 0x%x\n", reg_data);
+	reg_data = 0xABCD;
+	isp1763_reg_write16(loc_dev, HC_SCRATCH_REG, reg_data);
+	reg_data = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, reg_data);
+	pr_debug("After write, Scratch register is 0x%x\n", reg_data);
+
+	if (reg_data != 0xABCD) {
+		pr_err("%s: Scratch register write mismatch!!\n", __func__);
+		status = -ENODEV;
+		goto free_gpios;
+	}
+
+	memcpy(loc_dev->name, ISP1763_DRIVER_NAME, sizeof(ISP1763_DRIVER_NAME));
+	loc_dev->name[sizeof(ISP1763_DRIVER_NAME)] = 0;
+
+	pr_debug(KERN_NOTICE "-isp1763_pci_probe\n");
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return 0;
+
+free_gpios:
+	if (pdata->setup_gpio)
+		pdata->setup_gpio(0);
+free_regs:
+	iounmap(loc_dev->baseaddress);
+put_mem_res:
+	loc_dev->baseaddress = NULL;
+	hal_entry("%s: Exit\n", __FUNCTION__);
+	return status;
+}				/* End of isp1763_probe */
+
+
+/*--------------------------------------------------------------*
+ *
+ *  Module details: isp1763_remove
+ *
+ * cleanup function of ISP1763
+ * This functions de-initializes the local variables, frees GPIOs
+ * and releases memory resource.
+ *
+ *  Input:
+ *              struct platform_device *dev    ----> Platform Device structure
+ *
+ *  Output void
+ *
+ *  Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------*/
+static int __devexit
+isp1763_remove(struct platform_device *pdev)
+{
+	struct isp1763_dev *loc_dev;
+	struct isp1763_platform_data *pdata = pdev->dev.platform_data;
+
+	hal_init(("isp1763_pci_remove(dev=%p)\n", dev));
+
+	loc_dev = &isp1763_loc_dev[ISP1763_HC];
+	iounmap(loc_dev->baseaddress);
+	loc_dev->baseaddress = NULL;
+	if (pdata->setup_gpio)
+		return pdata->setup_gpio(0);
+
+	return 0;
+}				/* End of isp1763_remove */
+
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_init(isp1763_module_init);
+module_exit(isp1763_module_cleanup);
diff --git a/drivers/usb/host/pehci/hal/hal_msm.h b/drivers/usb/host/pehci/hal/hal_msm.h
new file mode 100644
index 0000000..a7a65b7
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/hal_msm.h
@@ -0,0 +1,85 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : hal
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a hardware abstraction layer header file.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef	HAL_X86_H
+#define	HAL_X86_H
+
+#define	DRIVER_AUTHOR	"ST-ERICSSON	  "
+#define	DRIVER_DESC	"ISP1763 bus driver"
+
+/* Driver tuning, per ST-ERICSSON requirements:	*/
+
+#define	MEM_TO_CHECK		4096	/*bytes, must be multiple of 2 */
+
+/* BIT defines */
+#define	BIT0	(1 << 0)
+#define	BIT1	(1 << 1)
+#define	BIT2	(1 << 2)
+#define	BIT3	(1 << 3)
+#define	BIT4	(1 << 4)
+#define	BIT5	(1 << 5)
+#define	BIT6	(1 << 6)
+#define	BIT7	(1 << 7)
+#define	BIT8	(1 << 8)
+#define	BIT9	(1 << 9)
+#define	BIT10	(1 << 10)
+#define	BIT11	(1 << 11)
+#define	BIT12	(1 << 12)
+#define	BIT13	(1 << 13)
+#define	BIT14	(1 << 14)
+#define	BIT15	(1 << 15)
+#define	BIT16	(1 << 16)
+#define	BIT17	(1 << 17)
+#define	BIT18	(1 << 18)
+#define	BIT19	(1 << 19)
+#define	BIT20	(1 << 20)
+#define	BIT21	(1 << 21)
+#define	BIT22	(1 << 22)
+#define	BIT23	(1 << 23)
+#define	BIT24	(1 << 24)
+#define	BIT25	(1 << 26)
+#define	BIT27	(1 << 27)
+#define	BIT28	(1 << 28)
+#define	BIT29	(1 << 29)
+#define	BIT30	(1 << 30)
+#define	BIT31	(1 << 31)
+
+/* Definitions Related to Chip Address and CPU Physical	Address
+ * cpu_phy_add:	CPU Physical Address , it uses 32 bit data per address
+ * chip_add   :	Chip Address, it uses double word(64) bit data per address
+ */
+#define	chip_add(cpu_phy_add)		(((cpu_phy_add)	- 0x400) / 8)
+#define	cpu_phy_add(chip_add)		((8 * (chip_add)) + 0x400)
+
+/* for getting end add,	and start add, provided	we have	one address with us */
+/* IMPORTANT length  hex(base16) and dec(base10) works fine*/
+#define	end_add(start_add, length)	(start_add + (length - 4))
+#define	start_add(end_add, length)	(end_add - (length - 4))
+
+/* Device Registers*/
+#define	DEV_UNLOCK_REGISTER		0x7C
+#define	DEV_INTERRUPT_REGISTER		0x18
+#define	INT_ENABLE_REGISTER		0x14
+
+#endif /*_HAL_X86_H_ */
diff --git a/drivers/usb/host/pehci/hal/isp1763.h b/drivers/usb/host/pehci/hal/isp1763.h
new file mode 100644
index 0000000..7355185
--- /dev/null
+++ b/drivers/usb/host/pehci/hal/isp1763.h
@@ -0,0 +1,227 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : hal
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a hardware abstraction layer header file.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef	ISP1763_H
+#define	ISP1763_H
+
+
+
+/* For debugging option: ------------------- */
+#define PTD_DUMP_SCHEDULE
+#undef  PTD_DUMP_SCHEDULE
+
+#define PTD_DUMP_COMPLETE
+#undef  PTD_DUMP_COMPLETE
+/* ------------------------------------*/
+#define CONFIG_ISO_SUPPORT 
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define	ISO_DBG_ENTRY 1
+#define	ISO_DBG_EXIT  1
+#define	ISO_DBG_ADDR 1
+#define	ISO_DBG_DATA 1
+#define	ISO_DBG_ERR  1
+#define	ISO_DBG_INFO 1
+
+#if 0				/* Set to 1 to enable isochronous debugging */
+#define	iso_dbg(category, format, arg...) \
+do \
+{ \
+	if(category) \
+	{ \
+		printk(format, ## arg);	\
+	} \
+} while(0)
+#else
+#define	iso_dbg(category, format, arg...) while(0)
+#endif
+
+#endif /* CONFIG_ISO_SUPPORT */
+
+/*Debug	For Entry/Exit of the functions	*/
+//#define HCD_DEBUG_LEVEL1 
+#ifdef HCD_DEBUG_LEVEL1
+#define	pehci_entry(format, args... ) printk(format, ##args)
+#else
+#define	pehci_entry(format, args...) do	{ } while(0)
+#endif
+
+/*Debug	for Port Info and Errors */
+//#define HCD_DEBUG_LEVEL2 
+#ifdef HCD_DEBUG_LEVEL2
+#define	pehci_print(format, args... ) printk(format, ##args)
+#else
+#define	pehci_print(format, args...) do	{ } while(0)
+#endif
+
+/*Debug	For the	Port changes and Enumeration */
+//#define HCD_DEBUG_LEVEL3 
+#ifdef HCD_DEBUG_LEVEL3
+#define	pehci_info(format,arg...) printk(format, ##arg)
+#else
+#define	pehci_info(format,arg...) do {}	while (0)
+#endif
+
+/*Debug	For Transfer flow  */
+// #define HCD_DEBUG_LEVEL4 
+#ifdef HCD_DEBUG_LEVEL4
+#define	pehci_check(format,args...) printk(format, ##args)
+#else
+#define	pehci_check(format,args...)
+#endif
+/*******************END	HOST CONTROLLER**********************************/
+
+
+
+/*******************START DEVICE CONTROLLER******************************/
+
+/* For MTP support */
+#undef MTP_ENABLE		/* Enable to add MTP support; But requires MTP class driver to be present to work */
+/*For CHAPTER8 TEST */
+#undef	CHAPTER8_TEST		/* Enable to Pass Chapter 8 Test */
+
+/* Debug Entery/Exit of	Function as well as some other Info */
+//#define DEV_DEBUG_LEVEL2
+#ifdef DEV_DEBUG_LEVEL2
+#define	dev_print(format,arg...) printk(format,	##arg)
+#else
+#define	dev_print(format,arg...) do {} while (0)
+#endif
+
+/*Debug	for Interrupt ,	Registers , device Enable/Disable and some other info */
+//#define DEV_DEBUG_LEVEL3
+#undef dev_info
+#ifdef DEV_DEBUG_LEVEL3
+#define	dev_info(format,arg...)	printk(format, ##arg)
+#else
+#define	dev_info(format,arg...)	do {} while (0)
+#endif
+
+/*Debug	for Tranffer flow , Enumeration	and Packet info	*/
+//#define DEV_DEBUG_LEVEL4
+#ifdef DEV_DEBUG_LEVEL4
+#define	dev_check(format,args...) printk(format, ##args)
+#else
+#define	dev_check(format,args...) do{}while(0)
+#endif
+/*******************END	DEVICE CONTROLLER********************************/
+
+
+/*******************START MSCD*******************************************/
+/*Debug	Entery/Exit of Function	as well	as some	other Information*/
+//#define MSCD_DEBUG_LEVEL2
+#ifdef MSCD_DEBUG_LEVEL2
+#define	mscd_print(format,arg...) printk(format, ##arg)
+#else
+#define	mscd_print(format,arg...) do {}	while (0)
+#endif
+
+/*Debug	for Info */
+//#define MSCD_DEBUG_LEVEL3
+#ifdef MSCD_DEBUG_LEVEL3
+#define	mscd_info(format,arg...) printk(format,	##arg)
+#else
+#define	mscd_info(format,arg...) do {} while (0)
+#endif
+/*******************END	MSCD*********************************************/
+
+
+/*******************START OTG CONTROLLER*********************************/
+/*#define	OTG */			/*undef	for Device only	and Host only */
+#define	ALL_FSM_FLAGS
+/*Debug	for Entry/Exit and Info	*/
+/* #define OTG_DEBUG_LEVEL1 */
+#ifdef OTG_DEBUG_LEVEL1
+#define	otg_entry(format, args... ) printk(format, ##args)
+#else
+#define	otg_entry(format, args...) do {	} while(0)
+#endif
+
+/*Debug	for State Machine Flow */
+/* #define OTG_DEBUG_LEVEL2 */
+#ifdef OTG_DEBUG_LEVEL2
+#define	otg_print(format,arg...) printk(format,	##arg)
+#else
+#define	otg_print(format,arg...) do {} while (0)
+#endif
+/*Debug	for Info */
+/* #define OTG_DEBUG_LEVEL3 */
+#ifdef OTG_DEBUG_LEVEL3
+#define	otg_info(format,arg...)	printk(format, ##arg)
+#else
+#define	otg_info(format,arg...)	do {} while (0)
+#endif
+
+/* #define OTG_DEBUG_LEVEL4 */
+#ifdef OTG_DEBUG_LEVEL4
+#define	otg_printB(format,arg...) printk(format, ##arg)
+#else
+#define	otg_printB(format,arg...) do {}	while (0)
+#endif
+/*******************END	OTG CONTROLLER***********************************/
+
+
+
+/*******************START FOR HAL ***************************************/
+#define info pr_debug
+#define warn pr_warn
+/*Debug For Entry and Exit of the functions */
+#undef HAL_DEBUG_LEVEL1
+#ifdef HAL_DEBUG_LEVEL1
+#define	hal_entry(format, args... ) printk(format, ##args)
+#else
+#define	hal_entry(format, args...) do {	} while(0)
+#endif
+
+/*Debug	For Interrupt information */
+#undef HAL_DEBUG_LEVEL2
+#ifdef HAL_DEBUG_LEVEL2
+#define	hal_int(format,	args...	) printk(format, ##args)
+#else
+#define	hal_int(format,	args...) do { }	while(0)
+#endif
+
+/*Debug	For HAL	Initialisation and Mem Initialisation */
+#undef HAL_DEBUG_LEVEL3
+#ifdef HAL_DEBUG_LEVEL3
+#define	hal_init(format, args... ) printk(format, ##args)
+#else
+#define	hal_init(format, args...) do { } while(0)
+#endif
+/*******************END	FOR HAL*******************************************/
+
+
+
+/*******************START FOR ALL CONTROLLERS*****************************/
+/*#define	CONFIG_USB_OTG */	/*undef	for Device only	and Host only */
+/*#define	ISP1763_DEVICE */
+
+#ifdef CONFIG_USB_DEBUG
+#define	DEBUG
+#else
+#undef DEBUG
+#endif
+/*******************END	FOR ALL	CONTROLLERS*******************************/
+#endif
diff --git a/drivers/usb/host/pehci/host/Makefile b/drivers/usb/host/pehci/host/Makefile
new file mode 100644
index 0000000..0c8552e
--- /dev/null
+++ b/drivers/usb/host/pehci/host/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the pehci driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_PEHCI_HCD) += pehci.o
+
diff --git a/drivers/usb/host/pehci/host/itdptd.c b/drivers/usb/host/pehci/host/itdptd.c
new file mode 100644
index 0000000..6699c3a
--- /dev/null
+++ b/drivers/usb/host/pehci/host/itdptd.c
@@ -0,0 +1,2156 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file. Isochronous event processing is handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+#ifdef CONFIG_ISO_SUPPORT
+void phcd_clean_periodic_ep(void);
+#endif
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define MAX_URBS		8
+#define MAX_EPS			2/*maximum 2 endpoints supported in ISO transfers.*/
+/*number of microframe per frame which is scheduled, for high speed device
+* actually , NUMMICROFRAME should be 8 , but the micro frame #7 is fail , so
+* there's just 4 microframe is used (#0 -> #4)
+* Writer : LyNguyen - 25Nov09
+*/
+#define NUMMICROFRAME		8
+struct urb *gstUrb_pending[MAX_URBS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+struct usb_host_endpoint *periodic_ep[MAX_EPS];
+
+int giUrbCount = 0;		/* count the pending urb*/
+int giUrbIndex = 0;		/*the index of urb need to be scheduled next*/
+/*
+ * phcd_iso_sitd_to_ptd - convert an SITD into a PTD
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * void  * ptd
+ *  - Points to the ISO ptd structure that needs to be initialized
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  -Initializing the PTD that will be used for the ISO transfer
+ */
+void *
+phcd_iso_sitd_to_ptd(phci_hcd * hcd,
+	struct ehci_sitd *sitd, struct urb *urb, void *ptd)
+{
+	struct _isp1763_isoptd *iso_ptd;
+	struct isp1763_mem_addr *mem_addr;
+
+	unsigned long max_packet, mult, length, td_info1, td_info3;
+	unsigned long token, port_num, hub_num, data_addr;
+	unsigned long frame_number;
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_sitd_to_ptd entry\n");
+
+	/* Variable initialization */
+	iso_ptd = (struct _isp1763_isoptd *) ptd;
+	mem_addr = &sitd->mem_addr;
+
+	/*
+	 * For both ISO and INT endpoints descriptors, new bit fields we added to
+	 * specify whether or not the endpoint supports high bandwidth, and if so
+	 * the number of additional packets that the endpoint can support during a
+	 * single microframe.
+	 * Bits 12:11 specify whether the endpoint supports high-bandwidth transfers
+	 * Valid values:
+	 *             00 None (1 transaction/uFrame)
+	 *             01 1 additional transaction
+	 *             10 2 additional transactions
+	 *             11 reserved
+	 */
+	max_packet = usb_maxpacket(urb->dev, urb->pipe,usb_pipeout(urb->pipe));
+
+	/*
+	 * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+	 * zero (0).
+	 */
+	mult = 1 + ((max_packet >> 11) & 0x3);
+	max_packet &= 0x7ff;
+
+	/* This is the size of the request (bytes to write or bytes to read) */
+	length = sitd->length;
+
+	/*
+	 * Set V bit to indicate that there is payload to be sent or received. And
+	 * indicate that the current PTD is active.
+	 */
+	td_info1 = QHA_VALID;
+
+	/*
+	 * Set the number of bytes that can be transferred by this PTD. This indicates
+	 * the depth of the data field.
+	 */
+	td_info1 |= (length << 3);
+
+	/*
+	 * Set the maximum packet length which indicates the maximum number of bytes that
+	 * can be sent to or received from the endpoint in a single data packet.
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * According to the ISP1763 specs for sITDs, OUT token max packet should
+		 * not be more  than 188 bytes, while IN token max packet not more than
+		 * 192 bytes (ISP1763 Rev 3.01, Table 72, page 79
+		 */
+		if (usb_pipein(urb->pipe) && (max_packet > 192)) {
+			iso_dbg(ISO_DBG_INFO,
+				"IN Max packet over maximum\n");
+			max_packet = 192;
+		}
+
+		if ((!usb_pipein(urb->pipe)) && (max_packet > 188)) {
+			iso_dbg(ISO_DBG_INFO,
+				"OUT Max packet over maximum\n");
+			max_packet = 188;
+		}
+	}
+	td_info1 |= (max_packet << 18);
+
+	/*
+	 * Place the FIRST BIT of the endpoint number here.
+	 */
+	td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+
+	/*
+	 * Set the number of successive packets the HC can submit to the endpoint.
+	 */
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		td_info1 |= MULTI(mult);
+	}
+
+	/* Set the first DWORD */
+	iso_ptd->td_info1 = td_info1;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",
+		iso_ptd->td_info1);
+
+	/*
+	 * Since the first bit have already been added on the first DWORD of the PTD
+	 * we only need to add the last 3-bits of the endpoint number.
+	 */
+	token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+
+	/*
+	 * Get the device address and set it accordingly to its assigned bits of the 2nd
+	 * DWORD.
+	 */
+	token |= usb_pipedevice(urb->pipe) << 3;
+
+	/* See a split transaction is needed */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * If we are performing a SPLIT transaction indicate that it is so by setting
+		 * the S bit of the second DWORD.
+		 */
+		token |= 1 << 14;
+
+		port_num = urb->dev->ttport;
+		hub_num = urb->dev->tt->hub->devnum;
+
+		/* Set the the port number of the hub or embedded TT */
+		token |= port_num << 18;
+
+		/*
+		 * Set the hub address, this should be zero for the internal or
+		 * embedded hub
+		 */
+		token |= hub_num << 25;
+	}
+
+	/* if(urb->dev->speed != USB_SPEED_HIGH) */
+	/*
+	 * Determine if the direction of this pipe is IN, if so set the Token bit of
+	 * the second DWORD to indicate it as IN. Since it is initialized to zero and
+	 * zero indicates an OUT token, then we do not need anything to the Token bit
+	 * if it is an OUT token.
+	 */
+	if (usb_pipein(urb->pipe)) {
+		token |= (IN_PID << 10);
+	}
+
+	/* Set endpoint type to Isochronous */
+	token |= EPTYPE_ISO;
+
+	/* Set the second DWORD */
+	iso_ptd->td_info2 = token;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",
+		iso_ptd->td_info2);
+
+	/*
+	 * Get the physical address of the memory location that was allocated for this PTD
+	 * in the PAYLOAD region, using the formula indicated in sectin 7.2.2 of the ISP1763 specs
+	 * rev 3.01 page 17 to 18.
+	 */
+	data_addr = ((unsigned long) (mem_addr->phy_addr) & 0xffff) - 0x400;
+	data_addr >>= 3;
+
+	/*  Set it to its location in the third DWORD */
+	td_info3 =( 0xffff&data_addr) << 8;
+
+	/*
+	 * Set the frame number when this PTD will be sent for ISO OUT or IN
+	 * Bits 0 to 2 are don't care, only bits 3 to 7.
+	 */
+	frame_number = sitd->framenumber;
+	frame_number = sitd->start_frame;
+	td_info3 |= (0xff& ((frame_number) << 3));
+
+	/* Set the third DWORD */
+	iso_ptd->td_info3 = td_info3;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",
+		iso_ptd->td_info3);
+
+	/*
+	 * Set the A bit of the fourth DWORD to 1 to indicate that this PTD is active.
+	 * This have the same functionality with the V bit of DWORD0
+	 */
+	iso_ptd->td_info4 = QHA_ACTIVE;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",
+		iso_ptd->td_info4);
+
+	/* Set the fourth DWORD to specify which uSOFs the start split needs to be placed */
+	if (usb_pipein(urb->pipe)){
+		iso_ptd->td_info5 = (sitd->ssplit);
+	}else{
+		iso_ptd->td_info5 = (sitd->ssplit << 2);
+	}
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",
+		iso_ptd->td_info5);
+
+	/*
+	 * Set the fifth DWORD to specify which uSOFs the complete split needs to be sent.
+	 * This is VALID only for IN (since ISO transfers don't have handshake stages)
+	 */
+	iso_ptd->td_info6 = sitd->csplit;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",
+		iso_ptd->td_info6);
+
+	/*printk(" [phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",iso_ptd->td_info1);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",iso_ptd->td_info2);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",iso_ptd->td_info3);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",iso_ptd->td_info4);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",iso_ptd->td_info5);
+	printk(" [phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",iso_ptd->td_info6);*/
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_to_ptd exit\n");
+	return iso_ptd;
+}
+
+
+/*
+ * phcd_iso_itd_to_ptd - convert an ITD into a PTD
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_itd *itd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more ST-ERICSSON specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * void  * ptd
+ *  - Points to the ISO ptd structure that needs to be initialized
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  -Initializing the PTD that will be used for the ISO transfer
+ */
+void *
+phcd_iso_itd_to_ptd(phci_hcd * hcd,
+	struct ehci_itd *itd, struct urb *urb, void *ptd)
+{
+	struct _isp1763_isoptd *iso_ptd;
+	struct isp1763_mem_addr *mem_addr;
+
+	unsigned long max_packet, mult, length, td_info1, td_info3;
+	unsigned long token, port_num, hub_num, data_addr;
+	unsigned long frame_number;
+	int maxpacket;
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_to_ptd entry\n");
+
+	/* Variable initialization */
+	iso_ptd = (struct _isp1763_isoptd *) ptd;
+	mem_addr = &itd->mem_addr;
+
+	/*
+	 * For both ISO and INT endpoints descriptors, new bit fields we added to
+	 * specify whether or not the endpoint supports high bandwidth, and if so
+	 * the number of additional packets that the endpoint can support during a
+	 * single microframe.
+	 * Bits 12:11 specify whether the endpoint supports high-bandwidth transfers
+	 * Valid values:
+	 *             00 None (1 transaction/uFrame)
+	 *             01 1 additional transaction
+	 *             10 2 additional transactions
+	 *             11 reserved
+	 */
+	max_packet = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));	
+
+	/*
+	 * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+	 * zero (0).
+	 */
+	maxpacket &= 0x7ff;
+	mult = 1 + ((max_packet >> 11) & 0x3);
+
+
+	max_packet &= 0x7ff;
+
+	/* This is the size of the request (bytes to write or bytes to read) */
+	length = itd->length;
+
+	/*
+	 * Set V bit to indicate that there is payload to be sent or received. And
+	 * indicate that the current PTD is active.
+	 */
+	td_info1 = QHA_VALID;
+
+	/*
+	 * Set the number of bytes that can be transferred by this PTD. This indicates
+	 * the depth of the data field.
+	 */
+	td_info1 |= (length << 3);
+
+	/*
+	 * Set the maximum packet length which indicates the maximum number of bytes that
+	 * can be sent to or received from the endpoint in a single data packet.
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * According to the ISP1763 specs for sITDs, OUT token max packet should
+		 * not be more  than 188 bytes, while IN token max packet not more than
+		 * 192 bytes (ISP1763 Rev 3.01, Table 72, page 79
+		 */
+		if (usb_pipein(urb->pipe) && (max_packet > 192)) {
+			iso_dbg(ISO_DBG_INFO,
+				"[phcd_iso_itd_to_ptd]: IN Max packet over maximum\n");
+			max_packet = 192;
+		}
+
+		if ((!usb_pipein(urb->pipe)) && (max_packet > 188)) {
+			iso_dbg(ISO_DBG_INFO,
+				"[phcd_iso_itd_to_ptd]: OUT Max packet over maximum\n");
+			max_packet = 188;
+		}
+	} else {		/*HIGH SPEED */
+
+		if (max_packet > 1024){
+			max_packet = 1024;
+		}
+	}
+	td_info1 |= (max_packet << 18);
+
+	/*
+	 * Place the FIRST BIT of the endpoint number here.
+	 */
+	td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+
+	/*
+	 * Set the number of successive packets the HC can submit to the endpoint.
+	 */
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		td_info1 |= MULTI(mult);
+	}
+
+	/* Set the first DWORD */
+	iso_ptd->td_info1 = td_info1;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",
+		iso_ptd->td_info1);
+
+	/*
+	 * Since the first bit have already been added on the first DWORD of the PTD
+	 * we only need to add the last 3-bits of the endpoint number.
+	 */
+	token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+
+	/*
+	 * Get the device address and set it accordingly to its assigned bits of the 2nd
+	 * DWORD.
+	 */
+	token |= usb_pipedevice(urb->pipe) << 3;
+
+	/* See a split transaction is needed */
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		/*
+		 * If we are performing a SPLIT transaction indicate that it is so by setting
+		 * the S bit of the second DWORD.
+		 */
+		token |= 1 << 14;
+
+		port_num = urb->dev->ttport;
+		hub_num = urb->dev->tt->hub->devnum;
+
+		/* Set the the port number of the hub or embedded TT */
+		token |= port_num << 18;
+
+		/*
+		 * Set the hub address, this should be zero for the internal or
+		 * embedded hub
+		 */
+		token |= hub_num << 25;
+	}
+
+	/* if(urb->dev->speed != USB_SPEED_HIGH) */
+	/*
+	 * Determine if the direction of this pipe is IN, if so set the Token bit of
+	 * the second DWORD to indicate it as IN. Since it is initialized to zero and
+	 * zero indicates an OUT token, then we do not need anything to the Token bit
+	 * if it is an OUT token.
+	 */
+	if (usb_pipein(urb->pipe)){
+		token |= (IN_PID << 10);
+	}
+
+	/* Set endpoint type to Isochronous */
+	token |= EPTYPE_ISO;
+
+	/* Set the second DWORD */
+	iso_ptd->td_info2 = token;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",
+		iso_ptd->td_info2);
+
+	/*
+	 * Get the physical address of the memory location that was allocated for this PTD
+	 * in the PAYLOAD region, using the formula indicated in sectin 7.2.2 of the ISP1763 specs
+	 * rev 3.01 page 17 to 18.
+	 */
+	data_addr = ((unsigned long) (mem_addr->phy_addr) & 0xffff) - 0x400;
+	data_addr >>= 3;
+
+	/*  Set it to its location in the third DWORD */
+	td_info3 = (data_addr&0xffff) << 8;
+
+	/*
+	 * Set the frame number when this PTD will be sent for ISO OUT or IN
+	 * Bits 0 to 2 are don't care, only bits 3 to 7.
+	 */
+	frame_number = itd->framenumber;
+	td_info3 |= (0xff&(frame_number << 3));
+
+	/* Set the third DWORD */
+	iso_ptd->td_info3 = td_info3;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",
+		iso_ptd->td_info3);
+
+	/*
+	 * Set the A bit of the fourth DWORD to 1 to indicate that this PTD is active.
+	 * This have the same functionality with the V bit of DWORD0
+	 */
+	iso_ptd->td_info4 = QHA_ACTIVE;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",
+		iso_ptd->td_info4);
+
+	/* Set the fourth DWORD to specify which uSOFs the start split needs to be placed */
+	iso_ptd->td_info5 = itd->ssplit;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",
+		iso_ptd->td_info5);
+
+	/*
+	 * Set the fifth DWORD to specify which uSOFs the complete split needs to be sent.
+	 * This is VALID only for IN (since ISO transfers don't have handshake stages)
+	 */
+	iso_ptd->td_info6 = itd->csplit;
+	iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",
+		iso_ptd->td_info6);
+
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_to_ptd exit\n");
+	return iso_ptd;
+}				/* phcd_iso_itd_to_ptd */
+
+/*
+ * phcd_iso_scheduling_info - Initializing the start split and complete split.
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_qh *qhead
+ *  - Contains information about the endpoint.
+ * unsigned long max_pkt
+ *  - Maximum packet size that the endpoint in capable of handling
+ * unsigned long high_speed
+ *  - Indicates if the bus is a high speed bus
+ * unsigned long ep_in
+ *  - Inidcates if the endpoint is an IN endpoint
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Determining the number of start split needed during an OUT transaction or
+ *    the number of complete splits needed during an IN transaction.
+ */
+unsigned long
+phcd_iso_scheduling_info(phci_hcd * hcd,
+	struct ehci_qh *qhead,
+	unsigned long max_pkt,
+	unsigned long high_speed, unsigned long ep_in)
+{
+	unsigned long count, usof, temp;
+
+	/* Local variable initialization */
+	usof = 0x1;
+
+	if (high_speed) {
+		qhead->csplit = 0;
+
+		/* Always send high speed transfers in first uframes */
+		qhead->ssplit = 0x1;
+		return 0;
+	}
+
+	/* Determine how many 188 byte-transfers are needed to send all data */
+	count = max_pkt / 188;
+
+	/*
+	 * Check is the data is not a factor of 188, if it is not then we need
+	 * one more 188 transfer to move the last set of data less than 188.
+	 */
+	if (max_pkt % 188){
+		count += 1;
+	}
+
+	/*
+	 * Remember that usof was initialized to 0x1 so that means
+	 * that usof is always guranteed a value of 0x1 and then
+	 * depending on the maxp, other bits of usof will also be set.
+	 */
+	for (temp = 0; temp < count; temp++){
+		usof |= (0x1 << temp);
+	}
+
+	if (ep_in) {
+		/*
+		 * Send start split into first frame.
+		 */
+		qhead->ssplit = 0x1;
+
+		/*
+		 * Inidicate that we can send a complete split starting from
+		 * the third uFrame to how much complete split is needed to
+		 * retrieve all data.
+		 *
+		 * Of course, the first uFrame is reserved for the start split, the
+		 * second is reserved for the TT to send the request and get some
+		 * data.
+		 */
+		qhead->csplit = (usof << 2);
+	} else {
+		/*
+		 * For ISO OUT we don't need to send out a complete split
+		 * since we do not require and data coming in to us (since ISO
+		 * do not have integrity checking/handshake).
+		 *
+		 * For start split we indicate that we send a start split from the
+		 * first uFrame up to the the last uFrame needed to retrieve all
+		 * data
+		 */
+		qhead->ssplit = usof;
+		qhead->csplit = 0;
+	}	/* else for if(ep_in) */
+	return 0;
+}				/* phcd_iso_scheduling_info */
+
+/*
+ * phcd_iso_sitd_fill - Allocate memory from the PAYLOAD memory region
+ *
+ * phci_hcd *pHcd_st
+ *  - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more  specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long packets
+ *  - Total number of packets to completely transfer this ISO transfer request.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Initialize the following elements of the ITS structure
+ *       > sitd->length = length;        -- the size of the request
+ *       > sitd->multi = multi;          -- the number of transactions for
+ *                                         this EP per micro frame
+ *       > sitd->hw_bufp[0] = buf_dma;   -- The base address of the buffer where
+ *                                         to put the data (this base address was
+ *                                         the buffer provided plus the offset)
+ * - Allocating memory from the PAYLOAD memory area, where the data coming from
+ *   the requesting party will be placed or data requested by the requesting party will
+ *   be retrieved when it is available.
+ */
+unsigned long
+phcd_iso_sitd_fill(phci_hcd * hcd,
+	struct ehci_sitd *sitd,
+	struct urb *urb, unsigned long packets)
+{
+	unsigned long length, offset, pipe;
+	unsigned long max_pkt;
+	dma_addr_t buff_dma;
+	struct isp1763_mem_addr *mem_addr;
+
+#ifdef COMMON_MEMORY
+	struct ehci_qh *qhead = NULL;
+#endif
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_fill entry\n");
+	/*
+	 * The value for both these variables are supplied by the one
+	 * who submitted the URB.
+	 */
+	length = urb->iso_frame_desc[packets].length;
+	offset = urb->iso_frame_desc[packets].offset;
+
+	/* Initialize the status and actual length of this packet */
+	urb->iso_frame_desc[packets].actual_length = 0;
+	urb->iso_frame_desc[packets].status = -EXDEV;
+
+	/* Buffer for this packet */
+	buff_dma = (u32) ((unsigned char *) urb->transfer_buffer + offset);
+
+	/* Memory for this packet */
+	mem_addr = &sitd->mem_addr;
+
+	pipe = urb->pipe;
+	max_pkt = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+
+	max_pkt = max_pkt & 0x7FF;
+
+	if ((length < 0) || (max_pkt < length)) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No available memory.\n");
+		return -ENOSPC;
+	}
+	sitd->buf_dma = buff_dma;
+
+
+#ifndef COMMON_MEMORY
+	/*
+	 * Allocate memory in the PAYLOAD memory region for the
+	 * data buffer for this SITD
+	 */
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		mem_addr = 0;
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead=urb->hcpriv;
+#else
+	qhead = urb->ep->hcpriv;
+#endif
+	if (qhead) {
+
+		mem_addr->phy_addr = qhead->memory_addr.phy_addr + offset;
+
+		mem_addr->virt_addr = qhead->memory_addr.phy_addr + offset;
+	} else {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+
+
+#endif
+	/* Length of this packet */
+	sitd->length = length;
+
+	/* Buffer address, one ptd per packet */
+	sitd->hw_bufp[0] = buff_dma;
+
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_sitd_fill exit\n");
+	return 0;
+}
+
+/*
+ * phcd_iso_itd_fill - Allocate memory from the PAYLOAD memory region
+ *
+ * phci_hcd *pHcd_st
+ *  - Main host controller driver structure
+ * struct ehci_itd *itd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more IC specific elements.
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long packets
+ *  - Total number of packets to completely transfer this ISO transfer request.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Initialize the following elements of the ITS structure
+ *       > itd->length = length;        -- the size of the request
+ *       > itd->multi = multi;          -- the number of transactions for
+ *                                         this EP per micro frame
+ *       > itd->hw_bufp[0] = buf_dma;   -- The base address of the buffer where
+ *                                         to put the data (this base address was
+ *                                         the buffer provided plus the offset)
+ * - Allocating memory from the PAYLOAD memory area, where the data coming from
+ *   the requesting party will be placed or data requested by the requesting party will
+ *   be retrieved when it is available.
+ */
+unsigned long
+phcd_iso_itd_fill(phci_hcd * hcd,
+	struct ehci_itd *itd,
+	struct urb *urb,
+	unsigned long packets, unsigned char numofPkts)
+{
+	unsigned long length, offset, pipe;
+	unsigned long max_pkt, mult;
+	dma_addr_t buff_dma;
+	struct isp1763_mem_addr *mem_addr;
+#ifdef COMMON_MEMORY
+	struct ehci_qh *qhead = NULL;
+#endif
+	int i = 0;
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_fill entry\n");
+	for (i = 0; i < 8; i++){
+		itd->hw_transaction[i] = 0;
+	}
+	/*
+	 * The value for both these variables are supplied by the one
+	 * who submitted the URB.
+	 */
+	length = urb->iso_frame_desc[packets].length;
+	offset = urb->iso_frame_desc[packets].offset;
+
+	/* Initialize the status and actual length of this packet */
+	urb->iso_frame_desc[packets].actual_length = 0;
+	urb->iso_frame_desc[packets].status = -EXDEV;
+
+	/* Buffer for this packet */
+	buff_dma = cpu_to_le32((unsigned char *) urb->transfer_buffer + offset);
+
+	/* Memory for this packet */
+	mem_addr = &itd->mem_addr;
+
+	pipe = urb->pipe;
+	max_pkt = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+
+	mult = 1 + ((max_pkt >> 11) & 0x3);
+	max_pkt = max_pkt & 0x7FF;
+	max_pkt *= mult;
+
+	if ((length < 0) || (max_pkt < length)) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No available memory.\n");
+		return -ENOSPC;
+	}
+	itd->buf_dma = buff_dma;
+	for (i = packets + 1; i < numofPkts + packets; i++)
+		length += urb->iso_frame_desc[i].length;
+
+	/*
+	 * Allocate memory in the PAYLOAD memory region for the
+	 * data buffer for this ITD
+	 */
+#ifndef COMMON_MEMORY
+
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		mem_addr = 0;
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	qhead = urb->ep->hcpriv;
+#else
+	qhead=urb->hcpriv;
+#endif
+	if (qhead) {
+
+		mem_addr->phy_addr = qhead->memory_addr.phy_addr + offset;
+
+		mem_addr->virt_addr = qhead->memory_addr.phy_addr + offset;
+	} else {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_fill Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+
+
+#endif
+	/* Length of this packet */
+	itd->length = length;
+
+	/* Number of transaction per uframe */
+	itd->multi = mult;
+
+	/* Buffer address, one ptd per packet */
+	itd->hw_bufp[0] = buff_dma;
+
+	iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_fill exit\n");
+	return 0;
+}				/* phcd_iso_itd_fill */
+
+/*
+ * phcd_iso_get_sitd_ptd_index - Allocate an ISO PTD from the ISO PTD map list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more  specific elements.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating an ISO PTD from the ISO PTD map list
+ * - Set the equivalent bit of the allocated PTD to active
+ *   in the bitmap so that this PTD will be included into
+ *   the periodic schedule
+ */
+void
+phcd_iso_get_sitd_ptd_index(phci_hcd * hcd, struct ehci_sitd *sitd)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	unsigned long buff_type, max_ptds;
+	unsigned char sitd_index, bitmap;
+
+	/* Local variable initialization */
+	bitmap = 0x1;
+	buff_type = td_ptd_pipe_x_buff_type[TD_PTD_BUFF_TYPE_ISTL];
+	ptd_map_buff = (td_ptd_map_buff_t *) & (td_ptd_map_buff[buff_type]);
+	max_ptds = ptd_map_buff->max_ptds;
+	sitd->sitd_index = TD_PTD_INV_PTD_INDEX;
+
+	for (sitd_index = 0; sitd_index < max_ptds; sitd_index++) {
+		/*
+		 * ISO have 32 PTDs, the first thing to do is look for a free PTD.
+		 */
+		if (ptd_map_buff->map_list[sitd_index].state == TD_PTD_NEW) {
+			iso_dbg(ISO_DBG_INFO,
+				"[phcd_iso_get_itd_ptd_index] There's a free PTD No. %d\n",
+				sitd_index);
+			/*
+			 * Determine if this is a newly allocated SITD by checking the
+			 * itd_index, since it was set to TD_PTD_INV_PTD_INDEX during
+			 * initialization
+			 */
+			if (sitd->sitd_index == TD_PTD_INV_PTD_INDEX) {
+				sitd->sitd_index = sitd_index;
+			}
+
+			/* Once there is a free slot, indicate that it is already taken */
+			ptd_map_buff->map_list[sitd_index].datatoggle = 0;
+			ptd_map_buff->map_list[sitd_index].state =
+				TD_PTD_ACTIVE;
+			ptd_map_buff->map_list[sitd_index].qtd = NULL;
+
+			/* Put a connection to the SITD with the PTD maplist */
+			ptd_map_buff->map_list[sitd_index].sitd = sitd;
+			ptd_map_buff->map_list[sitd_index].itd = NULL;
+			ptd_map_buff->map_list[sitd_index].qh = NULL;
+
+			/* ptd_bitmap just holds the bit assigned to this PTD. */
+			ptd_map_buff->map_list[sitd_index].ptd_bitmap =
+				bitmap << sitd_index;
+
+			phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+				map_list[sitd_index], sitd->sitd_index,
+				buff_type);
+
+			/*
+			 * Indicate that this SITD is the last in the list and update
+			 * the number of active PTDs
+			 */
+			ptd_map_buff->map_list[sitd_index].lasttd = 0;
+			ptd_map_buff->total_ptds++;
+
+
+			ptd_map_buff->active_ptd_bitmap |=
+				(bitmap << sitd_index);
+			ptd_map_buff->pending_ptd_bitmap |= (bitmap << sitd_index);	
+			break;
+		}		/* if(ptd_map_buff->map_list[sitd_index].state == TD_PTD_NEW) */
+	}			/* for(itd_index = 0; itd_index < max_ptds; itd_index++) */
+	return;
+}
+
+/*
+ * phcd_iso_get_itd_ptd_index - Allocate an ISO PTD from the ISO PTD map list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct ehci_itd *itd
+ *  - Isochronous Transfer Descriptor, contains elements as defined by the
+ *        EHCI standard plus a few more IC specific elements.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating an ISO PTD from the ISO PTD map list
+ * - Set the equivalent bit of the allocated PTD to active
+ *   in the bitmap so that this PTD will be included into
+ *   the periodic schedule
+ */
+void
+phcd_iso_get_itd_ptd_index(phci_hcd * hcd, struct ehci_itd *itd)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	unsigned long buff_type, max_ptds;
+	unsigned char itd_index, bitmap;
+
+	/* Local variable initialization */
+	bitmap = 0x1;
+	buff_type = td_ptd_pipe_x_buff_type[TD_PTD_BUFF_TYPE_ISTL];
+	ptd_map_buff = (td_ptd_map_buff_t *) & (td_ptd_map_buff[buff_type]);
+	max_ptds = ptd_map_buff->max_ptds;
+
+	itd->itd_index = TD_PTD_INV_PTD_INDEX;
+
+	for (itd_index = 0; itd_index < max_ptds; itd_index++) {
+		/*
+		 * ISO have 32 PTDs, the first thing to do is look for a free PTD.
+		 */
+		if (ptd_map_buff->map_list[itd_index].state == TD_PTD_NEW) {
+			/*
+			 * Determine if this is a newly allocated ITD by checking the
+			 * itd_index, since it was set to TD_PTD_INV_PTD_INDEX during
+			 * initialization
+			 */
+			if (itd->itd_index == TD_PTD_INV_PTD_INDEX) {
+				itd->itd_index = itd_index;
+			}
+
+			/* Once there is a free slot, indicate that it is already taken */
+			ptd_map_buff->map_list[itd_index].datatoggle = 0;
+			ptd_map_buff->map_list[itd_index].state = TD_PTD_ACTIVE;
+			ptd_map_buff->map_list[itd_index].qtd = NULL;
+
+			/* Put a connection to the ITD with the PTD maplist */
+			ptd_map_buff->map_list[itd_index].itd = itd;
+			ptd_map_buff->map_list[itd_index].qh = NULL;
+
+			/* ptd_bitmap just holds the bit assigned to this PTD. */
+			ptd_map_buff->map_list[itd_index].ptd_bitmap =
+				bitmap << itd_index;
+
+			phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+				map_list[itd_index],
+				itd->itd_index, buff_type);
+
+			/*
+			 * Indicate that this ITD is the last in the list and update
+			 * the number of active PTDs
+			 */
+			ptd_map_buff->map_list[itd_index].lasttd = 0;
+			ptd_map_buff->total_ptds++;
+
+			ptd_map_buff->active_ptd_bitmap |=
+				(bitmap << itd_index);
+			ptd_map_buff->pending_ptd_bitmap |= (bitmap << itd_index);	
+			break;
+		}		/* if(ptd_map_buff->map_list[itd_index].state == TD_PTD_NEW) */
+	}			/* for(itd_index = 0; itd_index < max_ptds; itd_index++) */
+	return;
+}				/* phcd_iso_get_itd_ptd_index */
+
+/*
+ * phcd_iso_sitd_free_list - Free memory used by SITDs in SITD list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long status
+ *  - Variable provided by the calling routine that contain the status of the
+ *        SITD list.
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Cleaning up memory used by each SITD in the SITD list
+ */
+void
+phcd_iso_sitd_free_list(phci_hcd * hcd, struct urb *urb, unsigned long status)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct ehci_sitd *first_sitd, *next_sitd, *sitd;
+	td_ptd_map_t *td_ptd_map;
+
+	/* Local variable initialization */
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+	first_sitd = (struct ehci_sitd *) urb->hcpriv;
+	sitd = first_sitd;
+
+	/*
+	 * Check if there is only one SITD, if so immediately
+	 * go and clean it up.
+	 */
+	if (sitd->hw_next == EHCI_LIST_END) {
+		if (sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+			td_ptd_map = &ptd_map_buff->map_list[sitd->sitd_index];
+			td_ptd_map->state = TD_PTD_NEW;
+		}
+
+		if (status != -ENOMEM) {
+			phci_hcd_mem_free(&sitd->mem_addr);
+		}
+
+		list_del(&sitd->sitd_list);
+		qha_free(qha_cache, sitd);
+
+		urb->hcpriv = 0;
+		return;
+	}
+	/* if(sitd->hw_next == EHCI_LIST_END) */
+	while (1) {
+		/* Get the SITD following the head SITD */
+		next_sitd = (struct ehci_sitd *) (sitd->hw_next);
+		if (next_sitd->hw_next == EHCI_LIST_END) {
+			/*
+			 * If the next SITD is the end of the list, check if space have
+			 * already been allocated in the PTD array.
+			 */
+			if (next_sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+				/* Free up its allocation */
+				td_ptd_map =
+					&ptd_map_buff->map_list[next_sitd->
+					sitd_index];
+				td_ptd_map->state = TD_PTD_NEW;
+			}
+
+			/*
+			 * If the error is not about memory allocation problems, then
+			 * free up the memory used.
+			 */
+			if (status != -ENOMEM) {
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_iso_itd_free_list Error]: Memory not available\n");
+				phci_hcd_mem_free(&next_sitd->mem_addr);
+			}
+
+			/* Remove from the SITD list and free up space allocated for SITD structure */
+			list_del(&next_sitd->sitd_list);
+			qha_free(qha_cache, next_sitd);
+			break;
+		}
+
+		/* if(next_itd->hw_next == EHCI_LIST_END) */
+		/*
+		 * If SITD is not the end of the list, it only means that it already have everything allocated
+		 * and there is no need to check which procedure failed. So just free all resourcs immediately
+		 */
+		sitd->hw_next = next_sitd->hw_next;
+
+		td_ptd_map = &ptd_map_buff->map_list[next_sitd->sitd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+		phci_hcd_mem_free(&next_sitd->mem_addr);
+		list_del(&next_sitd->sitd_list);
+		qha_free(qha_cache, next_sitd);
+	}			/*  while(1) */
+
+	/* Now work on the head SITD, it is the last one processed. */
+	if (first_sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+		td_ptd_map = &ptd_map_buff->map_list[first_sitd->sitd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+	}
+
+	if (status != -ENOMEM) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_free_list Error]: No memory\n");
+		phci_hcd_mem_free(&first_sitd->mem_addr);
+	}
+
+	list_del(&first_sitd->sitd_list);
+	qha_free(qha_cache, first_sitd);
+	urb->hcpriv = 0;
+	return;
+}
+
+/*
+ * phcd_iso_itd_free_list - Free memory used by ITDs in ITD list
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long status
+ *  - Variable provided by the calling routine that contain the status of the
+ *        ITD list.
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Cleaning up memory used by each ITD in the ITD list
+ */
+void
+phcd_iso_itd_free_list(phci_hcd * hcd, struct urb *urb, unsigned long status)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct ehci_itd *first_itd, *next_itd, *itd;
+	td_ptd_map_t *td_ptd_map;
+
+	/* Local variable initialization */
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+	first_itd = (struct ehci_itd *) urb->hcpriv;
+	itd = first_itd;
+
+	/*
+	 * Check if there is only one ITD, if so immediately
+	 * go and clean it up.
+	 */
+	if (itd->hw_next == EHCI_LIST_END) {
+		if (itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+			td_ptd_map = &ptd_map_buff->map_list[itd->itd_index];
+			td_ptd_map->state = TD_PTD_NEW;
+		}
+
+		if (status != -ENOMEM) {
+			phci_hcd_mem_free(&itd->mem_addr);
+		}
+
+		list_del(&itd->itd_list);
+		qha_free(qha_cache, itd);
+
+		urb->hcpriv = 0;
+		return;
+	}
+	/* if(itd->hw_next == EHCI_LIST_END) */
+	while (1) {
+		/* Get the ITD following the head ITD */
+		next_itd = (struct ehci_itd *) le32_to_cpu(itd->hw_next);
+		if (next_itd->hw_next == EHCI_LIST_END) {
+			/*
+			 * If the next ITD is the end of the list, check if space have
+			 * already been allocated in the PTD array.
+			 */
+			if (next_itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+				/* Free up its allocation */
+				td_ptd_map =
+					&ptd_map_buff->map_list[next_itd->
+					itd_index];
+				td_ptd_map->state = TD_PTD_NEW;
+			}
+
+			/*
+			 * If the error is not about memory allocation problems, then
+			 * free up the memory used.
+			 */
+			if (status != -ENOMEM) {
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_iso_itd_free_list Error]: Memory not available\n");
+				phci_hcd_mem_free(&next_itd->mem_addr);
+			}
+
+			/* Remove from the ITD list and free up space allocated for ITD structure */
+			list_del(&next_itd->itd_list);
+			qha_free(qha_cache, next_itd);
+			break;
+		}
+
+		/* if(next_itd->hw_next == EHCI_LIST_END) */
+		/*
+		 * If ITD is not the end of the list, it only means that it already have everything allocated
+		 * and there is no need to check which procedure failed. So just free all resourcs immediately
+		 */
+		itd->hw_next = next_itd->hw_next;
+
+		td_ptd_map = &ptd_map_buff->map_list[next_itd->itd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+		phci_hcd_mem_free(&next_itd->mem_addr);
+		list_del(&next_itd->itd_list);
+		qha_free(qha_cache, next_itd);
+	}			/*  while(1) */
+
+	/* Now work on the head ITD, it is the last one processed. */
+	if (first_itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+		td_ptd_map = &ptd_map_buff->map_list[first_itd->itd_index];
+		td_ptd_map->state = TD_PTD_NEW;
+	}
+
+	if (status != -ENOMEM) {
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_iso_itd_free_list Error]: No memory\n");
+		phci_hcd_mem_free(&first_itd->mem_addr);
+	}
+
+	list_del(&first_itd->itd_list);
+	qha_free(qha_cache, first_itd);
+	urb->hcpriv = 0;
+	return;
+}				/* phcd_iso_itd_free_list */
+
+void
+phcd_clean_iso_qh(phci_hcd * hcd, struct ehci_qh *qh)
+{
+	unsigned int i = 0;
+	u16 skipmap=0;
+	struct ehci_sitd *sitd;
+	struct ehci_itd *itd;
+
+	iso_dbg(ISO_DBG_ERR, "phcd_clean_iso_qh \n");
+	if (!qh){
+		return;
+	}
+	skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+	skipmap |= qh->periodic_list.ptdlocation;
+	isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+#ifdef COMMON_MEMORY
+	phci_hcd_mem_free(&qh->memory_addr);
+#endif
+	for (i = 0; i < 16 && qh->periodic_list.ptdlocation; i++) {
+		if (qh->periodic_list.ptdlocation & (0x1 << i)) {
+			printk("[phcd_clean_iso_qh] : %x \n",
+				qh->periodic_list.high_speed);
+
+			qh->periodic_list.ptdlocation &= ~(0x1 << i);
+
+			if (qh->periodic_list.high_speed == 0) {
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].sitd) {
+
+					printk("SITD found \n");
+					sitd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd;
+#ifndef COMMON_MEMORY
+					phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+					/*
+					if(sitd->urb)
+						urb=sitd->urb;
+					*/
+					sitd->urb = NULL;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd = NULL;
+					qha_free(qha_cache, sitd);
+				}
+			} else {
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].itd) {
+
+					printk("ITD found \n");
+					itd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd;
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&itd->mem_addr);
+#endif
+
+					/*
+					if(itd->urb)
+					urb=itd->urb;
+					*/
+					itd->urb = NULL;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd = NULL;
+					qha_free(qha_cache, itd);
+				}
+			}
+
+		}
+	}
+
+
+}
+
+
+/*
+ * phcd_store_urb_pending - store requested URB into a queue
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long *status
+ *  - Variable provided by the calling routine that will contain the status of the
+ *        phcd_submit_iso actions
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Store URB into a queue
+ *  - If ther's enough free PTD slots , repairing the PTDs
+ */
+void phcd_clean_periodic_ep(void){
+	periodic_ep[0] = NULL;
+	periodic_ep[1] = NULL;
+}
+
+int
+phcd_clean_urb_pending(phci_hcd * hcd, struct urb *urb)
+{
+	unsigned int i = 0;
+	struct ehci_qh *qhead;
+	struct ehci_sitd *sitd;
+	struct ehci_itd *itd;
+	u16 skipmap=0;;
+
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_clean_urb_pending] : Enter\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead=urb->hcpriv;
+	if (periodic_ep[0] == qhead->ep) {
+		periodic_ep[0] = NULL;
+
+	}
+
+	if (periodic_ep[1] == qhead->ep) {
+		periodic_ep[1] = NULL;
+	}
+#else	
+	qhead = urb->ep->hcpriv;
+	if (periodic_ep[0] == urb->ep) {
+		periodic_ep[0] = NULL;
+
+	}
+
+	if (periodic_ep[1] == urb->ep) {
+		periodic_ep[1] = NULL;
+	}
+#endif	
+	if (!qhead) {
+		return 0;
+	}
+	skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+	skipmap |= qhead->periodic_list.ptdlocation;
+	isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+#ifdef COMMON_MEMORY
+	phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+
+	for (i = 0; i < 16 && qhead->periodic_list.ptdlocation; i++) {
+
+		qhead->periodic_list.ptdlocation &= ~(0x1 << i);
+
+		if (qhead->periodic_list.ptdlocation & (0x1 << i)) {
+
+			printk("[phcd_clean_urb_pending] : %x \n",
+				qhead->periodic_list.high_speed);
+
+			if (qhead->periodic_list.high_speed == 0) {
+
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].sitd) {
+
+					sitd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd;
+#ifndef COMMON_MEMORY
+					phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].sitd = NULL;
+					qha_free(qha_cache, sitd);
+				}
+			} else {
+
+				if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+					map_list[i].itd) {
+
+					itd = td_ptd_map_buff
+						[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd;
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&itd->mem_addr);
+#endif
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].state = TD_PTD_NEW;
+					td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+						map_list[i].itd = NULL;
+					qha_free(qha_cache, itd);
+				}
+			}
+
+		}
+
+	}
+	INIT_LIST_HEAD(&qhead->periodic_list.sitd_itd_head);
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_clean_urb_pending] : Exit\n");
+	return 0;
+}
+
+
+
+int
+phcd_store_urb_pending(phci_hcd * hcd, int index, struct urb *urb, int *status)
+{
+	unsigned int uiNumofPTDs = 0;
+	unsigned int uiNumofSlots = 0;
+	unsigned int uiMult = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_store_urb_pending] : Enter\n");
+	if (urb != NULL) {
+		if (periodic_ep[0] != urb->ep && periodic_ep[1] != urb->ep) {
+			if (periodic_ep[0] == NULL) {
+			//	printk("storing in 0 %x %x\n",urb,urb->pipe);
+				periodic_ep[0] = urb->ep;
+			} else if (periodic_ep[1] == NULL) {
+				printk("storing in 1\n");
+				periodic_ep[1] = urb->ep;
+				usb_hcd_link_urb_to_ep(&(hcd->usb_hcd), urb);
+				return -1;
+			} else {
+				iso_dbg(ISO_DBG_ERR,
+					"Support only 2 ISO endpoints simultaneously \n");
+				*status = -1;
+				return -1;
+			}
+		}
+		usb_hcd_link_urb_to_ep(&(hcd->usb_hcd), urb);
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : Add an urb into gstUrb_pending array at index : %d\n",
+			giUrbCount);
+		giUrbCount++;
+	} else {
+
+		iso_dbg(ISO_DBG_ENTRY,
+			"[phcd_store_urb_pending] : getting urb from list \n");
+		if (index > 0 && index < 2) {
+			if (periodic_ep[index - 1]){
+				urb = container_of(periodic_ep[index - 1]->
+					urb_list.next, struct urb,
+					urb_list);
+			}
+		} else {
+			iso_dbg(ISO_DBG_ERR, " Unknown enpoints Error \n");
+			*status = -1;
+			return -1;
+		}
+
+	}
+
+
+	if ((urb != NULL && (urb->ep->urb_list.next == &urb->urb_list))){
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : periodic_sched : %d\n",
+			hcd->periodic_sched);
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : number_of_packets : %d\n",
+			urb->number_of_packets);
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : Maximum PacketSize : %d\n",
+			usb_maxpacket(urb->dev,urb->pipe, usb_pipeout(urb->pipe)));
+		/*if enough free slots */
+		if (urb->dev->speed == USB_SPEED_FULL) {	/*for FULL SPEED */
+	//		if (hcd->periodic_sched < 
+		//		MAX_PERIODIC_SIZE - urb->number_of_packets) {
+			if(1){
+				if (phcd_submit_iso(hcd, 
+					#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						struct usb_host_endpoint *ep,
+					#endif
+						urb,
+						( unsigned long *) &status) == 0) {
+					pehci_hcd_iso_schedule(hcd, urb);
+				} else{
+				//*status = 0;
+				}
+			}
+		} else if (urb->dev->speed == USB_SPEED_HIGH) {	/*for HIGH SPEED */
+			/*number of slots for 1 PTD */
+			uiNumofSlots = NUMMICROFRAME / urb->interval;
+			/*max packets size */
+			uiMult = usb_maxpacket(urb->dev, urb->pipe,
+					usb_pipeout(urb->pipe));
+			/*mult */
+			uiMult = 1 + ((uiMult >> 11) & 0x3);
+			/*number of PTDs need to schedule for this PTD */
+			uiNumofPTDs =
+				(urb->number_of_packets / uiMult) /
+				uiNumofSlots;
+			if ((urb->number_of_packets / uiMult) % uiNumofSlots != 0){
+				uiNumofPTDs += 1;
+			}
+
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_store_urb_pending] : interval : %d\n",
+				urb->interval);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_store_urb_pending] : uiMult : %d\n",
+				uiMult);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_store_urb_pending] : uiNumofPTDs : %d\n",
+				uiNumofPTDs);
+
+			if (hcd->periodic_sched <=
+				MAX_PERIODIC_SIZE - uiNumofPTDs) {
+
+				if (phcd_submit_iso(hcd,
+					#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						struct usb_host_endpoint *ep,
+					#endif
+					urb, (unsigned long *) &status)== 0) {
+
+					pehci_hcd_iso_schedule(hcd, urb);
+				}
+			} else{
+				*status = 0;
+			}
+		}
+	} else{
+		iso_dbg(ISO_DBG_DATA,
+			"[phcd_store_urb_pending] : nextUrb is NULL\n");
+	}
+#endif
+	iso_dbg(ISO_DBG_ENTRY, "[phcd_store_urb_pending] : Exit\n");
+	return 0;
+}
+
+/*
+ * phcd_submit_iso - ISO transfer URB submit routine
+ *
+ * phci_hcd *hcd
+ *      - Main host controller driver structure
+ * struct urb *urb
+ *  - USB Request Block, contains information regarding the type and how much data
+ *    is requested to be transferred.
+ * unsigned long *status
+ *  - Variable provided by the calling routine that will contain the status of the
+ *        phcd_submit_iso actions
+ *
+ * API Description
+ * This is mainly responsible for:
+ *  - Allocating memory for the endpoint information structure (pQHead_st)
+ *  - Requesting for bus bandwidth from the USB core
+ *  - Allocating and initializing Payload and PTD memory
+ */
+unsigned long
+phcd_submit_iso(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+		struct urb *urb, unsigned long *status)
+{
+	struct _periodic_list *periodic_list;
+	struct hcd_dev *dev;
+	struct ehci_qh *qhead;
+	struct ehci_itd *itd, *prev_itd;
+	struct ehci_sitd *sitd, *prev_sitd;
+	struct list_head *sitd_itd_list;
+	unsigned long ep_in, max_pkt, mult;
+	unsigned long bus_time, high_speed, start_frame;
+	unsigned long temp;
+	unsigned long packets;
+	/*for high speed device */
+	unsigned int iMicroIndex = 0;
+	unsigned int iNumofSlots = 0;
+	unsigned int iNumofPTDs = 0;
+	unsigned int iPTDIndex = 0;
+	unsigned int iNumofPks = 0;
+	int iPG = 0;
+	dma_addr_t buff_dma;
+	unsigned long length, offset;
+	int i = 0;
+
+	iso_dbg(ISO_DBG_ENTRY, "phcd_submit_iso Entry\n");
+
+	*status = 0;
+	/* Local variable initialization */
+	high_speed = 0;
+	periodic_list = &hcd->periodic_list[0];
+	dev = (struct hcd_dev *) urb->hcpriv;
+	urb->hcpriv = (void *) 0;
+	prev_itd = (struct ehci_itd *) 0;
+	itd = (struct ehci_itd *) 0;
+	prev_sitd = (struct ehci_sitd *) 0;
+	sitd = (struct ehci_sitd *) 0;
+	start_frame = 0;
+
+	ep_in = usb_pipein(urb->pipe);
+
+	/*
+	 * Take the endpoint, if there is still no memory allocated
+	 * for it allocate some and indicate this is for ISO.
+	 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead = ep->hcpriv;
+#else
+	qhead = urb->ep->hcpriv;
+#endif
+	if (!qhead) {
+
+		qhead = phci_hcd_qh_alloc(hcd);
+		if (qhead == 0) {
+			iso_dbg(ISO_DBG_ERR,
+				"[phcd_submit_iso Error]: Not enough memory\n");
+			return -ENOMEM;
+		}
+
+		qhead->type = TD_PTD_BUFF_TYPE_ISTL;
+		INIT_LIST_HEAD(&qhead->periodic_list.sitd_itd_head);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		qhead->ep=ep;
+		ep->hcpriv = qhead;
+		urb->hcpriv=qhead;
+#else
+		urb->ep->hcpriv = qhead;
+#endif
+	}
+
+		urb->hcpriv=qhead;
+
+	/* if(!qhead) */
+	/*
+	 * Get the number of additional packets that the endpoint can support during a
+	 * single microframe.
+	 */
+	max_pkt = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	/*
+	 * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+	 * zero (0).
+	 */
+	mult = 1 + ((max_pkt >> 11) & 0x3);
+
+	/* This is the actual length per for the whole transaction */
+	max_pkt *= mult;
+
+	/* Check bandwidth */
+	bus_time = 0;
+
+	if (urb->dev->speed == USB_SPEED_FULL) {
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		if (urb->bandwidth == 0) {
+			bus_time = usb_check_bandwidth(urb->dev, urb);
+			if (bus_time < 0) {
+				usb_dec_dev_use(urb->dev);
+				*status = bus_time;
+				return *status;
+			}
+		}
+#else
+#endif
+	} else {			/*HIGH SPEED */
+
+		high_speed = 1;
+
+		/*
+		 * Calculate bustime as dictated by the USB Specs Section 5.11.3
+		 * for high speed ISO
+		 */
+		bus_time = 633232L;
+		bus_time +=
+			(2083L * ((3167L + BitTime(max_pkt) * 1000L) / 1000L));
+		bus_time = bus_time / 1000L;
+		bus_time += BW_HOST_DELAY;
+		bus_time = NS_TO_US(bus_time);
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	usb_claim_bandwidth(urb->dev, urb, bus_time, 1);
+#else
+#endif
+
+	qhead->periodic_list.ptdlocation = 0;
+	/* Initialize the start split (ssplit) and complete split (csplit) variables of qhead */
+	if (phcd_iso_scheduling_info(hcd, qhead, max_pkt, high_speed, ep_in) <
+		0) {
+
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_submit_iso Error]: No space available\n");
+		return -ENOSPC;
+	}
+
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		iNumofSlots = NUMMICROFRAME / urb->interval;
+		/*number of PTDs need to schedule for this PTD */
+		iNumofPTDs = (urb->number_of_packets / mult) / iNumofSlots;
+		if ((urb->number_of_packets / mult) % iNumofSlots != 0){	
+			/*get remainder */
+			iNumofPTDs += 1;
+		}
+	}
+	if (urb->iso_frame_desc[0].offset != 0) {
+		*status = -EINVAL;
+		iso_dbg(ISO_DBG_ERR,
+			"[phcd_submit_iso Error]: Invalid value\n");
+		return *status;
+	}
+	if (1) {
+		/* Calculate the current frame number */
+		if (0){
+			if (urb->transfer_flags & URB_ISO_ASAP){
+				start_frame =
+					isp1763_reg_read16(hcd->dev,
+						hcd->regs.frameindex,
+						start_frame);
+			} else {
+				start_frame = urb->start_frame;
+			}
+		}
+
+		start_frame =
+			isp1763_reg_read16(hcd->dev, hcd->regs.frameindex,
+				start_frame);
+
+		/* The only valid bits of the frame index is the lower 14 bits. */
+
+		/*
+		 * Remove the count for the micro frame (uSOF) and just leave the
+		 * count for the frame (SOF). Since 1 SOF is equal to 8 uSOF then
+		 * shift right by three is like dividing it by 8 (each shift is divide by two)
+		 */
+		start_frame >>= 3;
+		if (urb->dev->speed != USB_SPEED_HIGH){
+			start_frame += 1;
+		}else{
+			start_frame += 2;
+		}
+		start_frame = start_frame & PTD_FRAME_MASK;
+		temp = start_frame;
+		if (urb->dev->speed != USB_SPEED_HIGH) {
+			qhead->next_uframe =
+				start_frame + urb->number_of_packets;
+		} else {
+			qhead->next_uframe = start_frame + iNumofPTDs;
+		}
+		qhead->next_uframe %= PTD_FRAME_MASK;
+		iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: startframe = %ld\n",
+			start_frame);
+	} else {
+		/*
+		 * The periodic frame list size is only 32 elements deep, so we need
+		 * the frame index to be less than or equal to 32 (actually 31 if we
+		 * start from 0)
+		 */
+		start_frame = (qhead->next_uframe) % PTD_FRAME_MASK;
+		if (urb->dev->speed != USB_SPEED_HIGH){
+			qhead->next_uframe =
+				start_frame + urb->number_of_packets;
+				iNumofPTDs=urb->number_of_packets;
+		} else {
+			qhead->next_uframe = start_frame + iNumofPTDs;
+		}
+
+		qhead->next_uframe %= PTD_FRAME_MASK;
+	}
+
+
+	iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: Start frame index: %ld\n",
+		start_frame);
+	iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: Max packet: %d\n",
+		(int) max_pkt);
+
+#ifdef COMMON_MEMORY
+	if(urb->number_of_packets>8 && urb->dev->speed!=USB_SPEED_HIGH)
+		phci_hcd_mem_alloc(8*max_pkt, &qhead->memory_addr, 0);
+	else
+	phci_hcd_mem_alloc(urb->transfer_buffer_length, &qhead->memory_addr, 0);
+	if (urb->transfer_buffer_length && ((qhead->memory_addr.phy_addr == 0)
+		|| (qhead->memory_addr.virt_addr ==0))) {
+		iso_dbg(ISO_DBG_ERR,
+			"[URB FILL MEMORY Error]: No payload memory available\n");
+		return -ENOMEM;
+	}
+#endif
+
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		iNumofPks = urb->number_of_packets;
+		qhead->totalptds=urb->number_of_packets;
+		qhead->actualptds=0;
+
+		/* Make as many tds as number of packets */
+		for (packets = 0; packets < urb->number_of_packets; packets++) {
+			/*
+			 * Allocate memory for the SITD data structure and initialize it.
+			 *
+			 * This data structure follows the format of the SITD
+			 * structure defined by the EHCI standard on the top part
+			 * but also contains specific elements in the bottom
+			 * part
+			 */
+			sitd = kmalloc(sizeof(*sitd), GFP_ATOMIC);
+			if (!sitd) {
+				*status = -ENOMEM;
+				if (((int)(qhead->next_uframe -
+					urb->number_of_packets)) < 0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	
+					
+				}
+				qhead->next_uframe -= urb->number_of_packets;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_sitd_free_list(hcd, urb, 
+						*status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: No memory available\n");
+				return *status;
+			}
+
+			memset(sitd, 0, sizeof(struct ehci_sitd));
+
+			INIT_LIST_HEAD(&sitd->sitd_list);
+
+			sitd->sitd_dma = (u32) (sitd);
+			sitd->urb = urb;
+
+			/*
+			 * Indicate that this SITD is the last in the list.
+			 *
+			 * Also set the itd_index to TD_PTD_INV_PTD_INDEX
+			 * (0xFFFFFFFF). This would indicate when we allocate
+			 * a PTD that this SITD did not have a PTD allocated
+			 * before.
+			 */
+
+			sitd->hw_next = EHCI_LIST_END;
+			sitd->sitd_index = TD_PTD_INV_PTD_INDEX;
+
+			/* This SITD will go into this frame */
+			sitd->framenumber = start_frame + packets;
+			sitd->start_frame = temp + packets;
+
+			/* Number of the packet */
+			sitd->index = packets;
+
+			sitd->framenumber = sitd->framenumber & PTD_FRAME_MASK;
+			sitd->ssplit = qhead->ssplit;
+			sitd->csplit = qhead->csplit;
+
+			/* Initialize the following elements of the ITS structure
+			 *      > sitd->length = length;                 -- the size of the request
+			 *      > sitd->multi = multi;                   -- the number of transactions for
+			 *                                         this EP per micro frame
+			 *      > sitd->hw_bufp[0] = buf_dma;    -- The base address of the buffer where
+			 *                                         to put the data (this base address was
+			 *                                         the buffer provided plus the offset)
+			 * And then, allocating memory from the PAYLOAD memory area, where the data
+			 * coming from the requesting party will be placed or data requested by the
+			 * requesting party will be retrieved when it is available.
+			 */
+			*status = phcd_iso_sitd_fill(hcd, sitd, urb, packets);
+
+			if (*status != 0) {
+				if (((int)(qhead->next_uframe - 
+					urb->number_of_packets)) < 0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + 
+						PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= urb->number_of_packets;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_sitd_free_list(hcd, urb,
+						*status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: Error in filling up SITD\n");
+				return *status;
+			}
+
+			/*
+			 * If this SITD is not the head/root SITD, link this SITD to the SITD
+			 * that came before it.
+			 */
+			if (prev_sitd) {
+				prev_sitd->hw_next = (u32) (sitd);
+			}
+
+			prev_sitd = sitd;
+
+			if(packets<8){  //bcs of memory constraint , we use only first 8 PTDs if number_of_packets is more than 8.
+			/*
+			 * Allocate an ISO PTD from the ISO PTD map list and
+			 * set the equivalent bit of the allocated PTD to active
+			 * in the bitmap so that this PTD will be included into
+			 * the periodic schedule
+			 */
+			phcd_iso_get_sitd_ptd_index(hcd, sitd);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso]: SITD index %d\n",
+				sitd->sitd_index);
+
+			/*if we dont have any space left */
+			if (sitd->sitd_index == TD_PTD_INV_PTD_INDEX) {
+				*status = -ENOSPC;
+				if (((int) (qhead->next_uframe -
+					urb->number_of_packets)) < 0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= urb->number_of_packets;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_sitd_free_list(hcd, urb,
+						*status);
+				}
+				return *status;
+			}
+					qhead->actualptds++;
+			}
+			/* Insert this td into the periodic list */
+
+			sitd_itd_list = &qhead->periodic_list.sitd_itd_head;
+			list_add_tail(&sitd->sitd_list, sitd_itd_list);
+			qhead->periodic_list.high_speed = 0;
+			if(sitd->sitd_index!=TD_PTD_INV_PTD_INDEX)
+			qhead->periodic_list.ptdlocation |=
+				0x1 << sitd->sitd_index;
+			/* Inidcate that a new SITD have been scheduled */
+			hcd->periodic_sched++;
+
+			/* Determine if there are any SITD scheduled before this one. */
+			if (urb->hcpriv == 0){
+				urb->hcpriv = sitd;
+			}
+		}	/* for(packets = 0; packets... */
+	} else if (urb->dev->speed == USB_SPEED_HIGH) {	
+		iNumofPks = iNumofPTDs;
+
+		packets = 0;
+		iPTDIndex = 0;
+		while (packets < urb->number_of_packets) {
+			iNumofSlots = NUMMICROFRAME / urb->interval;
+			/*
+			 * Allocate memory for the ITD data structure and initialize it.
+			 *
+			 * This data structure follows the format of the ITD
+			 * structure defined by the EHCI standard on the top part
+			 * but also contains specific elements in the bottom
+			 * part
+			 */
+			itd = kmalloc(sizeof(*itd), GFP_ATOMIC);
+			if (!itd) {
+				*status = -ENOMEM;
+				if(((int) (qhead->next_uframe - iNumofPTDs))<0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + 
+						PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= iNumofPTDs;
+
+				/* Handle ITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_itd_free_list(hcd, urb,
+							       *status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: No memory available\n");
+				return *status;
+			}
+			memset(itd, 0, sizeof(struct ehci_itd));
+
+			INIT_LIST_HEAD(&itd->itd_list);
+
+			itd->itd_dma = (u32) (itd);
+			itd->urb = urb;
+			/*
+			 * Indicate that this ITD is the last in the list.
+			 *
+			 * Also set the itd_index to TD_PTD_INV_PTD_INDEX
+			 * (0xFFFFFFFF). This would indicate when we allocate
+			 * a PTD that this SITD did not have a PTD allocated
+			 * before.
+			 */
+
+			itd->hw_next = EHCI_LIST_END;
+			itd->itd_index = TD_PTD_INV_PTD_INDEX;
+			/* This ITD will go into this frame */
+			itd->framenumber = start_frame + iPTDIndex;
+			/* Number of the packet */
+			itd->index = packets;
+
+			itd->framenumber = itd->framenumber & 0x1F;
+
+			itd->ssplit = qhead->ssplit;
+			itd->csplit = qhead->csplit;
+
+			/*caculate the number of packets for this itd */
+			itd->num_of_pkts = iNumofSlots * mult;
+			/*for the case , urb number_of_packets is less than (number of slot*mult*x times) */
+			if (itd->num_of_pkts >= urb->number_of_packets)
+			{
+				itd->num_of_pkts = urb->number_of_packets;
+			}
+			else {
+				if (itd->num_of_pkts >
+					urb->number_of_packets - packets){
+					itd->num_of_pkts =
+						urb->number_of_packets -
+						packets;
+				}
+			}
+
+			/* Initialize the following elements of the ITS structure
+			 *      > itd->length = length;                 -- the size of the request
+			 *      > itd->multi = multi;                   -- the number of transactions for
+			 *                                         this EP per micro frame
+			 *      > itd->hw_bufp[0] = buf_dma;    -- The base address of the buffer where
+			 *                                         to put the data (this base address was
+			 *                                         the buffer provided plus the offset)
+			 * And then, allocating memory from the PAYLOAD memory area, where the data
+			 * coming from the requesting party will be placed or data requested by the
+			 * requesting party will be retrieved when it is available.
+			 */
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso] packets index = %ld itd->num_of_pkts = %d\n",
+				packets, itd->num_of_pkts);
+			*status =
+				phcd_iso_itd_fill(hcd, itd, urb, packets,
+						itd->num_of_pkts);
+			if (*status != 0) {
+				if (((int) (qhead->next_uframe - iNumofPTDs)) <
+					0) {
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	/*plus max PTDs*/
+				}
+				qhead->next_uframe -= iNumofPTDs;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_itd_free_list(hcd, urb,
+						*status);
+				}
+				iso_dbg(ISO_DBG_ERR,
+					"[phcd_submit_iso Error]: Error in filling up ITD\n");
+				return *status;
+			}
+
+			iPG = 0;
+			iMicroIndex = 0;
+			while (iNumofSlots > 0) {
+				offset = urb->iso_frame_desc[packets].offset;
+				/* Buffer for this packet */
+				buff_dma =
+					(u32) ((unsigned char *) urb->
+						transfer_buffer + offset);
+
+				/*for the case mult is 2 or 3 */
+				length = 0;
+				for (i = packets; i < packets + mult; i++) {
+					length += urb->iso_frame_desc[i].length;
+				}
+				itd->hw_transaction[iMicroIndex] =
+					EHCI_ISOC_ACTIVE | (length & 
+					EHCI_ITD_TRANLENGTH)
+					<< 16 | iPG << 12 | buff_dma;
+					
+				if (itd->hw_bufp[iPG] != buff_dma){
+					itd->hw_bufp[++iPG] = buff_dma;
+				}
+
+				iso_dbg(ISO_DBG_DATA,
+					"[%s] offset : %ld buff_dma : 0x%08x length : %ld\n",
+					__FUNCTION__, offset,
+					(unsigned int) buff_dma, length);
+
+				itd->ssplit |= 1 << iMicroIndex;
+				packets++;
+				iMicroIndex += urb->interval;
+				iNumofSlots--;
+
+				/*last packets or last slot */
+				if (packets == urb->number_of_packets
+					|| iNumofSlots == 0) {
+
+					itd->hw_transaction[iMicroIndex] |=
+						EHCI_ITD_IOC;
+
+					break;
+					
+				}
+			}
+
+			/*
+			 * If this SITD is not the head/root SITD, link this SITD to the SITD
+			 * that came before it.
+			 */
+			if (prev_itd) {
+				prev_itd->hw_next = (u32) (itd);
+			}
+
+			prev_itd = itd;
+
+			/*
+			 * Allocate an ISO PTD from the ISO PTD map list and
+			 * set the equivalent bit of the allocated PTD to active
+			 * in the bitmap so that this PTD will be included into
+			 * the periodic schedule
+			 */
+
+
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso]: ITD index %d\n",
+				itd->framenumber);
+			phcd_iso_get_itd_ptd_index(hcd, itd);
+			iso_dbg(ISO_DBG_DATA,
+				"[phcd_submit_iso]: ITD index %d\n",
+				itd->itd_index);
+
+			/*if we dont have any space left */
+			if (itd->itd_index == TD_PTD_INV_PTD_INDEX) {
+				*status = -ENOSPC;
+				if (((int) (qhead->next_uframe - iNumofPTDs)) <
+					0){
+					/*plus max PTDs*/
+					qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;	
+				}
+				qhead->next_uframe -= iNumofPTDs;
+
+				/* Handle SITD list cleanup */
+				if (urb->hcpriv) {
+					phcd_iso_itd_free_list(hcd, urb,
+							       *status);
+				}
+				return *status;
+			}
+
+			sitd_itd_list = &qhead->periodic_list.sitd_itd_head;
+			list_add_tail(&itd->itd_list, sitd_itd_list);
+			qhead->periodic_list.high_speed = 1;
+			qhead->periodic_list.ptdlocation |=
+				0x1 << itd->itd_index;
+
+			/* Inidcate that a new SITD have been scheduled */
+			hcd->periodic_sched++;
+
+			/* Determine if there are any ITD scheduled before this one. */
+			if (urb->hcpriv == 0){
+				urb->hcpriv = itd;
+			}
+			iPTDIndex++;
+
+		}		/*end of while */
+	}
+
+	/*end of HIGH SPEED */
+	/* Last td of current transaction */
+	if (high_speed == 0){
+		sitd->hw_next = EHCI_LIST_END;
+	}
+	urb->error_count = 0;
+	return *status;
+}				/* phcd_submit_iso */
+#endif /* CONFIG_ISO_SUPPORT */
diff --git a/drivers/usb/host/pehci/host/mem.c b/drivers/usb/host/pehci/host/mem.c
new file mode 100644
index 0000000..dbf28a9
--- /dev/null
+++ b/drivers/usb/host/pehci/host/mem.c
@@ -0,0 +1,355 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file. Memory initialization, allocation, and 
+* deallocation are handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifdef CONFIG_ISO_SUPPORT
+
+/*memory utilization fuctions*/
+void
+phci_hcd_mem_init(void)
+{
+	int i = 0;
+	u32 start_addr = 0x1000;
+	struct isp1763_mem_addr *memaddr;
+	for (i = 0; i < BLK_TOTAL; i++) {
+		memaddr = &memalloc[i];
+		memset(memaddr, 0, sizeof *memaddr);
+	}
+	/*initialize block of 128bytes */
+	for (i = 0; i < BLK_128_; i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_128;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_128;
+	}
+	/*initialize block of 256bytes */
+	for (i = BLK_128_; i < BLK_256_; i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_256;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_256;
+	}
+	/*initialize block of 1024bytes */
+	for (i = BLK_128_ + BLK_256_; i < (BLK_128_ + BLK_256_ + BLK_1024_);
+		i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_1024;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_1024;
+	}
+
+	/*initialize block of  2kbytes */
+	for (i = (BLK_128_ + BLK_256_ + BLK_1024_);
+		i < (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_); i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_2048;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_2048;
+	}
+	/* initialize block of 4kbytes */
+	for (i = (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_);
+		i < (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_ + BLK_4096_); 
+		i++){
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_4096;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_4096;
+	}
+	/* initialize block of 8kbytes */
+	for (i = (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_); i <
+		(BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_ + BLK_4096_ +
+		BLK_8196_); i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_8192;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_8192;
+	}
+
+}
+
+
+/*free memory*/
+static void
+phci_hcd_mem_free(struct isp1763_mem_addr *memptr)
+{
+	/*block number to be freed */
+	int block = memptr->blk_num;
+
+	if (block < BLK_TOTAL){
+		if ((memptr->blk_size) && (memalloc[block].used != 0)) {
+			memalloc[block].used = 0;
+			memptr->used = 0;
+		}
+	}
+}
+
+
+/*allocate memory*/
+static void
+phci_hcd_mem_alloc(u32 size, struct isp1763_mem_addr *memptr, u32 flag)
+{
+	u32 blk_size = size;
+	u16 i;
+	u32 nextblk1 = 0, nextblk4 = 0;
+	u32 start = 0, end = 0;
+	struct isp1763_mem_addr *memaddr = 0;
+
+	memset(memptr, 0, sizeof *memptr);
+
+	pehci_print("phci_hcd_mem_alloc(size = %d)\n", size);
+
+	if (blk_size == 0) {
+		memptr->phy_addr = 0;
+		memptr->virt_addr = 0;
+		memptr->blk_size = 0;
+		memptr->num_alloc = 0;
+		memptr->blk_num = 0;
+		return;
+	}
+
+	for (i = 0; i < BLK_TOTAL; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used && size <= memaddr->blk_size) {
+			memaddr->used = 1;
+			memptr->used = 1;
+			memptr->blk_num = i;
+			memptr->blk_size = memaddr->blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+	return;
+	/*end of the 1k blocks */
+	nextblk1 = BLK_256_ + BLK_1024_;
+	/*end of the 4k blocks */
+	nextblk4 = nextblk1 + BLK_4096_;
+
+	if (blk_size <= BLK_SIZE_128) {
+		blk_size = BLK_SIZE_128;
+		start = 0;
+		end = BLK_256_;
+	}
+	if (blk_size <= BLK_SIZE_256) {
+		blk_size = BLK_SIZE_256;
+		start = 0;
+		end = BLK_256_;
+	} else if (blk_size <= BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_1024;
+		start = BLK_256_;
+		end = start + BLK_1024_;
+	} else if (blk_size > BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_4096;
+		start = BLK_256_ + BLK_1024_;
+		end = start + BLK_4096_;
+	}
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->blk_num = i;
+			memptr->used = 1;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+	/*look for in the next block if memory is free */
+	/*start from the first place of the next block */
+	start = end;
+
+	/*for 1k and 256 size request only 4k can be returned */
+	end = nextblk4;
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->used = 1;
+			memptr->blk_num = i;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+}
+
+#else
+
+void
+phci_hcd_mem_init(void)
+{
+	int i = 0;
+	u32 start_addr = 0x1000;
+	struct isp1763_mem_addr *memaddr;
+	for (i = 0; i < BLK_TOTAL; i++) {
+		memaddr = &memalloc[i];
+		memset(memaddr, 0, sizeof *memaddr);
+	}
+
+	/*initialize block of 256bytes */
+	for (i = 0; i < BLK_256_; i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_256;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_256;
+	}
+	/*initialize block of 1024bytes */
+	for (i = BLK_256_; i < (BLK_256_ + BLK_1024_); i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_1024;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_1024;
+	}
+
+	/*initialize block of  4kbytes */
+	for (i = (BLK_256_ + BLK_1024_); i < (BLK_256_ + BLK_1024_ + BLK_4096_);
+		i++) {
+		memaddr = &memalloc[i];
+		memaddr->blk_num = i;
+		memaddr->used = 0;
+		memaddr->blk_size = BLK_SIZE_4096;
+		memaddr->phy_addr = start_addr;
+		start_addr += BLK_SIZE_4096;
+	}
+
+}
+
+
+/*free memory*/
+static void
+phci_hcd_mem_free(struct isp1763_mem_addr *memptr)
+{
+	/*block number to be freed */
+	int block = memptr->blk_num;
+
+	if (block < BLK_TOTAL)
+		if ((memptr->blk_size) && (memalloc[block].used != 0)) {
+			memalloc[block].used = 0;
+			memptr->used = 0;
+		}
+}
+
+
+/*allocate memory*/
+static void
+phci_hcd_mem_alloc(u32 size, struct isp1763_mem_addr *memptr, u32 flag)
+{
+	u32 blk_size = size;
+	u16 i;
+	u32 nextblk1 = 0, nextblk4 = 0;
+	u32 start = 0, end = 0;
+	struct isp1763_mem_addr *memaddr = 0;
+
+	memset(memptr, 0, sizeof *memptr);
+
+	pehci_print("phci_hcd_mem_alloc(size = %d)\n", size);
+
+	if (blk_size == 0) {
+		memptr->phy_addr = 0;
+		memptr->virt_addr = 0;
+		memptr->blk_size = 0;
+		memptr->num_alloc = 0;
+		memptr->blk_num = 0;
+		return;
+	}
+
+	/*end of the 1k blocks */
+	nextblk1 = BLK_256_ + BLK_1024_;
+	/*end of the 4k blocks */
+	nextblk4 = nextblk1 + BLK_4096_;
+
+
+	if (blk_size <= BLK_SIZE_256) {
+		blk_size = BLK_SIZE_256;
+		start = 0;
+		end = BLK_256_;
+	} else if (blk_size <= BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_1024;
+		start = BLK_256_;
+		end = start + BLK_1024_;
+	} else if (blk_size > BLK_SIZE_1024) {
+		blk_size = BLK_SIZE_4096;
+		start = BLK_256_ + BLK_1024_;
+		end = start + BLK_4096_;
+	}
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->blk_num = i;
+			memptr->used = 1;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+	/*look for in the next block if memory is free */
+	/*start from the first place of the next block */
+	start = end;
+
+	/*for 1k and 256 size request only 4k can be returned */
+	end = nextblk4;
+
+	for (i = start; i < end; i++) {
+		memaddr = &memalloc[i];
+		if (!memaddr->used) {
+			memaddr->used = 1;
+			memptr->used = 1;
+			memptr->blk_num = i;
+			memptr->blk_size = blk_size;
+			memptr->phy_addr = memaddr->phy_addr;
+			memptr->virt_addr = memptr->phy_addr;
+			return;
+		}
+	}
+
+}
+
+#endif
diff --git a/drivers/usb/host/pehci/host/otg.c b/drivers/usb/host/pehci/host/otg.c
new file mode 100644
index 0000000..546d9e9
--- /dev/null
+++ b/drivers/usb/host/pehci/host/otg.c
@@ -0,0 +1,189 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file. OTG related events are handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+/*hub device which connected with root port*/
+struct usb_device *hubdev = 0;
+/* hub interrupt urb*/
+struct urb *huburb;
+
+/*return otghub from here*/
+struct usb_device *
+phci_register_otg_device(struct isp1763_dev *dev)
+{
+	printk("OTG dev %x %d\n",(u32) hubdev, hubdev->devnum);
+	if (hubdev && hubdev->devnum >= 0x2) {
+		return hubdev;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(phci_register_otg_device);
+
+/*suspend the otg port(0)
+ * needed when port is switching
+ * from host to device
+ * */
+int
+phci_suspend_otg_port(struct isp1763_dev *dev, u32 command)
+{
+	int status = 0;
+	hubdev->otgstate = USB_OTG_SUSPEND;
+	if (huburb->status == -EINPROGRESS) {
+		huburb->status = 0;
+	}
+
+	huburb->status = 0;
+	huburb->complete(huburb);
+	return status;
+}
+EXPORT_SYMBOL(phci_suspend_otg_port);
+
+/*set the flag to enumerate the device*/
+int
+phci_enumerate_otg_port(struct isp1763_dev *dev, u32 command)
+{
+	/*set the flag to enumerate */
+	/*connect change interrupt will happen from
+	 * phci_intl_worker only
+	 * */
+	hubdev->otgstate = USB_OTG_ENUMERATE;
+	if (huburb->status == -EINPROGRESS) {
+		huburb->status = 0;
+	}
+	/*complete the urb */
+
+	huburb->complete(huburb);
+
+	/*reset the otghub urb status */
+	huburb->status = -EINPROGRESS;
+	return 0;
+}
+EXPORT_SYMBOL(phci_enumerate_otg_port);
+
+/*host controller resume sequence at otg port*/
+int
+phci_resume_otg_port(struct isp1763_dev *dev, u32 command)
+{
+	printk("Resume is called\n");
+	hubdev->otgstate = USB_OTG_RESUME;
+	if (huburb->status == -EINPROGRESS) {
+		huburb->status = 0;
+	}
+	/*complete the urb */
+
+	huburb->complete(huburb);
+
+	/*reset the otghub urb status */
+	huburb->status = -EINPROGRESS;
+	return 0;
+}
+EXPORT_SYMBOL(phci_resume_otg_port);
+/*host controller remote wakeup sequence at otg port*/
+int
+phci_remotewakeup(struct isp1763_dev *dev)
+{
+    printk("phci_remotewakeup_otg_port is called\n");
+    hubdev->otgstate = USB_OTG_REMOTEWAKEUP;
+    if(huburb->status == -EINPROGRESS)
+        huburb->status = 0;
+    /*complete the urb*/
+#if ((defined LINUX_269) || defined (LINUX_2611))
+    huburb->complete(huburb,NULL);      
+#else
+	 huburb->complete(huburb);
+#endif
+    /*reset the otghub urb status*/
+    huburb->status = -EINPROGRESS;
+    return 0;
+}
+EXPORT_SYMBOL(phci_remotewakeup);
+
+/*host controller wakeup sequence at otg port*/
+int
+phci_resume_wakeup(struct isp1763_dev *dev)
+{
+    printk("phci_wakeup_otg_port is called\n");
+#if 0
+    hubdev->otgstate = USB_OTG_WAKEUP_ALL;
+    if(huburb->status == -EINPROGRESS)
+#endif
+        huburb->status = 0;
+    /*complete the urb*/
+#if ((defined LINUX_269) || defined (LINUX_2611))
+    huburb->complete(huburb,NULL);      
+#else
+	 huburb->complete(huburb);
+#endif
+    /*reset the otghub urb status*/
+    huburb->status = -EINPROGRESS;
+    return 0;
+}
+EXPORT_SYMBOL(phci_resume_wakeup);
+
+struct isp1763_driver *host_driver;
+struct isp1763_driver *device_driver;
+
+void
+pehci_delrhtimer(struct isp1763_dev *dev)
+{
+
+	struct usb_hcd *usb_hcd =
+		container_of(huburb->dev->parent->bus, struct usb_hcd, self);
+	del_timer_sync(&usb_hcd->rh_timer);
+	del_timer(&usb_hcd->rh_timer);
+
+}
+EXPORT_SYMBOL(pehci_delrhtimer);
+
+int
+pehci_Deinitialize(struct isp1763_dev *dev)
+{
+	dev -= 2;
+	if (dev->index == 0) {
+		if (dev->driver) {
+			if (dev->driver->powerdown) {
+				dev->driver->powerdown(dev);
+			}
+		}
+	}
+return 0;
+}
+EXPORT_SYMBOL(pehci_Deinitialize);
+
+int
+pehci_Reinitialize(struct isp1763_dev *dev)
+{
+
+	dev -= 2;
+	if (dev->index == 0) {
+		if(dev->driver->powerup){
+			dev->driver->powerup(dev);
+		}
+	}
+return 0;
+}
+EXPORT_SYMBOL(pehci_Reinitialize);
+
+
diff --git a/drivers/usb/host/pehci/host/pehci.c b/drivers/usb/host/pehci/host/pehci.c
new file mode 100644
index 0000000..19e9441
--- /dev/null
+++ b/drivers/usb/host/pehci/host/pehci.c
@@ -0,0 +1,6567 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* Refer to the follwing files in ~/drivers/usb/host for copyright owners:
+* ehci-dbg.c, ehci-hcd.c, ehci-hub.c, ehci-mem.c, ehci-q.c and ehic-sched.c (kernel version 2.6.9)
+* Code is modified for ST-Ericsson product 
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <stdarg.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+
+#include "../hal/isp1763.h"
+#include "pehci.h"
+#include "../hal/hal_intf.h"
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+extern int HostComplianceTest;
+extern int HostTest;
+extern int No_Data_Phase;
+extern int No_Status_Phase;
+#define	EHCI_TUNE_CERR		3
+#define	URB_NO_INTERRUPT	0x0080
+#define	EHCI_TUNE_RL_TT		0
+#define	EHCI_TUNE_MULT_TT	1
+#define	EHCI_TUNE_RL_HS		0
+#define	EHCI_TUNE_MULT_HS	1
+
+
+#define POWER_DOWN_CTRL_NORMAL_VALUE	0xffff1ba0
+#define POWER_DOWN_CTRL_SUSPEND_VALUE	0xffff08b0
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+//This macro is not supported in linux-2.6.35
+#define	USB_PORT_FEAT_HIGHSPEED 10
+#endif
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define	FALSE 0
+#define	TRUE (!FALSE)
+extern void *phcd_iso_sitd_to_ptd(phci_hcd * hcd,
+	struct ehci_sitd *sitd,
+	struct urb *urb, void *ptd);
+extern void *phcd_iso_itd_to_ptd(phci_hcd * hcd,
+	struct	ehci_itd *itd,
+	struct	urb *urb, void *ptd);
+
+extern unsigned	long phcd_submit_iso(phci_hcd *	hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+	struct urb *urb, unsigned long *status);
+void pehci_hcd_iso_schedule(phci_hcd * hcd, struct urb *);
+unsigned long lgFrameIndex = 0;
+unsigned long lgScheduledPTDIndex = 0;
+int igNumOfPkts = 0;
+#endif /* CONFIG_ISO_SUPPORT */
+
+struct isp1763_dev *isp1763_hcd;
+
+#ifdef HCD_PACKAGE
+/*file operation*/
+struct fasync_struct *fasync_q;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map, struct pt_regs *regs);
+#else
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map);
+#endif
+
+#include "otg.c"  /*OTG and HCD package needs it */
+
+
+int hcdpowerdown = 0;
+int portchange=0; //for remotewakeup
+EXPORT_SYMBOL(hcdpowerdown);
+unsigned char otg_se0_enable;
+EXPORT_SYMBOL(otg_se0_enable);
+
+
+/*Enable all other interrupt.*/
+
+#ifdef MSEC_INT_BASED
+#ifdef THREAD_BASED//This is to test interrupt mapping problem
+//#define	INTR_ENABLE_MASK (HC_OPR_REG_INT|HC_CLK_RDY_INT )
+#define INTR_ENABLE_MASK (/*HC_MSEC_INT |*/ HC_INTL_INT | HC_ATL_INT| HC_ISO_INT /*| HC_EOT_INT | HC_ISO_INT*/)
+#else
+#define	INTR_ENABLE_MASK (HC_MSEC_INT|HC_OPR_REG_INT|HC_CLK_RDY_INT )
+#endif
+#else
+#define	INTR_ENABLE_MASK ( HC_INTL_INT | HC_ATL_INT |HC_ISO_INT| HC_EOT_INT|HC_OPR_REG_INT|HC_CLK_RDY_INT)
+#endif
+
+
+
+#ifdef THREAD_BASED
+
+#define NO_SOF_REQ_IN_TSK 		0x1
+#define NO_SOF_REQ_IN_ISR 		0x2
+#define NO_SOF_REQ_IN_REQ 	0x3
+#define MSEC_INTERVAL_CHECKING 5
+
+typedef struct _st_UsbIt_Msg_Struc {
+	struct usb_hcd 		*usb_hcd;
+	u8				uIntStatus;
+	struct list_head 		list;
+} st_UsbIt_Msg_Struc, *pst_UsbIt_Msg_Struc ;
+
+typedef struct _st_UsbIt_Thread {
+    wait_queue_head_t       	ulThrdWaitQhead;
+    int                           		lThrdWakeUpNeeded;
+    struct task_struct           	*phThreadTask;
+    spinlock_t              lock;
+} st_UsbIt_Thread, *pst_UsbIt_Thread;
+
+st_UsbIt_Thread g_stUsbItThreadHandler;
+
+st_UsbIt_Msg_Struc 	g_messList;
+st_UsbIt_Msg_Struc 	g_enqueueMessList;
+spinlock_t              	enqueue_lock;
+
+int pehci_hcd_process_irq_it_handle(struct usb_hcd* usb_hcd_);
+int pehci_hcd_process_irq_in_thread(struct usb_hcd *usb_hcd_);
+
+#endif /*THREAD_BASED*/
+
+#ifdef THREAD_BASED
+phci_hcd *g_pehci_hcd;
+#endif
+
+
+struct wake_lock pehci_wake_lock;
+
+/*---------------------------------------------------
+ *    Globals for EHCI
+ -----------------------------------------------------*/
+
+/* used	when updating hcd data */
+static spinlock_t hcd_data_lock	= SPIN_LOCK_UNLOCKED;
+
+static const char hcd_name[] = "ST-Ericsson ISP1763";
+static td_ptd_map_buff_t td_ptd_map_buff[TD_PTD_TOTAL_BUFF_TYPES];	/* td-ptd map buffer for all 1362 buffers */
+
+static u8 td_ptd_pipe_x_buff_type[TD_PTD_TOTAL_BUFF_TYPES] = {
+	TD_PTD_BUFF_TYPE_ATL,
+	TD_PTD_BUFF_TYPE_INTL,
+	TD_PTD_BUFF_TYPE_ISTL
+};
+
+
+/*global memory	blocks*/
+isp1763_mem_addr_t memalloc[BLK_TOTAL];
+#include "mem.c"
+#include "qtdptd.c"
+
+#ifdef CONFIG_ISO_SUPPORT
+#include "itdptd.c"
+#endif /* CONFIG_ISO_SUPPORT */
+
+static int
+pehci_rh_control(struct	usb_hcd	*usb_hcd, u16 typeReq,
+		 u16 wValue, u16 wIndex, char *buf, u16	wLength);
+
+static int pehci_bus_suspend(struct usb_hcd *usb_hcd);
+static int pehci_bus_resume(struct usb_hcd *usb_hcd);
+/*----------------------------------------------------*/
+static void
+pehci_complete_device_removal(phci_hcd * hcd, struct ehci_qh *qh)
+{
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *td_ptd_buff;
+	struct urb * urb;
+	urb_priv_t *urb_priv;
+	struct ehci_qtd	*qtd = 0;
+//	struct usb_hcd *usb_hcd=&hcd->usb_hcd;
+	u16 skipmap=0;
+
+	if (qh->type ==	TD_PTD_BUFF_TYPE_ISTL) {
+#ifdef COMMON_MEMORY
+		phci_hcd_mem_free(&qh->memory_addr);
+#endif
+		return;
+	}
+
+	td_ptd_buff = &td_ptd_map_buff[qh->type];
+	td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+
+	/*this flag should only	be set when device is going */
+	td_ptd_map->state = TD_PTD_REMOVE;
+	/*if nothing there */
+	if (list_empty(&qh->qtd_list)) {
+		if (td_ptd_map->state != TD_PTD_NEW) {
+			phci_hcd_release_td_ptd_index(qh);
+		}
+		qha_free(qha_cache, qh);
+		qh = 0;
+		return;
+	} else {
+	
+		if(!list_empty(&qh->qtd_list)){
+				qtd=NULL;
+				qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list);
+				if(qtd){
+					urb=qtd->urb;
+					urb_priv= urb->hcpriv;
+					
+					if(urb)
+					switch (usb_pipetype(urb->pipe)) {
+						case PIPE_CONTROL:
+						case PIPE_BULK:
+							break;
+						case PIPE_INTERRUPT:
+							td_ptd_buff = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL];
+							td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+
+							/*urb is already been removed */
+						//	if (td_ptd_map->state == TD_PTD_NEW) {
+						//		kfree(urb_priv);
+						//		break;
+						//	}
+
+							/* These TDs are not pending anymore */
+							td_ptd_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+							td_ptd_map->state = TD_PTD_REMOVE;
+							urb_priv->state	|= DELETE_URB;
+
+							/*read the skipmap, to see if this transfer has	to be rescheduled */
+							skipmap	=
+							isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+							skipmap);
+
+							isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap,
+							skipmap | td_ptd_map->ptd_bitmap);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+							pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+							pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+							break;
+					}
+
+					
+				}else{
+					//break;
+				}
+		}
+		qha_free(qha_cache, qh);
+		qh = 0;
+		return;
+	}
+	/*MUST not come	down below this	*/
+	err("Never Error: Should not come to this portion of code\n");
+
+	return;
+}
+
+/*functions looks for the values in register
+  specified in ptr, if register	values masked
+  with the mask	and result is equal to done,
+  operation is successful else fails with timeout*/
+static int
+pehci_hcd_handshake(phci_hcd * hcd, u32	ptr, u32 mask, u32 done, int usec)
+{
+	u32 result = 0;
+	do {
+		result = isp1763_reg_read16(hcd->dev, ptr, result);
+		printk(KERN_NOTICE "Registr %x val is %x\n", ptr, result);
+		if (result == ~(u32) 0)	{/* card removed */
+			return -ENODEV;
+		}
+		result &= mask;
+		if (result == done) {
+			return 0;
+		}
+		udelay(1);
+		usec--;
+	} while	(usec >	0);
+
+	return -ETIMEDOUT;
+}
+
+#ifndef	MSEC_INT_BASED
+/*schedule atl and interrupt tds,
+  only when we are not running on sof interrupt
+ */
+static void
+pehci_hcd_td_ptd_submit_urb(phci_hcd * hcd, struct ehci_qh *qh,	u8 bufftype)
+{
+	unsigned long flags=0;
+	struct ehci_qtd	*qtd = 0;
+	struct urb *urb	= 0;
+	struct _isp1763_qha *qha = 0;
+	u16 location = 0;
+	u16 skipmap = 0;
+	u16 buffstatus = 0;
+	u16 ormask = 0;
+	u16 intormask =	0;
+	u32 length = 0;
+	struct list_head *head;
+
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct isp1763_mem_addr	*mem_addr = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_print("Buuffer type %d\n", bufftype);
+
+	spin_lock_irqsave(&hcd->lock, flags);
+	ptd_map_buff = &td_ptd_map_buff[bufftype];
+
+	qha = &hcd->qha;
+
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+					   skipmap);
+
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+					   skipmap);
+
+		intormask =
+			isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or,
+					   intormask);
+		break;
+	default:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap,
+					   skipmap);
+		break;
+
+	}
+
+
+	buffstatus =
+		isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,
+				   buffstatus);
+
+	/*header, qtd, and urb of current transfer */
+	location = qh->qtd_ptd_index;
+	td_ptd_map = &ptd_map_buff->map_list[location];
+
+	if (!(qh->qh_state & QH_STATE_TAKE_NEXT)) {
+		pehci_check("qh	will schdule from interrupt routine,map	%x\n",
+			    td_ptd_map->ptd_bitmap);
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		return;
+	}
+	head = &qh->qtd_list;
+	qtd = list_entry(head->next, struct ehci_qtd, qtd_list);
+
+	/*already scheduled, may be from interrupt */
+	if (!(qtd->state & QTD_STATE_NEW)) {
+		pehci_check("qtd already in, state %x\n", qtd->state);
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		return;
+	}
+
+	qtd->state &= ~QTD_STATE_NEW;
+	qtd->state |= QTD_STATE_SCHEDULED;
+
+	qh->qh_state &=	~QH_STATE_TAKE_NEXT;
+	/*take the first td */
+	td_ptd_map->qtd	= qtd;
+	/*take the urb */
+	urb = qtd->urb;
+	ptd_map_buff->active_ptds++;
+
+	/*trust	the atl	worker,	at this	location there wont be any td */
+	/*if this td is	the last one */
+	if (qtd->state & QTD_STATE_LAST) {
+		qh->hw_current = cpu_to_le32(0);
+		/*else update the hw_next of qh	to the next td */
+	} else {
+		qh->hw_current = qtd->hw_next;
+	}
+	memset(qha, 0, sizeof(isp1763_qha));
+
+	pehci_check("td	being scheduled	: length: %d, device: %d, map: %x\n",
+		    qtd->length, urb->dev->devnum, td_ptd_map->ptd_bitmap);
+	/*NEW, now need	to get the memory for this transfer */
+	length = qtd->length;
+	mem_addr = &qtd->mem_addr;
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		err("Never Error: Can not allocate memory for the current td,length %d\n", length);
+		/*should not happen */
+		/*can happen only when we exceed the limit of devices we support
+		   MAX 4 mass storage at a time	*/
+	}
+	phci_hcd_qha_from_qtd(hcd, qtd, qtd->urb, (void *) qha,
+		td_ptd_map->ptd_ram_data_addr, qh);
+	if (qh->type ==	TD_PTD_BUFF_TYPE_INTL) {
+		phci_hcd_qhint_schedule(hcd, qh, qtd, (isp1763_qhint *)	qha,
+					qtd->urb);
+	}
+	/*write	qha into the header of the host	controller */
+	isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0,
+			  (u32 *) (qha), PHCI_QHA_LENGTH, 0);
+
+	/*if this is SETUP/OUT token , then need to write into the buffer */
+	/*length should	be valid and supported by the ptd */
+	if (qtd->length && (qtd->length <= HC_ATL_PL_SIZE)){
+		switch (PTD_PID(qha->td_info2))	{
+		case OUT_PID:
+		case SETUP_PID:
+
+			isp1763_mem_write(hcd->dev, (u32) mem_addr->phy_addr, 0,
+					  (void	*) qtd->hw_buf[0], length, 0);
+
+
+#if 0
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+#endif
+
+			
+
+			break;
+		}
+	}
+
+	/*unskip the tds at this location */
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+		skipmap	&= ~td_ptd_map->ptd_bitmap;
+		/*enable atl interrupts	on donemap */
+		ormask |= td_ptd_map->ptd_bitmap;
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+				    ormask);
+		break;
+
+	case TD_PTD_BUFF_TYPE_INTL:
+		skipmap	&= ~td_ptd_map->ptd_bitmap;
+		intormask |= td_ptd_map->ptd_bitmap;
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or,
+				    intormask);
+		break;
+
+	case TD_PTD_BUFF_TYPE_ISTL:
+		skipmap	&= ~td_ptd_map->ptd_bitmap;
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+		break;
+	}
+
+	/*if any new schedule, enable the atl buffer */
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				    buffstatus | ATL_BUFFER);
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+		buffstatus |= ATL_BUFFER;
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				    buffstatus | INT_BUFFER);
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap);
+		break;
+	case TD_PTD_BUFF_TYPE_ISTL:
+		/*not supposed to be seen here */
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				    buffstatus | ISO_BUFFER);
+		break;
+	}
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return;
+
+}
+#endif
+
+
+
+#ifdef MSEC_INT_BASED
+/*schedule next	(atl/int)tds and any pending tds*/
+static void
+pehci_hcd_schedule_pending_ptds(phci_hcd * hcd, u16 donemap, u8 bufftype,
+				u16 only)
+{
+	struct ehci_qtd	*qtd = 0;
+	struct ehci_qh *qh = 0;
+	struct list_head *qtd_list = 0;
+	struct _isp1763_qha allqha;
+	struct _isp1763_qha *qha = 0;
+	u16 mask = 0x1,	index =	0;
+	u16 location = 0;
+	u16 skipmap = 0;
+	u32 newschedule	= 0;
+	u16 buffstatus = 0;
+	u16 schedulemap	= 0;
+#ifndef	CONFIG_ISO_SUPPORT
+	u16 lasttd = 1;
+#endif
+	u16 lastmap = 0;
+	struct urb *urb	= 0;
+	urb_priv_t *urbpriv = 0;
+	int length = 0;
+	u16 ormask = 0,	andmask	= 0;
+	u16 intormask =	0;
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct isp1763_mem_addr	*mem_addr = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_print("Buffer type %d\n",	bufftype);
+
+	/*need to hold this lock if another interrupt is comming
+	   for previously scheduled transfer, while scheduling new tds
+	 */
+	spin_lock(&hcd_data_lock);
+	ptd_map_buff = &td_ptd_map_buff[bufftype];
+	qha = &allqha;
+	switch (bufftype) {
+	case TD_PTD_BUFF_TYPE_ATL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+					   skipmap);
+		rmb();
+
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+
+		andmask	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_and,
+					   andmask);
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+					   skipmap);
+		/*read the interrupt mask registers */
+
+		intormask =
+			isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or,
+					   intormask);
+		break;
+	default:
+		err("Never Error: Bogus	type of	bufer\n");
+		return;
+	}
+
+	buffstatus =
+		isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,
+				   buffstatus);
+	/*td headers need attention */
+	schedulemap = donemap;
+	while (schedulemap) {
+		index =	schedulemap & mask;
+		schedulemap &= ~mask;
+		mask <<= 1;
+
+		if (!index) {
+			location++;
+			continue;
+		}
+
+		td_ptd_map = &ptd_map_buff->map_list[location];
+		/*	can happen if donemap comes after
+		   removal of the urb and associated tds
+		 */
+		if ((td_ptd_map->state == TD_PTD_NEW) ||
+			(td_ptd_map->state == TD_PTD_REMOVE)) {
+			qh = td_ptd_map->qh;
+			pehci_check
+				("should not come here,	map %x,pending map %x\n",
+				 td_ptd_map->ptd_bitmap,
+				 ptd_map_buff->pending_ptd_bitmap);
+
+			pehci_check("buffer type %s\n",
+				(bufftype == 0) ? "ATL" : "INTL");
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*clear	the pending map	*/
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*no endpoint at this location */
+		if (!(td_ptd_map->qh)) {
+			err("queue head	can not	be null	here\n");
+			/*move to the next location */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*current endpoint */
+		qh = td_ptd_map->qh;
+		if (!(skipmap &	td_ptd_map->ptd_bitmap)) {
+			/*should not happen, if	happening, then	*/
+			pehci_check("buffertype	%d,td_ptd_map %x,skipnap %x\n",
+				    bufftype, td_ptd_map->ptd_bitmap, skipmap);
+			lastmap	= td_ptd_map->ptd_bitmap;
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*if we	processed all the tds in ths transfer */
+		if (td_ptd_map->lasttd)	{
+			err("should not	show  map %x,qtd %p\n",
+			td_ptd_map->ptd_bitmap, td_ptd_map->qtd);
+			/*this can happen in case the transfer is not being
+			 * procesed by the host	, tho the transfer is there
+			 * */
+			qh->hw_current = cpu_to_le32(td_ptd_map->qtd);
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*if we	have ptd that is going for reload */
+		if ((td_ptd_map->qtd) && (td_ptd_map->state & TD_PTD_RELOAD)) {
+			warn("%s: reload td\n",	__FUNCTION__);
+			td_ptd_map->state &= ~TD_PTD_RELOAD;
+			qtd = td_ptd_map->qtd;
+			goto loadtd;
+		}
+
+		/* qh is there but no qtd so it	means fresh transfer */
+		if ((td_ptd_map->qh) &&	!(td_ptd_map->qtd)) {
+			if (list_empty(&qh->qtd_list)) {
+				/*should not hapen again, as it	comes here
+				   when	it has td in its map
+				 */
+				pehci_check
+					("must not come	here any more, td map %x\n",
+					 td_ptd_map->ptd_bitmap);
+				/*this location	is idle	and can	be free	next time if
+				   no new transfers are	comming	for this */
+				donemap	&= ~td_ptd_map->ptd_bitmap;
+				td_ptd_map->state |= TD_PTD_IDLE;
+				ptd_map_buff->pending_ptd_bitmap &=
+					~td_ptd_map->ptd_bitmap;
+				location++;
+				continue;
+			}
+			qtd_list = &qh->qtd_list;
+			qtd = td_ptd_map->qtd =
+				list_entry(qtd_list->next, struct ehci_qtd,
+					   qtd_list);
+			/*got the td, now goto reload */
+			goto loadtd;
+		}
+
+		/*if there is already one qtd there in the transfer */
+		if (td_ptd_map->qtd) {
+			/*new schedule */
+			qtd = td_ptd_map->qtd;
+		}
+		loadtd:
+		/*should not happen */
+		if (!qtd) {
+			err("this piece	of code	should not be executed\n");
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		ptd_map_buff->active_ptds++;
+		/*clear	the pending map	here */
+		ptd_map_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+
+
+		/*if this td is	the last one */
+		if (qtd->state & QTD_STATE_LAST) {
+			/*no qtd anymore */
+			qh->hw_current = cpu_to_le32(0);
+
+			/*else update the hw_next of qh	to the next td */
+		} else {
+			qh->hw_current = qtd->hw_next;
+		}
+
+		if (location !=	qh->qtd_ptd_index) {
+			err("Never Error: Endpoint header location and scheduling information are not same\n");
+		}
+
+		/*next location	*/
+		location++;
+		/*found	new transfer */
+		newschedule = 1;
+		/*take the urb */
+		urb = qtd->urb;
+		/*sometimes we miss due	to skipmap
+		   so to make sure that	we dont	put again the
+		   same	stuff
+		 */
+		if (!(qtd->state & QTD_STATE_NEW)) {
+			err("Never Error: We should not	put the	same stuff\n");
+			continue;
+		}
+
+		urbpriv	= (urb_priv_t *) urb->hcpriv;
+		urbpriv->timeout = 0;
+
+		/*no more new */
+		qtd->state &= ~QTD_STATE_NEW;
+		qtd->state |= QTD_STATE_SCHEDULED;
+
+
+
+		/*NEW, now need	to get the memory for this transfer */
+		length = qtd->length;
+		mem_addr = &qtd->mem_addr;
+		phci_hcd_mem_alloc(length, mem_addr, 0);
+		if (length && ((mem_addr->phy_addr == 0)
+			       || (mem_addr->virt_addr == 0))) {
+
+			err("Never Error: Can not allocate memory for the current td,length %d\n", length);
+			location++;
+			continue;
+		}
+
+		pehci_check("qtd being scheduled %p, device %d,map %x\n", qtd,
+			    urb->dev->devnum, td_ptd_map->ptd_bitmap);
+
+
+		memset(qha, 0, sizeof(isp1763_qha));
+		/*convert qtd to qha */
+		phci_hcd_qha_from_qtd(hcd, qtd,	qtd->urb, (void	*) qha,
+			td_ptd_map->ptd_ram_data_addr, qh);
+
+		if (qh->type ==	TD_PTD_BUFF_TYPE_INTL) {
+			phci_hcd_qhint_schedule(hcd, qh, qtd,
+				(isp1763_qhint *) qha,
+				qtd->urb);
+
+		}
+
+
+		length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3);
+		if (length > HC_ATL_PL_SIZE) {
+			err("Never Error: Bogus	length,length %d(max %d)\n",
+			qtd->length, HC_ATL_PL_SIZE);
+		}
+
+		/*write	qha into the header of the host	controller */
+		isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0,
+			(u32 *) (qha), PHCI_QHA_LENGTH, 0);
+
+#ifdef PTD_DUMP_SCHEDULE
+		printk("SCHEDULE next (atl/int)tds PTD header\n");
+		printk("DW0: 0x%08X\n", qha->td_info1);
+		printk("DW1: 0x%08X\n", qha->td_info2);
+		printk("DW2: 0x%08X\n", qha->td_info3);
+		printk("DW3: 0x%08X\n", qha->td_info4);
+#endif
+		
+		/*if this is SETUP/OUT token , then need to write into the buffer */
+		/*length should	be valid */
+		if (qtd->length && (length <= HC_ATL_PL_SIZE)){
+			switch (PTD_PID(qha->td_info2))	{
+			case OUT_PID:
+			case SETUP_PID:
+
+				isp1763_mem_write(hcd->dev,
+					(u32)	mem_addr->phy_addr, 0,
+					(void	*) qtd->hw_buf[0],
+					length, 0);
+#if 0
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+#endif
+
+
+
+				break;
+			}
+		}
+
+		/*unskip the tds at this location */
+		switch (bufftype) {
+		case TD_PTD_BUFF_TYPE_ATL:
+			skipmap	&= ~td_ptd_map->ptd_bitmap;
+			lastmap	= td_ptd_map->ptd_bitmap;
+			/*try to reduce	the interrupts */
+			ormask |= td_ptd_map->ptd_bitmap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+			break;
+
+		case TD_PTD_BUFF_TYPE_INTL:
+			skipmap	&= ~td_ptd_map->ptd_bitmap;
+			lastmap	= td_ptd_map->ptd_bitmap;
+			intormask |= td_ptd_map->ptd_bitmap;
+			;
+			isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or,
+					    intormask);
+			break;
+
+		case TD_PTD_BUFF_TYPE_ISTL:
+#ifdef CONFIG_ISO_SUPPORT
+			iso_dbg(ISO_DBG_INFO,
+				"Never Error: Should not come here\n");
+#else
+			skipmap	&= ~td_ptd_map->ptd_bitmap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,
+					    skipmap);
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.isotdlastmap,
+				lasttd);
+#endif /* CONFIG_ISO_SUPPORT */
+			break;
+		}
+
+
+	}
+	/*if any new schedule, enable the atl buffer */
+
+	if (newschedule) {
+		switch (bufftype) {
+		case TD_PTD_BUFF_TYPE_ATL:
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+					    buffstatus | ATL_BUFFER);
+			/*i am comming here to only those tds that has to be scheduled */
+			/*so skip map must be in place */
+			if (skipmap & donemap) {
+				pehci_check
+					("must be both ones compliment of each other\n");
+				pehci_check
+					("problem, skipmap %x, donemap %x,\n",
+					 skipmap, donemap);
+
+			}
+			skipmap	&= ~donemap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+					    skipmap);
+
+			break;
+		case TD_PTD_BUFF_TYPE_INTL:
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+					    buffstatus | INT_BUFFER);
+			skipmap	&= ~donemap;
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap,
+					    skipmap);
+			break;
+		case TD_PTD_BUFF_TYPE_ISTL:
+#ifndef	CONFIG_ISO_SUPPORT
+
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+					    buffstatus | ISO_BUFFER);
+#endif
+			break;
+		}
+	}
+	spin_unlock(&hcd_data_lock);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+#endif
+
+
+
+static void
+pehci_hcd_qtd_schedule(phci_hcd	* hcd, struct ehci_qtd *qtd,
+		       struct ehci_qh *qh, td_ptd_map_t	* td_ptd_map)
+{
+	struct urb *urb;
+	urb_priv_t *urbpriv = 0;
+	u32 length=0;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	struct _isp1763_qha *qha, qhtemp;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	if (qtd->state & QTD_STATE_SCHEDULED) {
+		return;
+	}
+	/*redundant */
+	qha = &qhtemp;
+
+	/*if this td is	the last one */
+	if (qtd->state & QTD_STATE_LAST) {
+		/*no qtd anymore */
+		qh->hw_current = cpu_to_le32(0);
+
+		/*else update the hw_next of qh	to the next td */
+	} else {
+		qh->hw_current = qtd->hw_next;
+	}
+
+	urb = qtd->urb;
+	urbpriv	= (urb_priv_t *) urb->hcpriv;
+	urbpriv->timeout = 0;
+
+	/*NEW, now need	to get the memory for this transfer */
+	length = qtd->length;
+	mem_addr = &qtd->mem_addr;
+	phci_hcd_mem_alloc(length, mem_addr, 0);
+	if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+		err("Never Error: Cannot allocate memory for the current td,length %d\n", length);
+		return;
+	}
+
+	pehci_check("newqtd being scheduled, device: %d,map: %x\n",
+		    urb->dev->devnum, td_ptd_map->ptd_bitmap);
+
+	//udelay(100);
+
+	memset(qha, 0, sizeof(isp1763_qha));
+	/*convert qtd to qha */
+	phci_hcd_qha_from_qtd(hcd, qtd,	qtd->urb, (void	*) qha,
+			      td_ptd_map->ptd_ram_data_addr, qh
+			      /*td_ptd_map->datatoggle */ );
+
+	if (qh->type ==	TD_PTD_BUFF_TYPE_INTL) {
+		phci_hcd_qhint_schedule(hcd, qh, qtd, (isp1763_qhint *)	qha,
+					qtd->urb);
+	}
+
+
+	length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3);
+	if (length > HC_ATL_PL_SIZE) {
+		err("Never Error: Bogus	length,length %d(max %d)\n",
+		qtd->length, HC_ATL_PL_SIZE);
+	}
+
+	/*write	qha into the header of the host	controller */
+	isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0,
+			  (u32 *) (qha), PHCI_QHA_LENGTH, 0);
+	
+#if 0 //def PTD_DUMP_SCHEDULE
+		printk("SCHEDULE Next qtd\n");
+		printk("DW0: 0x%08X\n", qha->td_info1);
+		printk("DW1: 0x%08X\n", qha->td_info2);
+		printk("DW2: 0x%08X\n", qha->td_info3);
+		printk("DW3: 0x%08X\n", qha->td_info4);
+#endif
+	
+	/*if this is SETUP/OUT token , then need to write into the buffer */
+	/*length should	be valid */
+	if (qtd->length && (length <= HC_ATL_PL_SIZE)){
+		switch (PTD_PID(qha->td_info2))	{
+		case OUT_PID:
+		case SETUP_PID:
+
+			isp1763_mem_write(hcd->dev, (u32) mem_addr->phy_addr, 0,
+				(void	*) qtd->hw_buf[0], length, 0);
+
+#if 0
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+#endif
+
+
+			break;
+		}
+	}
+	/*qtd is scheduled */
+	qtd->state &= ~QTD_STATE_NEW;
+	qtd->state |= QTD_STATE_SCHEDULED;
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return;
+}
+#ifdef USBNET 
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_urb_delayed_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map, struct pt_regs *regs)
+#else
+static void
+pehci_hcd_urb_delayed_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map)
+#endif
+{
+	static u32 remove = 0;
+	static u32 qh_state = 0;
+
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+#ifdef USBNET 
+	struct isp1763_async_cleanup_urb *urb_st = 0;
+#endif
+
+
+
+	urb_priv->timeout = 0;
+
+	if((td_ptd_map->state == TD_PTD_REMOVE	) ||
+		  (urb_priv->state == DELETE_URB) ||
+		     !HCD_IS_RUNNING(hcd->state)){
+	remove=1;
+	}
+	qh_state=qh->qh_state;
+	qh->qh_state = QH_STATE_COMPLETING;
+	/*remove the done tds */
+	spin_lock(&hcd_data_lock);
+	phci_hcd_urb_free_priv(hcd, urb_priv, qh);
+	spin_unlock(&hcd_data_lock);
+
+	urb_priv->timeout = 0;
+	kfree(urb_priv);
+	urb->hcpriv = 0;
+
+
+	/*if normal completion */
+	if (urb->status	== -EINPROGRESS) {
+		urb->status = 0;
+	}
+
+	if(remove)
+	if (list_empty(&qh->qtd_list)) {
+		phci_hcd_release_td_ptd_index(qh);
+	}
+	remove=0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,urb);
+#endif
+
+//if(qh_state!=QH_STATE_COMPLETING)
+{
+//	spin_unlock(&hcd->lock);
+	/* assume interrupt has been disabled and has acquired hcd->lock */
+	urb_st = (struct isp1763_async_cleanup_urb *)kmalloc(sizeof(struct isp1763_async_cleanup_urb), GFP_ATOMIC);
+	urb_st->urb = urb;
+	list_add_tail(&urb_st->urb_list, &(hcd->cleanup_urb.urb_list));
+
+//	isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, INTR_ENABLE_MASK | HC_SOF_INT);
+	isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, HC_MSOF_INT);
+//	spin_lock(&hcd->lock);
+}
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map, struct pt_regs *regs)
+#else
+static void
+pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb,
+	td_ptd_map_t * td_ptd_map)
+#endif
+{
+	static u32 remove = 0;
+	static u32 qh_state = 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+	
+	if(urb_priv==NULL){
+	printk("***************urb_priv is NULL ************ %s: Entered\n",	__FUNCTION__);
+	goto exit;
+	}
+	pehci_check("complete the td , length: %d\n", td_ptd_map->qtd->length);
+	urb_priv->timeout = 0;
+
+	if((td_ptd_map->state == TD_PTD_REMOVE	) ||
+		  (urb_priv->state == DELETE_URB) ||
+		     !HCD_IS_RUNNING(hcd->state)){
+	remove=1;
+	}
+
+
+	qh_state=qh->qh_state;
+
+	qh->qh_state = QH_STATE_COMPLETING;
+	/*remove the done tds */
+	spin_lock(&hcd_data_lock);
+	phci_hcd_urb_free_priv(hcd, urb_priv, qh);
+	spin_unlock(&hcd_data_lock);
+
+	urb_priv->timeout = 0;
+	kfree(urb_priv);
+	urb->hcpriv = 0;
+
+
+	/*if normal completion */
+	if (urb->status	== -EINPROGRESS) {
+		urb->status = 0;
+	}
+
+	if(remove)
+	if (list_empty(&qh->qtd_list)) {
+		phci_hcd_release_td_ptd_index(qh);
+	}
+	remove=0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+	{
+		usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,urb);
+	}
+#endif
+	spin_unlock(&hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+	usb_hcd_giveback_urb(&hcd->usb_hcd, urb, urb->status);
+#endif
+	spin_lock(&hcd->lock);
+exit:
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	
+}
+
+/*update the error status of the td*/
+static void
+pehci_hcd_update_error_status(u32 ptdstatus, struct urb	*urb)
+{
+	/*if ptd status	is halted */
+	if (ptdstatus &	PTD_STATUS_HALTED) {
+		if (ptdstatus &	PTD_XACT_ERROR)	{
+			/*transaction error results due	to retry count goes to zero */
+			if (PTD_RETRY(ptdstatus)) {
+				/*halt the endpoint */
+				printk("transaction error , retries %d\n",
+					PTD_RETRY(ptdstatus));
+				urb->status = -EPIPE;
+			} else {
+				printk("transaction error , retries %d\n",
+					PTD_RETRY(ptdstatus));
+				/*protocol error */
+				urb->status = -EPROTO;
+			}
+		} else if (ptdstatus & PTD_BABBLE) {
+			printk("babble error, qha %x\n", ptdstatus);
+			/*babble error */
+			urb->status = -EOVERFLOW;
+		} else if (PTD_RETRY(ptdstatus)) {
+			printk("endpoint halted with retrie remaining %d\n",
+				PTD_RETRY(ptdstatus));
+			urb->status = -EPIPE;
+		} else {	/*unknown error, i will	report it as halted, as	i will never see xact error bit	set */
+			printk("protocol error, qha %x\n", ptdstatus);
+			urb->status = -EPIPE;
+		}
+
+		/*if halted need to recover */
+		if (urb->status	== -EPIPE) {
+		}
+	}
+}
+
+#ifdef CONFIG_ISO_SUPPORT	/* New code for	ISO support */
+
+/*******************************************************************
+ * phcd_iso_handler - ISOCHRONOUS Transfer handler
+ *
+ * phci_hcd *hcd,
+ *	Host controller	driver structure which contains	almost all data
+ *	needed by the host controller driver to	process	data and interact
+ *	with the host controller.
+ *
+ * struct pt_regs *regs
+ *
+ * API Description
+ * This	is the ISOCHRONOUS Transfer handler, mainly responsible	for:
+ *  - Checking the periodic list if there are any ITDs for scheduling or
+ *    removal.
+ *  - For ITD scheduling, converting an	ITD into a PTD,	which is the data
+ *    structure	that the host contrtoller can understand and process.
+ *  - For ITD completion, checking the transfer	status and performing the
+ *    required actions depending on status.
+ *  - Freeing up memory	used by	an ITDs	once it	is not needed anymore.
+ ************************************************************************/
+void 
+pehci_hcd_iso_sitd_schedule(phci_hcd *hcd,struct urb* urb,struct ehci_sitd* sitd){
+		td_ptd_map_t *td_ptd_map;
+		td_ptd_map_buff_t *ptd_map_buff;
+		struct _isp1763_isoptd *iso_ptd;
+		u32 ormask = 0, skip_map = 0,last_map=0,buff_stat=0;
+		struct isp1763_mem_addr *mem_addr;
+		ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+		
+		/* Get the PTD allocated for this SITD. */
+		td_ptd_map =
+				&ptd_map_buff->map_list[sitd->
+					sitd_index];
+		iso_ptd = &hcd->isotd;
+		
+		memset(iso_ptd, 0,	sizeof(struct _isp1763_isoptd));
+		/* Read buffer status register to check later if the ISO buffer is
+		filled or not */
+		buff_stat =
+			isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,buff_stat);
+
+		/* Read the contents of the ISO skipmap register */
+		skip_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap,
+				skip_map);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_sitd_schedule]: Read skip map: 0x%08x\n",
+			(unsigned int) skip_map);
+
+		/* Read the contents of the ISO lastmap  register */
+		last_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdlastmap,
+			last_map);
+
+		/* Read the contents of the ISO ormask  register */
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or,
+			ormask);
+		
+		/* Create a PTD from an SITD */
+		phcd_iso_sitd_to_ptd(hcd, sitd, sitd->urb,
+				(void *) iso_ptd);	
+		/* Indicate that this SITD's PTD have been
+		filled up */
+		ptd_map_buff->pending_ptd_bitmap &=
+			~td_ptd_map->ptd_bitmap;		
+
+				/*
+				 * Place the newly initialized ISO PTD structure into
+				 the location allocated for this PTD in the ISO PTD
+				 memory region.
+				 */
+#ifdef SWAP
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd, PHCI_QHA_LENGTH, 0,
+					PTD_HED);
+#else /* NO_SWAP */
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0);
+#endif
+
+				/*
+ 				* Set this flag to avoid unlinking before
+ 				schedule at particular frame number
+				 */
+				td_ptd_map->state = TD_PTD_IN_SCHEDULE;
+
+				/*
+				 * If the length is not zero and the direction is
+				 OUT then  copy the  data to be transferred
+				 into the PAYLOAD memory area.
+				 */
+				if (sitd->length) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case OUT_PID:
+						/* Get the Payload memory
+						allocated for this PTD */
+						mem_addr = &sitd->mem_addr;
+#ifdef SWAP
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr-> phy_addr,
+							0, (u32*)
+							((sitd->hw_bufp[0])),
+							sitd->length, 0,
+							PTD_PAY);
+#else /* NO_SWAP */
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr,
+							0, (u32 *)
+							sitd->hw_bufp[0],
+							sitd->length, 0);
+#endif
+						break;
+					}
+					/* switch(PTD_PID(iso_ptd->td_info2))*/
+				}
+
+				/* if(sitd->length) */
+				/* If this is the last td, indicate to complete
+				the URB */
+				if (sitd->hw_next == EHCI_LIST_END) {
+					td_ptd_map->lasttd = 1;
+				}
+
+				/*
+				 * Clear the bit corresponding to this PTD in
+				 the skip map so that it will be processed on
+				 the next schedule traversal.
+				 */
+				skip_map &= ~td_ptd_map->ptd_bitmap;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_sitd_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map);
+
+				/*
+				 * Update the last map register to indicate
+				 that the newly created PTD is the last PTD
+				 added only if it is larger than the previous
+				 bitmap.
+				 */
+				if (last_map < td_ptd_map->ptd_bitmap) {
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.isotdlastmap,
+						td_ptd_map->ptd_bitmap);
+					iso_dbg(ISO_DBG_DATA,
+						"[pehci_hcd_iso_sitd_schedule]:Last Map: 0x%08x\n",
+						td_ptd_map->ptd_bitmap);
+				}
+
+				/*
+				 * Set the ISO_BUF_FILL bit to 1 to indicate
+				 that there is a PTD for ISO that needs to
+				 * be processed.
+				 */
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.buffer_status,
+					(buff_stat | ISO_BUFFER));
+				
+				isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,skip_map);
+		
+}
+
+/*******************************************************************
+ * phcd_iso_handler - ISOCHRONOUS Transfer handler
+ *
+ * phci_hcd *hcd,
+ *	Host controller	driver structure which contains	almost all data
+ *	needed by the host controller driver to	process	data and interact
+ *	with the host controller.
+ *
+ * struct pt_regs *regs
+ *
+ * API Description
+ * This	is the ISOCHRONOUS Transfer handler, mainly responsible	for:
+ *  - Checking the periodic list if there are any ITDs for scheduling or
+ *    removal.
+ *  - For ITD scheduling, converting an	ITD into a PTD,	which is the data
+ *    structure	that the host contrtoller can understand and process.
+ *  - For ITD completion, checking the transfer	status and performing the
+ *    required actions depending on status.
+ *  - Freeing up memory	used by	an ITDs	once it	is not needed anymore.
+ ************************************************************************/
+void
+pehci_hcd_iso_schedule(phci_hcd * hcd, struct urb *urb)
+{
+	struct list_head *sitd_itd_sched, *position;
+	struct ehci_itd *itd;
+	struct ehci_sitd *sitd;
+	td_ptd_map_t *td_ptd_map;
+	unsigned long last_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct _isp1763_isoptd *iso_ptd;
+	unsigned long buff_stat;
+	struct isp1763_mem_addr *mem_addr;
+	u32 ormask = 0, skip_map = 0;
+	u32 iNumofPkts;
+	unsigned int iNumofSlots = 0, mult = 0;
+	struct ehci_qh *qhead;
+
+	buff_stat = 0;
+	iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_schedule]: Enter\n");
+	iso_ptd = &hcd->isotd;
+
+	last_map = 0;
+	/* Check if there are any ITDs scheduled  for processing */
+	if (hcd->periodic_sched == 0) {
+		return;
+	}
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		mult = usb_maxpacket(urb->dev, urb->pipe,
+				usb_pipeout(urb->pipe));
+		mult = 1 + ((mult >> 11) & 0x3);
+		iNumofSlots = NUMMICROFRAME / urb->interval;
+		/*number of PTDs need to schedule for this PTD */
+		iNumofPkts = (urb->number_of_packets / mult) / iNumofSlots;
+		if ((urb->number_of_packets / mult) % iNumofSlots != 0){
+			/*get remainder */
+			iNumofPkts += 1;
+		}
+	} else{
+		iNumofPkts = urb->number_of_packets;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qhead = urb->hcpriv;
+#else
+	qhead = urb->ep->hcpriv;
+#endif
+	if (!qhead) {
+		iso_dbg(ISO_DBG_ENTRY,
+			"[pehci_hcd_iso_schedule]: Qhead==NULL\n");
+		return ;
+	}
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+
+	while (iNumofPkts > 0) {
+	/* Read buffer status register to check later if the ISO buffer is
+	filled or not */
+	buff_stat =
+		isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,buff_stat);
+
+		/* Read the contents of the ISO skipmap register */
+		skip_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap,
+				skip_map);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_schedule]: Read skip map: 0x%08x\n",
+			(unsigned int) skip_map);
+
+		/* Read the contents of the ISO lastmap  register */
+		last_map =
+			isp1763_reg_read16(hcd->dev, hcd->regs.isotdlastmap,
+			last_map);
+
+		/* Read the contents of the ISO ormask  register */
+		ormask = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or,
+			ormask);
+
+		/* Process ITDs linked to this frame, checking if there are any that needs to
+		be scheduled */
+		sitd_itd_sched = &qhead->periodic_list.sitd_itd_head;
+		if (list_empty(sitd_itd_sched)) {
+			iso_dbg(ISO_DBG_INFO,
+				"[pehci_hcd_iso_schedule]: ISO schedule list's empty. Nothing to schedule.\n");
+			return;
+		}
+
+		list_for_each(position, sitd_itd_sched) {
+			if (qhead->periodic_list.high_speed == 0){
+				/* Get an SITD in the list for processing */
+				sitd = list_entry(position, struct ehci_sitd,
+					sitd_list);
+				iNumofPkts--;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: SITD Index:%d\n", sitd->sitd_index);
+				if(sitd->sitd_index==TD_PTD_INV_PTD_INDEX)
+					continue;
+				/* Get the PTD allocated for this SITD. */
+				td_ptd_map =
+					&ptd_map_buff->map_list[sitd->
+					sitd_index];
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/* Create a PTD from an SITD */
+				phcd_iso_sitd_to_ptd(hcd, sitd, sitd->urb,
+					(void *) iso_ptd);
+
+				/* Indicate that this SITD's PTD have been
+				filled up */
+				ptd_map_buff->pending_ptd_bitmap &=
+					~td_ptd_map->ptd_bitmap;
+
+				/*
+				 * Place the newly initialized ISO PTD structure into
+				 the location allocated for this PTD in the ISO PTD
+				 memory region.
+				 */
+#ifdef SWAP
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd, PHCI_QHA_LENGTH, 0,
+					PTD_HED);
+#else /* NO_SWAP */
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0);
+#endif
+
+				/*
+ 				* Set this flag to avoid unlinking before
+ 				schedule at particular frame number
+				 */
+				td_ptd_map->state = TD_PTD_IN_SCHEDULE;
+
+				/*
+				 * If the length is not zero and the direction is
+				 OUT then  copy the  data to be transferred
+				 into the PAYLOAD memory area.
+				 */
+				if (sitd->length) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case OUT_PID:
+						/* Get the Payload memory
+						allocated for this PTD */
+						mem_addr = &sitd->mem_addr;
+#ifdef SWAP
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr-> phy_addr,
+							0, (u32*)
+							((sitd->hw_bufp[0])),
+							sitd->length, 0,
+							PTD_PAY);
+#else /* NO_SWAP */
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr,
+							0, (u32 *)
+							sitd->hw_bufp[0],
+							sitd->length, 0);
+#endif
+						break;
+					}
+					/* switch(PTD_PID(iso_ptd->td_info2))*/
+				}
+
+				/* if(sitd->length) */
+				/* If this is the last td, indicate to complete
+				the URB */
+				if (sitd->hw_next == EHCI_LIST_END) {
+					td_ptd_map->lasttd = 1;
+				}
+
+				/*
+				 * Clear the bit corresponding to this PTD in
+				 the skip map so that it will be processed on
+				 the next schedule traversal.
+				 */
+				skip_map &= ~td_ptd_map->ptd_bitmap;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map);
+
+				/*
+				 * Update the last map register to indicate
+				 that the newly created PTD is the last PTD
+				 added only if it is larger than the previous
+				 bitmap.
+				 */
+				if (last_map < td_ptd_map->ptd_bitmap) {
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.isotdlastmap,
+						td_ptd_map->ptd_bitmap);
+					iso_dbg(ISO_DBG_DATA,
+						"[pehci_hcd_iso_schedule]:Last Map: 0x%08x\n",
+						td_ptd_map->ptd_bitmap);
+				}
+
+				/*
+				 * Set the ISO_BUF_FILL bit to 1 to indicate
+				 that there is a PTD for ISO that needs to
+				 * be processed.
+				 */
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.buffer_status,
+					(buff_stat | ISO_BUFFER));
+
+			} else {	/*HIGH SPEED */
+
+				/* Get an ITD in the list for processing */
+				itd = list_entry(position, struct ehci_itd,
+					itd_list);
+				iNumofPkts--;
+
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: ITD Index: %d\n",	itd->itd_index);
+				/* Get the PTD allocated for this ITD. */
+				td_ptd_map =
+					&ptd_map_buff->map_list[itd->itd_index];
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/* Create a PTD from an ITD */
+				phcd_iso_itd_to_ptd(hcd, itd, itd->urb,
+					(void *) iso_ptd);
+
+				/* Indicate that this SITD's PTD have been
+				filled up */
+				ptd_map_buff->pending_ptd_bitmap &=
+					~td_ptd_map->ptd_bitmap;
+
+				/*
+				 * Place the newly initialized ISO PTD
+				 structure into the location allocated
+				 * for this PTD in the ISO PTD memory region.
+				 */
+#ifdef SWAP
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0,
+					PTD_HED);
+#else /* NO_SWAP */
+				isp1763_mem_write(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) iso_ptd,PHCI_QHA_LENGTH, 0);
+#endif
+				/*
+				 * Set this flag to avoid unlinking before schedule
+				 * at particular frame number
+				 */
+				td_ptd_map->state = TD_PTD_IN_SCHEDULE;
+
+				/*
+				 * If the length is not zero and the direction
+				 is OUT then copy the data to be transferred
+				 into the PAYLOAD memory area.
+				 */
+				if (itd->length) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case OUT_PID:
+						/* Get the Payload memory
+						allocated for this PTD */
+						mem_addr = &itd->mem_addr;
+#ifdef SWAP
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr, 0,
+							(u32*)
+							((itd->hw_bufp[0])),
+							itd->length, 0,
+							PTD_PAY);
+#else /* NO_SWAP */
+						isp1763_mem_write(hcd->dev,
+							(unsigned long)
+							mem_addr->phy_addr, 0,
+							(u32 *)itd->hw_bufp[0],
+							itd->length, 0);
+#endif
+						break;
+					}
+					/* switch(PTD_PID(iso_ptd->td_info2)) */
+				}
+
+				
+				/* If this is the last td, indicate to
+				complete the URB */
+				if (itd->hw_next == EHCI_LIST_END) {
+					td_ptd_map->lasttd = 1;
+				}
+
+				/*
+				 * Clear the bit corresponding to this PT D
+				 in the skip map so that it will be processed
+				 on the next schedule traversal.
+				 */
+				skip_map &= ~td_ptd_map->ptd_bitmap;
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map);
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.isotdskipmap,
+					skip_map);
+
+				/*
+				 * Update the last map register to indicate
+				 that the newly created PTD is the last PTD
+				 added only if it is larger than the previous
+				 bitmap.
+				 */
+				if (last_map < td_ptd_map->ptd_bitmap) {
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.isotdlastmap,
+						td_ptd_map->ptd_bitmap);
+					iso_dbg(ISO_DBG_DATA,
+						"[pehci_hcd_iso_schedule]:Last Map: 0x%08x\n",
+						td_ptd_map->ptd_bitmap);
+				}
+
+				/*
+				 * Set the ISO_BUF_FILL bit to 1 to indicate
+				 that there is a PTD for ISO that needs to
+				 * be processed.
+				 */
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.buffer_status,
+					(buff_stat | ISO_BUFFER));
+			}
+		}		/* list_for_each(position, itd_sched) */
+		isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,skip_map);
+	}/*end of while (igNumOfPkts) */
+
+	iso_dbg(ISO_DBG_INFO,
+		"[pehci_hcd_iso_schedule]: ISO-Frame scheduling done\n");
+	iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_schedule]: Exit\n");
+}
+
+/*******************************************************************
+ * phcd_iso_handler - ISOCHRONOUS Transfer handler
+ *
+ * phci_hcd *hcd,
+ *      Host controller driver structure which contains almost all data
+ *      needed by the host controller driver to process data and interact
+ *      with the host controller.
+ *
+ * struct pt_regs *regs
+ *
+ * API Description
+ * This is the ISOCHRONOUS Transfer handler, mainly responsible for:
+ *  - Checking the periodic list if there are any ITDs for scheduling or
+ *    removal.
+ *  - For ITD scheduling, converting an ITD into a PTD, which is the data
+ *    structure that the host contrtoller can understand and process.
+ *  - For ITD completion, checking the transfer status and performing the
+ *    required actions depending on status.
+ *  - Freeing up memory used by an ITDs once it is not needed anymore.
+ ************************************************************************/
+
+int debugiso = 0;
+
+void
+pehci_hcd_iso_worker(phci_hcd * hcd)
+{
+	u32 donemap = 0, skipmap = 0; /*ormask = 0,  buff_stat = 0;*/
+	u32 pendingmap = 0;
+	u32 mask = 0x1, index = 0, donetoclear = 0;
+	u32 uFrIndex = 0;
+	unsigned char last_td = FALSE, iReject = 0;
+	struct isp1763_mem_addr *mem_addr;
+	struct _isp1763_isoptd *iso_ptd;
+	unsigned long length = 0, uframe_cnt, usof_stat;
+	struct ehci_qh *qhead;
+	struct ehci_itd *itd, *current_itd;
+	struct ehci_sitd *sitd=0, *current_sitd=0;
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct list_head *sitd_itd_remove, *position;// *lst_temp;	
+	struct urb *urb;
+	u8 i = 0;
+	unsigned long startAdd = 0;
+	int ret = 0;
+
+
+	iso_ptd = &hcd->isotd;
+
+	/* Check if there are any ITDs scheduled  for processing */
+	if (hcd->periodic_sched == 0) {
+		goto exit;
+	}
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+	pendingmap = ptd_map_buff->pending_ptd_bitmap;
+
+
+	/*read the done map for interrupt transfers */
+	donemap = isp1763_reg_read16(hcd->dev, hcd->regs.isotddonemap, donemap);
+
+	iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_worker]: Enter %x \n", donemap);
+	if (!donemap) {		/*there isnt any completed PTD */
+		goto exit;
+	}
+	donetoclear = donemap;
+	uFrIndex = 0;
+	while (donetoclear) {
+		mask = 0x1 << uFrIndex;
+		index = uFrIndex;
+		uFrIndex++;
+		if (!(donetoclear & mask))
+			continue;
+		donetoclear &= ~mask;
+		iso_dbg(ISO_DBG_DATA, "[pehci_hcd_iso_worker]: uFrIndex = %d\n", index);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_worker]:donetoclear = 0x%x mask = 0x%x\n",
+			donetoclear, mask);
+
+
+		if (ptd_map_buff->map_list[index].sitd) {
+			urb = ptd_map_buff->map_list[index].sitd->urb;
+			if (!urb) {
+				printk("ERROR : URB is NULL \n");
+				continue;
+			}
+			sitd = ptd_map_buff->map_list[index].sitd;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			qhead=urb->hcpriv;
+#else
+			qhead = urb->ep->hcpriv;
+#endif
+			if (!qhead) {
+				printk("ERROR : Qhead is NULL \n");
+				continue;
+			}
+
+			sitd_itd_remove = &qhead->periodic_list.sitd_itd_head;
+		} else if (ptd_map_buff->map_list[index].itd) {
+			urb = ptd_map_buff->map_list[index].itd->urb;
+			if (!urb) {
+				printk("ERROR : URB is NULL \n");
+				continue;
+			}
+			itd = ptd_map_buff->map_list[index].itd;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			qhead=urb->hcpriv;
+#else
+			qhead = urb->ep->hcpriv;
+#endif
+			if (!qhead) {
+				printk("ERROR : Qhead is NULL \n");
+				continue;
+			}
+
+			sitd_itd_remove = &qhead->periodic_list.sitd_itd_head;
+
+		} else {
+			printk("ERROR : NO sitd in that PTD location : \n");
+			continue;
+		}
+		/* Process ITDs linked to this frame, checking for completed ITDs */
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_iso_worker]: Removal Frame number: %d\n",
+			(int) index);
+		if (list_empty(sitd_itd_remove)) {
+			continue;
+		}
+
+		if (urb) {
+			last_td = FALSE;
+			if (qhead->periodic_list.high_speed == 0)/*FULL SPEED*/
+			{
+
+				/* Get the PTD that was allocated for this
+				particular SITD*/
+				td_ptd_map =
+					&ptd_map_buff->map_list[sitd->
+								sitd_index];
+
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: PTD is done,%d\n",index);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: SITD Index: %d\n",sitd->sitd_index);
+				urb = sitd->urb;
+
+				/*
+				 * Get the base address of the memory allocated
+				 in the PAYLOAD region for this SITD
+				 */
+				mem_addr = &sitd->mem_addr;
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/*
+				 * Read this ptd from the ram address,
+				 address is in the td_ptd_map->ptd_header_addr
+				 */
+
+				isp1763_mem_read(hcd->dev,
+					td_ptd_map->ptd_header_addr,
+					0, (u32 *) iso_ptd,
+					PHCI_QHA_LENGTH, 0);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD0 = 0x%08x\n", iso_ptd->td_info1);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD1 = 0x%08x\n", iso_ptd->td_info2);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD2 = 0x%08x\n", iso_ptd->td_info3);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD3 = 0x%08x\n", iso_ptd->td_info4);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD4 = 0x%08x\n", iso_ptd->td_info5);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD5 = 0x%08x\n", iso_ptd->td_info6);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD6 = 0x%08x\n", iso_ptd->td_info7);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD7 = 0x%08x\n", iso_ptd->td_info8);
+
+				/* Go over the status of each of the 8 Micro Frames */
+				for (uframe_cnt = 0; uframe_cnt < 8;
+					uframe_cnt++) {
+					/*
+					 * We go over the status one at a time. The status bits and their
+					 * equivalent status are:
+					 * Bit 0 - Transaction Error (IN and OUT)
+					 * Bit 1 - Babble (IN token only)
+					 * Bit 2 - Underrun (OUT token only)
+					 */
+					usof_stat =
+						iso_ptd->td_info5 >> (8 +
+						(uframe_cnt * 3));
+
+					switch (usof_stat & 0x7) {
+					case INT_UNDERRUN:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Buffer underrun\n");
+							urb->error_count++;
+						break;
+					case INT_EXACT:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Transaction error\n");
+							printk("[pehci_hcd_iso_worker Error]: Transaction error\n");
+							urb->error_count++;
+						break;
+					case INT_BABBLE:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Babble error\n");
+							printk("[pehci_hcd_iso_worker Error]: Babble error\n");
+						urb->iso_frame_desc[sitd->sitd_index].status
+							= -EOVERFLOW;
+						urb->error_count++;
+						break;
+					}	/* switch(usof_stat & 0x7) */
+				}	/* end of for( ulMicroFrmCnt = 0; ulMicroFrmCnt < 8; ulMicroFrmCnt++) */
+
+				/*
+				 * Get the number of bytes transferred. This indicates the number of
+				 * bytes sent or received for this transaction.
+				 */
+				if (urb->dev->speed != USB_SPEED_HIGH) {
+					/* Length is 1K for full/low speed device */
+					length = PTD_XFERRED_NONHSLENGTH
+						(iso_ptd->td_info4);
+				} else {
+					/* Length is 32K for high speed device */
+					length = PTD_XFERRED_LENGTH(iso_ptd->
+						td_info4);
+				}
+
+				/* Halted, need to finish all the transfer on this endpoint */
+				if (iso_ptd->td_info4 & PTD_STATUS_HALTED) {
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error] PTD Halted\n");
+						printk("[pehci_hcd_iso_worker Error] PTD Halted\n");
+					/*
+					 * When there is an error, do not process the other PTDs.
+					 * Stop at the PTD with the error and remove all other PTDs.
+					 */
+					td_ptd_map->lasttd = 1;
+
+					/*
+					 * In case of halt, next transfer will start with toggle zero,
+					 * USB specs, 5.8.5
+					 */
+					td_ptd_map->datatoggle = 0;
+				}
+
+				/* if(iso_ptd->td_info4 & PTD_STATUS_HALTED) */
+				/* Update the actual length of the transfer from the data we got earlier */
+				urb->iso_frame_desc[sitd->index].actual_length =
+					length;
+
+				/* If the PTD have been executed properly the V bit should be cleared */
+				if (iso_ptd->td_info1 & QHA_VALID) {
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error]: Valid bit not cleared\n");
+						printk("[pehci_hcd_iso_worker Error]: Valid bit not cleared\n");
+					urb->iso_frame_desc[sitd->index].
+						status = -ENOSPC;
+				} else {
+					urb->iso_frame_desc[sitd->index].
+						status = 0;
+				}
+
+				/* Check if this is the last SITD either due to some error or normal completion */
+				if ((td_ptd_map->lasttd)
+					|| (sitd->hw_next == EHCI_LIST_END)) {
+					last_td = TRUE;
+				}
+
+				/* Copy data to/from */
+				if (length && (length <= MAX_PTD_BUFFER_SIZE)) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case IN_PID:
+						/*
+						 * Get the data from the PAYLOAD area and place it into
+						 * the buffer provided by the requestor.
+						 */
+
+						isp1763_mem_read(hcd->dev,
+							(unsigned long)mem_addr->
+							phy_addr, 0,(u32 *)
+							sitd->hw_bufp[0],
+							length, 0);
+
+					case OUT_PID:
+						/*
+						 * urb->actual length was initialized to zero, so for the first
+						 * uFrame having it incremented immediately is not a problem.
+						 */
+						urb->actual_length += length;
+						break;
+					}/* switch(PTD_PID(iso_ptd->td_info2)) */
+				}
+				/* if(length && (length <= MAX_PTD_BUFFER_SIZE)) */
+//				removesitd:
+				/*read skip-map */
+				skipmap =
+					isp1763_reg_read16(hcd->dev,
+						hcd->regs.isotdskipmap,
+						skipmap);
+				iso_dbg(ISO_DBG_DATA,
+					"[%s] : read skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+				if (last_td == TRUE) {
+					/* Start removing the ITDs in the list */
+					while (1) {
+						/*
+						 * This indicates that we are processing the tail PTD.
+						 * Perform cleanup procedure on this last PTD
+						 */
+						if (sitd->hw_next == EHCI_LIST_END) {
+							td_ptd_map =
+								&ptd_map_buff->
+								map_list[sitd->
+								sitd_index];
+
+							/*
+							 * Free up our allocation in the PAYLOAD area so that others can use
+							 * it.
+							 */
+#ifndef COMMON_MEMORY
+							phci_hcd_mem_free
+								(&sitd->
+								mem_addr);
+#endif
+							/* Remove this SITD entry in the SITD list */
+							list_del(&sitd->
+								sitd_list);
+
+							/* Free up the memory allocated for the SITD structure */
+							qha_free(qha_cache,
+								sitd);
+
+							/* Indicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->ptd_bitmap;
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+
+							/* All ITDs in this list have been successfully removed. */
+							break;
+						} else {
+						/*
+						* This indicates that we stopped due to an error on a PTD that is
+						* not the last in the list. We need to free up this PTD as well as
+						* the PTDs after it.
+						*/
+						/*
+						 * Put the current SITD error onto this variable.
+						 * We will be unlinking this from the list and free up its
+						 * resources later.
+						 */
+							current_sitd = sitd;
+
+							td_ptd_map =
+								&ptd_map_buff->
+								map_list[sitd->
+								sitd_index];
+
+							/*
+							 * Get the next SITD, and place it to the sitd variable.
+							 * In a way we are moving forward in the SITD list.
+							 */
+							sitd = (struct ehci_sitd
+								*)
+								(current_sitd->
+								hw_next);
+							/* Free up the current SITD's resources */
+#ifndef COMMON_MEMORY
+							phci_hcd_mem_free
+								(&current_sitd->
+								 mem_addr);
+#endif
+							/* Remove this SITD entry in the SITD list */
+							list_del(&current_sitd->
+								sitd_list);
+
+							/* Free up the memory allocated for the SITD structure */
+							qha_free(qha_cache,
+								current_sitd);
+
+							/* Inidicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Sine it is done, skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->
+								ptd_bitmap;
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+							/*
+							 * Start all over again until it gets to the tail of the
+							 * list of PTDs/ITDs
+							 */
+							continue;
+						}	/* else of if(sitd->hw_next == EHCI_LIST_END) */
+
+						/* It should never get here, but I put this as a precaution */
+						break;
+					}	/*end of while(1) */
+
+					/* Check if there were ITDs that were not processed due to the error */
+					if (urb->status == -EINPROGRESS) {
+						if ((urb->actual_length !=
+							urb->transfer_buffer_length)
+							&& (urb->transfer_flags &
+							URB_SHORT_NOT_OK)) {
+							iso_dbg(ISO_DBG_ERR,
+								"[pehci_hcd_iso_worker Error]: Short Packet\n");
+							urb->status =
+								-EREMOTEIO;
+						} else {
+							urb->status = 0;
+						}
+					}
+
+					urb->hcpriv = 0;
+					iso_dbg(ISO_DBG_DATA,
+						"[%s] : remain skipmap =0x%x\n",
+						__FUNCTION__, skipmap);
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+					/* We need to unlock this here, since this was locked when we are called
+					 * from the interrupt handler */
+					spin_unlock(&hcd->lock);
+					/* Perform URB cleanup */
+					iso_dbg(ISO_DBG_INFO,
+						"[pehci_hcd_iso_worker] Complete a URB\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,
+						urb);
+#endif
+					hcd->periodic_more_urb = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						qhead=urb->hcpriv;
+					if (!list_empty(&qhead->ep->urb_list))
+#else
+					if (!list_empty(&urb->ep->urb_list))
+#endif
+						hcd->periodic_more_urb = 1;
+					
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb, urb->status);
+#endif
+
+					spin_lock(&hcd->lock);
+					continue;
+				}
+
+				/* if( last_td == TRUE ) */
+				/*
+				 * If the last_td is not set then we do not need to check for errors and directly
+				 * proceed with the cleaning sequence.
+				 */
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: last_td is not set\n");
+				/*update skipmap */
+				skipmap |= td_ptd_map->ptd_bitmap;
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.isotdskipmap,
+					skipmap);
+				iso_dbg(ISO_DBG_DATA,
+					"%s : remain skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+
+				/* Decrement the count of active PTDs */
+				hcd->periodic_sched--;
+				/*schedule next PTD for this URB */
+				if(qhead->actualptds<qhead->totalptds)
+				{
+					sitd_itd_remove = &qhead->periodic_list.sitd_itd_head;
+					/* find sitd to schedule */
+					list_for_each(position, sitd_itd_remove) {
+						
+						if (qhead->periodic_list.high_speed == 0){
+						/* Get an SITD in the list for processing */
+							current_sitd= list_entry(position, struct ehci_sitd,
+									sitd_list);		
+							if(current_sitd->sitd_index==TD_PTD_INV_PTD_INDEX)
+								break;
+						}	
+					}
+				      if(current_sitd->sitd_index==TD_PTD_INV_PTD_INDEX){
+					  	qhead->actualptds++;
+					/*allocate memory and PTD index */
+						memcpy(&current_sitd->mem_addr,&sitd->mem_addr,sizeof(struct isp1763_mem_addr));
+//				printk("current %x\n",sitd->sitd_index);
+						current_sitd->sitd_index=sitd->sitd_index;
+					/*schedule PTD */
+						td_ptd_map->sitd = current_sitd;
+						hcd->periodic_sched++;
+						pehci_hcd_iso_sitd_schedule(hcd, urb,current_sitd);
+				      }
+
+				/* Remove this SITD from the list of active ITDs */
+				list_del(&sitd->sitd_list);
+
+				/* Free up the memory we allocated for the SITD structure */
+				qha_free(qha_cache, sitd);
+
+					
+				}else{
+#ifndef COMMON_MEMORY
+				phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+				/* Remove this SITD from the list of active ITDs */
+				list_del(&sitd->sitd_list);
+
+				/* Free up the memory we allocated for the SITD structure */
+				qha_free(qha_cache, sitd);
+
+				/*
+				 * Clear the bit associated with this PTD from the grouptdmap and
+				 * make this PTD available for other transfers
+				 */
+				td_ptd_map->state = TD_PTD_NEW;
+				td_ptd_map->sitd = NULL;
+				td_ptd_map->itd = NULL;
+
+				}		
+
+				
+				
+			}	else {	/*HIGH SPEED */
+
+				/* Get an ITD in the list for processing */
+				itd = ptd_map_buff->map_list[index].itd;
+
+				/* Get the PTD that was allocated for this particular ITD. */
+				td_ptd_map =
+					&ptd_map_buff->map_list[itd->itd_index];
+
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: PTD is done , %d\n",
+					index);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: ITD Index: %d\n",
+					itd->itd_index);
+
+				urb = itd->urb;
+
+				/*
+				 * Get the base address of the memory allocated in the
+				 * PAYLOAD region for this ITD
+				 */
+				mem_addr = &itd->mem_addr;
+				memset(iso_ptd, 0,
+					sizeof(struct _isp1763_isoptd));
+
+				/*
+				 * Read this ptd from the ram address,address is in the
+				 * td_ptd_map->ptd_header_addr
+				 */
+
+				isp1763_mem_read(hcd->dev,
+					td_ptd_map->ptd_header_addr,
+					0, (u32 *) iso_ptd,
+					PHCI_QHA_LENGTH, 0);
+
+				/* 
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD0 =
+					0x%08x\n", iso_ptd->td_info1);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD1 =
+					0x%08x\n", iso_ptd->td_info2);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD2 =
+					0x%08x\n", iso_ptd->td_info3);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD3 =
+					0x%08x\n", iso_ptd->td_info4);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD4 =
+					0x%08x\n",iso_ptd->td_info5);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD5 =
+					0x%08x\n", iso_ptd->td_info6);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD6 =
+					0x%08x\n", iso_ptd->td_info7);
+				iso_dbg(ISO_DBG_DATA,
+					"[pehci_hcd_iso_worker]: DWORD7 =
+					0x%08x\n", iso_ptd->td_info8);
+				*/
+
+
+				/* If the PTD have been executed properly,
+				the V bit should be cleared */
+				if (iso_ptd->td_info1 & QHA_VALID) {
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error]: Valid bit not cleared\n");
+					for(i = 0; i<itd->num_of_pkts; i++){
+						urb->iso_frame_desc[itd->index
+							+ i].status = -ENOSPC;
+					}
+				} else {
+					for (i = 0; i<itd->num_of_pkts; i++){
+						urb->iso_frame_desc[itd->index
+							+i].status = 0;
+					}
+				}
+
+				/* Go over the status of each of the 8 Micro Frames */
+				for (uframe_cnt = 0; (uframe_cnt < 8)
+					&& (uframe_cnt < itd->num_of_pkts);
+					uframe_cnt++) {
+					/*
+					 * We go over the status one at a time. The status bits and their
+					 * equivalent status are:
+					 * Bit 0 - Transaction Error (IN and OUT)
+					 * Bit 1 - Babble (IN token only)
+					 * Bit 2 - Underrun (OUT token only)
+					 */
+					usof_stat =
+						iso_ptd->td_info5 >> (8 +
+						(uframe_cnt * 3));
+
+					switch (usof_stat & 0x7) {
+					case INT_UNDERRUN:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Buffer underrun\n");
+						urb->iso_frame_desc[itd->index +
+							uframe_cnt].
+						status = -ECOMM;
+						urb->error_count++;
+						break;
+					case INT_EXACT:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: %p Transaction error\n",
+							urb);
+						urb->iso_frame_desc[itd->index +
+							uframe_cnt].
+							status = -EPROTO;
+						urb->error_count++;
+						debugiso = 25;
+						break;
+					case INT_BABBLE:
+						iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Babble error\n");
+						urb->iso_frame_desc[itd->index +
+							uframe_cnt].
+							status = -EOVERFLOW;
+						urb->error_count++;
+						break;
+					}/* switch(usof_stat & 0x7) */
+				}/* end of for( ulMicroFrmCnt = 0; ulMicroFrmCnt < 8; ulMicroFrmCnt++) */
+
+				/*
+				 * Get the number of bytes transferred. This indicates the number of
+				 * bytes sent or received for this transaction.
+				 */
+
+				/* Length is 32K for high speed device */
+				length = PTD_XFERRED_LENGTH(iso_ptd->td_info4);
+
+				/* Halted, need to finish all the transfer on this endpoint */
+				if (iso_ptd->td_info4 & PTD_STATUS_HALTED) {
+
+					iso_dbg(ISO_DBG_ERR,
+						"[pehci_hcd_iso_worker Error] PTD Halted\n");
+					printk("[pehci_hcd_iso_worker Error] PTD Halted===============\n");
+					/*
+					 * When there is an error, do not process the other PTDs.
+					 * Stop at the PTD with the error and remove all other PTDs.
+					 */
+					td_ptd_map->lasttd = 1;
+
+					/*
+					 * In case of halt, next transfer will start with toggle zero,
+					 * USB specs, 5.8.5
+					 */
+					td_ptd_map->datatoggle = 0;
+				}
+				/* if(iso_ptd->td_info4 & PTD_STATUS_HALTED) */
+				/* Update the actual length of the transfer from the data we got earlier */
+				if (PTD_PID(iso_ptd->td_info2) == OUT_PID) {
+					for (i = 0; i < itd->num_of_pkts; i++){
+						urb->iso_frame_desc[itd->index +
+						i].actual_length =(unsigned int)
+						length / itd->num_of_pkts;
+					}
+				} else{
+					iso_dbg(ISO_DBG_DATA,
+						"itd->num_of_pkts = %d, itd->ssplit = %x\n",
+						itd->num_of_pkts, itd->ssplit);
+					urb->iso_frame_desc[itd->index +
+						0].actual_length =
+						iso_ptd->td_info6 & 0x00000FFF;
+					iso_dbg(ISO_DBG_DATA,
+						"actual length[0] = %d\n",
+						urb->iso_frame_desc[itd->index +0].
+						actual_length);
+
+					if((itd->num_of_pkts > 1)
+						&& ((itd->ssplit & 0x2) == 0x2)
+						&& (urb->iso_frame_desc[itd->index +
+						1].status ==0)) {
+						
+						urb->iso_frame_desc[itd->index +1].
+							actual_length =	(iso_ptd->
+							td_info6 & 0x00FFF000)>> 12;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[1] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index + 1].
+							actual_length);
+					}else{
+						urb->iso_frame_desc[itd->index +1].
+							actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 2)
+						&& ((itd->ssplit & 0x4) == 0x4)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						2].status ==0)) {
+						
+						urb->iso_frame_desc[itd->index +
+							2].actual_length =
+							((iso_ptd->td_info6 &
+							0xFF000000 )>> 24)
+							| ((iso_ptd->td_info7
+							& 0x0000000F)<< 8);
+						
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[2] = %d\n",
+							urb->iso_frame_desc[itd->
+							index + 2].actual_length);
+					} else{
+						urb->iso_frame_desc[itd->index +2].
+							actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 3)
+						&& ((itd->ssplit & 0x8) == 0x8)
+						&& (urb->iso_frame_desc[itd->index +
+						3].status == 0)) {
+
+						urb->iso_frame_desc[itd->index + 3].
+							actual_length =(iso_ptd->
+							td_info7 & 0x0000FFF0)>> 4;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[3] = %d\n",
+							urb->iso_frame_desc[itd->
+							index + 3].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +3].
+							actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 4)
+						&& ((itd->ssplit & 0x10) == 0x10)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						4].status ==0)) {
+
+						urb->iso_frame_desc[itd->index +
+							4].actual_length =
+							(iso_ptd->
+							td_info7 & 0x0FFF0000) >> 16;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[4] = %d\n",
+							urb->iso_frame_desc[itd->index +
+							4].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							4].actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 5)
+						&& ((itd->ssplit & 0x20) == 0x20)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						5].status ==
+						0)) {
+
+						urb->iso_frame_desc[itd->index +
+							5].actual_length =
+							((iso_ptd->
+							td_info7 & 0xF0000000) >> 28) | 
+							((iso_ptd->td_info8 &
+							0x000000FF)
+							<< 4);
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[5] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index +
+							5].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							5].actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 6)
+						&& ((itd->ssplit & 0x40) == 0x40)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						6].status ==0)) {
+
+						urb->iso_frame_desc[itd->index +
+							6].actual_length =
+							(iso_ptd->
+							td_info8 & 0x000FFF00)
+							>> 8;
+						
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[6] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index +
+							6].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							6].actual_length = 0;
+					}
+
+					if ((itd->num_of_pkts > 7)
+						&& ((itd->ssplit & 0x80) == 0x80)
+						&& (urb->
+						iso_frame_desc[itd->index +
+						7].status ==
+						0)) {
+
+						urb->iso_frame_desc[itd->index +
+							7].actual_length =
+							(iso_ptd->
+							td_info8 & 0xFFF00000) >> 20;
+
+						iso_dbg(ISO_DBG_DATA,
+							"actual length[7] = %d\n",
+							urb->
+							iso_frame_desc[itd->
+							index +
+							7].actual_length);
+					} else {
+						urb->iso_frame_desc[itd->index +
+							7].actual_length = 0;
+					}
+				}
+				/* Check if this is the last ITD either due to some error or normal completion */
+				if ((td_ptd_map->lasttd)
+					|| (itd->hw_next == EHCI_LIST_END)) {
+
+					last_td = TRUE;
+
+				}
+
+				/* Copy data to/from */
+				if (length && (length <= MAX_PTD_BUFFER_SIZE)) {
+					switch (PTD_PID(iso_ptd->td_info2)) {
+					case IN_PID:
+						/*
+						 * Get the data from the PAYLOAD area and place it into
+						 * the buffer provided by the requestor.
+						 */
+						/*for first packet*/
+						startAdd = mem_addr->phy_addr;
+						iso_dbg(ISO_DBG_DATA,
+							"start add = %ld hw_bufp[0] = 0x%08x length = %d\n",
+							startAdd,
+							itd->hw_bufp[0],
+							urb->
+							iso_frame_desc[itd->
+							index].actual_length);
+						if (urb->
+							iso_frame_desc[itd->index].
+							status == 0) {
+
+							if (itd->hw_bufp[0] ==0) {
+								dma_addr_t
+									buff_dma;
+
+								buff_dma =
+									(u32) ((unsigned char *) urb->transfer_buffer +
+									urb->iso_frame_desc[itd->index].offset);
+								itd->buf_dma =
+									buff_dma;
+								itd->hw_bufp[0]
+									=
+									buff_dma;
+							}
+							if (itd->hw_bufp[0] !=0) {
+
+								ret = isp1763_mem_read(hcd->dev, (unsigned long)
+									startAdd,
+									0,(u32*)itd->
+									hw_bufp[0],
+									urb->
+									iso_frame_desc
+									[itd->
+									index].
+									actual_length,
+									0);
+
+							} else {
+								printk("isp1763_mem_read data payload fail\n");
+								printk("start add = %ld hw_bufp[0] = 0x%08x length = %d\n",
+									startAdd, itd->hw_bufp[0],
+									urb->iso_frame_desc[itd->index].actual_length);
+								urb->iso_frame_desc[itd->index].status = -EPROTO;
+								urb->error_count++;
+							}
+						}
+
+
+						for (i = 1;
+							i < itd->num_of_pkts;
+							i++) {
+							startAdd +=
+								(unsigned
+								long) (urb->
+								iso_frame_desc
+								[itd->
+								index +
+								i - 1].
+								actual_length);
+
+							iso_dbg(ISO_DBG_DATA,
+								"start add = %ld hw_bufp[%d] = 0x%08x length = %d\n",
+								startAdd, i,
+								itd->hw_bufp[i],
+								urb->
+								iso_frame_desc
+								[itd->index +
+								i].
+								actual_length);
+							if (urb->
+								iso_frame_desc[itd->
+								index + i].
+								status == 0) {
+
+								isp1763_mem_read
+									(hcd->dev,
+									startAdd,
+									0,(u32*)
+									itd->
+									hw_bufp
+									[i],urb->
+									iso_frame_desc
+									[itd->
+									index + i].
+									actual_length,
+									0);
+
+								if (ret == -EINVAL){
+									printk("isp1763_mem_read data payload fail %d\n", i);
+								}
+							}
+						}
+
+					case OUT_PID:
+						/*
+						 * urb->actual length was initialized to zero, so for the first
+						 * uFrame having it incremented immediately is not a problem.
+						 */
+						urb->actual_length += length;
+						break;
+					}	/* switch(PTD_PID(iso_ptd->td_info2)) */
+				}
+
+				/* if(length && (length <= MAX_PTD_BUFFER_SIZE)) */
+//				removeitd:
+				/*read skip-map */
+				skipmap =
+					isp1763_reg_read16(hcd->dev,
+						hcd->regs.isotdskipmap,
+						skipmap);
+
+				iso_dbg(ISO_DBG_DATA,
+					"[%s] : read skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+				if (last_td == TRUE) {
+					/* Start removing the ITDs in the list */
+					while (1) {
+						/*
+						 * This indicates that we are processing the tail PTD.
+						 * Perform cleanup procedure on this last PTD
+						 */
+						if (itd->hw_next ==
+							EHCI_LIST_END) {
+							td_ptd_map =
+							&ptd_map_buff->
+							map_list[itd->
+							itd_index];
+
+							/*
+							 * Free up our allocation in the PAYLOAD area so that others can use
+							 * it.
+							 */
+#ifndef COMMON_MEMORY
+							phci_hcd_mem_free(&itd->
+								mem_addr);
+#endif
+
+							/* Remove this ITD entry in the ITD list */
+							list_del(&itd->
+								itd_list);
+
+							/* Free up the memory allocated for the ITD structure */
+							qha_free(qha_cache,
+								itd);
+
+							/* Indicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->
+								ptd_bitmap;
+
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+
+							/* All ITDs in this list have been successfully removed. */
+							break;
+						}
+						/* if(itd->hw_next == EHCI_LIST_END) */
+						/*
+						 * This indicates that we stopped due to an error on a PTD that is
+						 * not the last in the list. We need to free up this PTD as well as
+						 * the PTDs after it.
+						 */
+						else {
+							/*
+							 * Put the current ITD error onto this variable.
+							 * We will be unlinking this from the list and free up its
+							 * resources later.
+							 */
+							current_itd = itd;
+
+							td_ptd_map =
+								&ptd_map_buff->
+								map_list[itd->
+								itd_index];
+
+							/*
+							 * Get the next ITD, and place it to the itd variable.
+							 * In a way we are moving forward in the ITD list.
+							 */
+							itd = (struct ehci_itd
+								*) (current_itd->
+								hw_next);
+#ifndef COMMON_MEMORY
+							/* Free up the current ITD's resources */
+							phci_hcd_mem_free
+								(&current_itd->
+								mem_addr);
+#endif
+
+							/* Remove this ITD entry in the ITD list */
+							list_del(&current_itd->
+								itd_list);
+
+							/* Free up the memory allocated for the ITD structure */
+							qha_free(qha_cache,
+								current_itd);
+
+							/* Inidicate that the PTD we have used is now free */
+							td_ptd_map->state =
+								TD_PTD_NEW;
+							td_ptd_map->sitd = NULL;
+							td_ptd_map->itd = NULL;
+
+							/* Decrease the number of active PTDs scheduled */
+							hcd->periodic_sched--;
+
+							/* Sine it is done, skip this PTD during the next PTD processing. */
+							skipmap |=
+								td_ptd_map->
+								ptd_bitmap;
+							isp1763_reg_write16
+								(hcd->dev,
+								hcd->regs.
+								isotdskipmap,
+								skipmap);
+							/*
+							 * Start all over again until it gets to the tail of the
+							 * list of PTDs/ITDs
+							 */
+							continue;
+						}/* else of if(itd->hw_next == EHCI_LIST_END) */
+						/* It should never get here, but I put this as a precaution */
+						break;
+					}	/*end of while(1) */
+					/* Check if there were ITDs that were not processed due to the error */
+					if (urb->status == -EINPROGRESS) {
+						if ((urb->actual_length !=
+							urb->transfer_buffer_length)
+							&& (urb->
+							transfer_flags &
+							URB_SHORT_NOT_OK)) {
+
+							iso_dbg(ISO_DBG_ERR,
+							"[pehci_hcd_iso_worker Error]: Short Packet\n");
+
+							urb->status =
+								-EREMOTEIO;
+						} else {
+							urb->status = 0;
+						}
+					}
+
+					urb->hcpriv = 0;
+					iso_dbg(ISO_DBG_DATA,
+						"[%s] : remain skipmap =0x%x\n",
+						__FUNCTION__, skipmap);
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+//					if (urb->reject.counter) {
+					if (unlikely(atomic_read(&urb->reject))) {// kernel reference code hcd.c
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#else
+					if (unlikely(urb->reject)) {
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#endif
+
+/*
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+
+					if (urb->reject.counter) {
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#else
+				        if (unlikely(urb->reject)) {					       
+				
+					
+						iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__);
+						iReject = 1;
+					}
+#endif
+*/
+
+#ifdef COMMON_MEMORY
+					phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+					/* We need to unlock this here, since this was locked when we are called */
+					/* from the interrupt handler */
+					spin_unlock(&hcd->lock);
+					/* Perform URB cleanup */
+					iso_dbg(ISO_DBG_INFO,
+						"[pehci_hcd_iso_worker] Complete a URB\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, urb);
+#endif
+					hcd->periodic_more_urb = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						qhead=urb->hcpriv;
+					if (!list_empty(&qhead->ep->urb_list)){
+
+#else
+					if (!list_empty(&urb->ep->urb_list)){
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						if (urb->hcpriv== periodic_ep[0]){
+#else
+						if (urb->ep == periodic_ep[0]){
+#endif
+							hcd->periodic_more_urb =
+							1;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						} else if (urb->hcpriv==
+							 periodic_ep[1]){
+#else
+						} else if (urb->ep ==
+							 periodic_ep[1]){
+#endif							 
+							hcd->periodic_more_urb =
+							2;
+						} else {
+							hcd->periodic_more_urb =
+							0;
+						}
+
+
+					}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+				usb_hcd_giveback_urb(&hcd->usb_hcd, urb, 
+										urb->status);
+#endif
+
+					spin_lock(&hcd->lock);
+					continue;
+				}
+				/* if( last_td == TRUE ) */
+				/*
+				 * If the last_td is not set then we do not need to check for errors and directly
+				 * proceed with the cleaning sequence.
+				 */
+				iso_dbg(ISO_DBG_INFO,
+					"[pehci_hcd_iso_worker]: last_td is not set\n");
+				/*update skipmap */
+				skipmap |= td_ptd_map->ptd_bitmap;
+				isp1763_reg_write16(hcd->dev,
+					hcd->regs.isotdskipmap,
+					skipmap);
+				iso_dbg(ISO_DBG_DATA,
+					"%s : remain skipmap =0x%x\n",
+					__FUNCTION__, skipmap);
+
+				/* Decrement the count of active PTDs */
+				hcd->periodic_sched--;
+#ifndef COMMON_MEMORY
+				/* Free up the memory we allocated in the PAYLOAD area */
+				phci_hcd_mem_free(&itd->mem_addr);
+#endif
+				/* Remove this ITD from the list of active ITDs */
+				list_del(&itd->itd_list);
+
+				/* Free up the memory we allocated for the ITD structure */
+				qha_free(qha_cache, itd);
+				/*
+				 * Clear the bit associated with this PTD from the grouptdmap and
+				 * make this PTD available for other transfers
+				 */
+				td_ptd_map->state = TD_PTD_NEW;
+				td_ptd_map->sitd = NULL;
+				td_ptd_map->itd = NULL;
+			}	/*end of HIGH SPEED */
+		}		/* end of list_for_each_safe(position, lst_temp, itd_remove) */
+		iso_dbg(ISO_DBG_INFO,
+			"[pehci_hcd_iso_worker]: ISO-Frame removal done\n");
+
+
+	}			/* while donetoclear */
+
+
+	if (iReject) {
+		spin_unlock(&hcd->lock);
+		if (hcd->periodic_more_urb) {
+
+			if(periodic_ep[hcd->periodic_more_urb])
+			while (&periodic_ep[hcd->periodic_more_urb - 1]->
+				urb_list) {
+
+				urb = container_of(periodic_ep
+					[hcd->periodic_more_urb -
+					1]->urb_list.next,
+					struct urb, urb_list);
+				
+				if (urb) {
+					urb->status = -ENOENT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0))
+					usb_hcd_unlink_urb_from_ep(&hcd->
+					usb_hcd,urb);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+					usb_hcd_giveback_urb(&hcd->usb_hcd, urb);
+#else
+					usb_hcd_giveback_urb(&hcd->usb_hcd, urb,
+						urb->status);
+#endif
+				}
+			}
+		}
+
+		spin_lock(&hcd->lock);
+	}
+
+	/* When there is no more PTDs queued for scheduling or removal
+	 * clear the buffer status to indicate there are no more PTDs for
+	 * processing and set the skip map to 1 to indicate that the first
+	 * PTD is also the last PTD.
+	 */
+
+	if (hcd->periodic_more_urb) {
+		int status = 0;
+		iso_dbg(ISO_DBG_INFO,
+			"[phcd_iso_handler]: No more PTDs queued\n");
+		hcd->periodic_sched = 0;
+		phcd_store_urb_pending(hcd, hcd->periodic_more_urb, NULL,
+				       &status);
+		hcd->periodic_more_urb = 0;
+	}
+exit:
+	iso_dbg(ISO_DBG_ENTRY, "-- %s: Exit\n", __FUNCTION__);
+}				/* end of pehci_hcd_iso_worker */
+
+#endif /* CONFIG_ISO_SUPPORT */
+
+/*interrupt transfer handler*/
+/********************************************************
+  1. read done map
+  2. read the ptd to see any errors
+  3. copy the payload to and from
+  4. update ehci td
+  5. make new ptd if transfer there and earlier done
+  6. schedule
+ *********************************************************/
+static void
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+pehci_hcd_intl_worker(phci_hcd * hcd, struct pt_regs *regs)
+#else
+pehci_hcd_intl_worker(phci_hcd * hcd)
+#endif
+{
+	int i =	0;
+	u16 donemap = 0, donetoclear;
+	u16 mask = 0x1,	index =	0;
+	u16 pendingmap = 0;
+	u16 location = 0;
+	u32 length = 0;
+	u16 skipmap = 0;
+	u16 ormask = 0;
+	u32 usofstatus = 0;
+	struct urb *urb;
+	struct ehci_qtd	*qtd = 0;
+	struct ehci_qh *qh = 0;
+
+	struct _isp1763_qhint *qhint = &hcd->qhint;
+
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	u16 dontschedule = 0;
+
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL]);
+	pendingmap = ptd_map_buff->pending_ptd_bitmap;
+
+	/*read the done	map for	interrupt transfers */
+	donetoclear = donemap =
+		isp1763_reg_read16(hcd->dev, hcd->regs.inttddonemap, donemap);
+	if (donemap) {
+		/*skip done tds	*/
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+			skipmap);
+		skipmap	|= donemap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap);
+		donemap	|= pendingmap;
+	}
+	/*if sof interrupt is enabled */
+#ifdef MSEC_INT_BASED
+	else {
+		/*if there is something	pending	, put this transfer in */
+		if (ptd_map_buff->pending_ptd_bitmap) {
+			pehci_hcd_schedule_pending_ptds(hcd, pendingmap, (u8)
+				TD_PTD_BUFF_TYPE_INTL,
+				1);
+		}
+		//return 0;
+		goto exit;
+	}
+#else
+	else {
+	goto exit;	
+	//return 0;
+	}
+
+#endif
+
+
+	ormask = isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or,
+		ormask);
+	/*process all the endpoints first those	are done */
+	donetoclear = donemap;
+	while (donetoclear) {
+		/*index	is the number of endpoints open	currently */
+		index =	donetoclear & mask;
+		donetoclear &= ~mask;
+		mask <<= 1;
+		/*what if we are in the	middle of schedule
+		   where nothing is done */
+		if (!index) {
+			location++;
+			continue;
+		}
+
+		/*read our td_ptd_map */
+		td_ptd_map = &ptd_map_buff->map_list[location];
+
+		/*if this one is already in the	removal	*/
+		if (td_ptd_map->state == TD_PTD_REMOVE ||
+			td_ptd_map->state == TD_PTD_NEW) {
+			pehci_check("interrupt td is being removed\n");
+			/*this will be handled by urb_remove */
+			/*if this is last urb no need to complete it again */
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*if there is something	pending	*/
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			continue;
+		}
+
+
+		/*if we	found something	already	in */
+		if (!(skipmap &	td_ptd_map->ptd_bitmap)) {
+			pehci_check("intr td_ptd_map %x,skipnap	%x\n",
+			td_ptd_map->ptd_bitmap, skipmap);
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;;
+			location++;
+			continue;
+		}
+
+
+		if (td_ptd_map->state == TD_PTD_NEW) {
+			pehci_check
+				("interrupt not	come here, map %x,location %d\n",
+				 td_ptd_map->ptd_bitmap, location);
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			location++;
+			continue;
+		}
+
+		/*move to the next schedule */
+		location++;
+		/*endpoint, td,	urb and	memory
+		 * for current transfer*/
+		qh = td_ptd_map->qh;
+		qtd = td_ptd_map->qtd;
+		if (qtd->state & QTD_STATE_NEW)	{
+			/*we need to schedule it */
+			goto schedule;
+		}
+		urb = qtd->urb;
+		mem_addr = &qtd->mem_addr;
+
+		/*clear	the irq	mask for this transfer */
+		ormask &= ~td_ptd_map->ptd_bitmap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or,
+			ormask);
+
+		ptd_map_buff->active_ptds--;
+		memset(qhint, 0, sizeof(struct _isp1763_qhint));
+
+		/*read this ptd	from the ram address,address is	in the
+		   td_ptd_map->ptd_header_addr */
+		isp1763_mem_read(hcd->dev, td_ptd_map->ptd_header_addr,	0,
+				 (u32 *) (qhint), PHCI_QHA_LENGTH, 0);
+
+#ifdef PTD_DUMP_COMPLETE
+		printk("INTL PTD header after COMPLETION\n");
+		printk("CDW0: 0x%08X\n", qhint->td_info1);
+		printk("CDW1: 0x%08X\n", qhint->td_info2);
+		printk("CDW2: 0x%08X\n", qhint->td_info3);
+		printk("CDW3: 0x%08X\n", qhint->td_info4);
+#endif
+
+		/*statuc of 8 uframes */
+		for (i = 0; i <	8; i++)	{
+			/*take care of errors */
+			usofstatus = qhint->td_info5 >>	(8 + i * 3);
+			switch (usofstatus & 0x7) {
+			case INT_UNDERRUN:
+				pehci_print("under run , %x\n",	usofstatus);
+				break;
+			case INT_EXACT:
+				pehci_print("transaction error,	%x\n",
+					    usofstatus);
+				break;
+			case INT_BABBLE:
+				pehci_print("babble error, %x\n", usofstatus);
+				break;
+			}
+		}
+
+		if (urb->dev->speed != USB_SPEED_HIGH) {
+			/*length is 1K for full/low speed device */
+			length = PTD_XFERRED_NONHSLENGTH(qhint->td_info4);
+		} else {
+			/*length is 32K	for high speed device */
+			length = PTD_XFERRED_LENGTH(qhint->td_info4);
+		}
+
+		pehci_hcd_update_error_status(qhint->td_info4, urb);
+		/*halted, need to finish all the transfer on this endpoint */
+		if (qhint->td_info4 & PTD_STATUS_HALTED) {
+			qtd->state |= QTD_STATE_LAST;
+			/*in case of halt, next	transfer will start with toggle	zero,
+			 *USB speck, 5.8.5*/
+			qh->datatoggle = td_ptd_map->datatoggle	= 0;
+			donemap	&= ~td_ptd_map->ptd_bitmap;
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			dontschedule = 1;
+			goto copylength;
+		}
+
+
+		copylength:
+		/*preserve the current data toggle */
+		qh->datatoggle = td_ptd_map->datatoggle	=
+			PTD_NEXTTOGGLE(qhint->td_info4);
+		/*copy data from the host */
+		switch (PTD_PID(qhint->td_info2)) {
+		case IN_PID:
+			if (length && (length <= MAX_PTD_BUFFER_SIZE))
+				/*do read only when there is somedata */
+				isp1763_mem_read(hcd->dev,
+					(u32) mem_addr->phy_addr, 0,
+					urb->transfer_buffer +
+					urb->actual_length, length, 0);
+
+		case OUT_PID:
+			urb->actual_length += length;
+			qh->hw_current = qtd->hw_next;
+			phci_hcd_mem_free(&qtd->mem_addr);
+			qtd->state &= ~QTD_STATE_NEW;
+			qtd->state |= QTD_STATE_DONE;
+			break;
+		}
+
+		if (qtd->state & QTD_STATE_LAST) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, regs);
+#else
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+			if (dontschedule) {	/*cleanup will start from drivers */
+				dontschedule = 0;
+				continue;
+			}
+
+			/*take the next	if in the queue	*/
+			if (!list_empty(&qh->qtd_list))	{
+				struct list_head *head;
+				/*last td of previous urb */
+				head = &qh->qtd_list;
+				qtd = list_entry(head->next, struct ehci_qtd,
+					qtd_list);
+				td_ptd_map->qtd	= qtd;
+				qh->hw_current = cpu_to_le32(qtd);
+				qh->qh_state = QH_STATE_LINKED;
+
+			} else {
+				td_ptd_map->qtd	=
+						 (struct ehci_qtd *) le32_to_cpu(0);
+				qh->hw_current = cpu_to_le32(0);
+				qh->qh_state = QH_STATE_IDLE;
+				donemap	&= ~td_ptd_map->ptd_bitmap;
+				ptd_map_buff->pending_ptd_bitmap &= 
+						~td_ptd_map->ptd_bitmap;
+	       			td_ptd_map->state=TD_PTD_NEW;
+				continue;
+			}
+
+		}
+
+		schedule:
+		{
+			/*current td comes from	qh->hw_current */
+			ptd_map_buff->pending_ptd_bitmap &=
+				~td_ptd_map->ptd_bitmap;
+			ormask |= td_ptd_map->ptd_bitmap;
+			ptd_map_buff->active_ptds++;
+			pehci_check
+				("inter	schedule next qtd %p, active tds %d\n",
+				 qtd, ptd_map_buff->active_ptds);
+			pehci_hcd_qtd_schedule(hcd, qtd, qh, td_ptd_map);
+		}
+
+	}			/*end of while */
+
+
+	/*clear	all the	tds inside this	routine	*/
+	skipmap	&= ~donemap;
+	isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap);
+	ormask |= donemap;
+	isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, ormask);
+exit:
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	
+//	return (int)0;
+}
+
+/*atl(bulk/control) transfer handler*/
+/*1. read done map
+  2. read the ptd to see any errors
+  3. copy the payload to and from
+  4. update ehci td
+  5. make new ptd if transfer there and	earlier	done
+  6. schedule
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_hcd_atl_worker(phci_hcd * hcd, struct pt_regs *regs)
+#else
+static void
+pehci_hcd_atl_worker(phci_hcd * hcd)
+#endif
+{
+	u16 donemap = 0, donetoclear = 0;
+	u16 pendingmap = 0;
+	u32 rl = 0;
+	u16 mask = 0x1,	index =	0;
+	u16 location = 0;
+	u32 nakcount = 0;
+	u32 active = 0;
+	u32 length = 0;
+	u16 skipmap = 0;
+	u16 tempskipmap	= 0;
+	u16 ormask = 0;
+	struct urb *urb;
+	struct ehci_qtd	*qtd = 0;
+	struct ehci_qh *qh;
+	struct _isp1763_qha atlqha;
+	struct _isp1763_qha *qha;
+	td_ptd_map_t *td_ptd_map;
+	td_ptd_map_buff_t *ptd_map_buff;
+	urb_priv_t *urbpriv = 0;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	u16 dontschedule = 0;
+	ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ATL]);
+	pendingmap = ptd_map_buff->pending_ptd_bitmap;
+
+#ifdef MSEC_INT_BASED
+	/*running on skipmap rather donemap,
+	   some	cases donemap may not be set
+	   for complete	transfer
+	 */
+	skipmap	= isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+	tempskipmap = ~skipmap;
+	tempskipmap &= 0xffff;
+
+	if (tempskipmap) {
+		donemap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltddonemap,
+					   donemap);
+		skipmap	|= donemap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+		qha = &atlqha;
+		donemap	|= pendingmap;
+		tempskipmap &= ~donemap;
+	}  else {
+
+	/*if sof interrupt enabled */
+
+		/*if there is something	pending	, put this transfer in */
+		if (pendingmap)	{
+			pehci_hcd_schedule_pending_ptds(hcd, pendingmap, (u8)
+				TD_PTD_BUFF_TYPE_ATL,
+				1);
+		}
+		goto exit;
+	}
+#else
+
+	donemap	= isp1763_reg_read16(hcd->dev, hcd->regs.atltddonemap, donemap);
+	if (donemap) {
+
+
+		pehci_info("DoneMap Value in ATL Worker	%x\n", donemap);
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+					   skipmap);
+		skipmap	|= donemap;
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+		qha = &atlqha;
+	} else {
+		pehci_info("Done Map Value is 0x%X \n",	donemap);
+		pehci_entry("--	%s: Exit abnormally with DoneMap all zero \n",
+			    __FUNCTION__);
+		goto exit;
+
+	}
+#endif
+
+	/*read the interrupt mask registers */
+	ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or,
+				    ormask);
+
+
+	/*this map is used only	to update and
+	 * scheduling for the tds who are not
+	 * complete. the tds those are complete
+	 * new schedule	will happen from
+	 * td_ptd_submit_urb routine
+	 * */
+	donetoclear = donemap;
+	/*we will be processing	skipped	tds also */
+	donetoclear |= tempskipmap;
+	/*process all the endpoints first those	are done */
+	while (donetoclear) {
+		/*index	is the number of endpoint open currently */
+		index =	donetoclear & mask;
+		donetoclear &= ~mask;
+		mask <<= 1;
+		/*what if we are in the	middle of schedule
+		   where nothing is done
+		 */
+		if (!index) {
+			location++;
+			continue;
+		}
+
+		/*read our td_ptd_map */
+		td_ptd_map = &ptd_map_buff->map_list[location];
+
+		/*urb is in remove */
+		if (td_ptd_map->state == TD_PTD_NEW ||
+			td_ptd_map->state == TD_PTD_REMOVE)	{
+			pehci_check
+				("atl td is being removed,map %x, skipmap %x\n",
+				 td_ptd_map->ptd_bitmap, skipmap);
+			pehci_check("temp skipmap %x, pendign map %x,done %x\n",
+				    tempskipmap, pendingmap, donemap);
+
+			/*unlink urb will take care of this */
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			location++;
+			continue;
+		}
+
+
+		/*move to the next endpoint */
+		location++;
+		/*endpoint, td,	urb and	memory
+		 * for current endpoint*/
+		qh = td_ptd_map->qh;
+		qtd = td_ptd_map->qtd;
+		if (!qh	|| !qtd) {
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			continue;
+		}
+#ifdef MSEC_INT_BASED
+		/*new td must be scheduled */
+		if ((qtd->state	& QTD_STATE_NEW)	/*&&
+							   (pendingmap & td_ptd_map->ptd_bitmap) */ ) {
+			/*this td will come here first time from
+			 *pending tds, so its qh->hw_current needs to
+			 * adjusted
+			 */
+			qh->hw_current = QTD_NEXT(qtd->qtd_dma);
+			goto schedule;
+		}
+#endif
+		urb = qtd->urb;
+		if (urb	== NULL) {
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			continue;
+		}
+		urbpriv	= (urb_priv_t *) urb->hcpriv;
+		mem_addr = &qtd->mem_addr;
+
+#ifdef MSEC_INT_BASED
+		/*check	here for the td	if its done */
+		if (donemap & td_ptd_map->ptd_bitmap) {
+			/*nothing to do	*/
+			;
+		} else {
+			/*if td	is not done, lets check	how long
+			   its been scheduled
+			 */
+			if (tempskipmap	& td_ptd_map->ptd_bitmap) {
+				/*i will give 20 msec to complete */
+				if (urbpriv->timeout < 20) {
+					urbpriv->timeout++;
+					continue;
+				}
+				urbpriv->timeout++;
+				/*otherwise check its status */
+			}
+
+		}
+#endif
+		memset(qha, 0, sizeof(struct _isp1763_qha));
+
+		/*read this ptd	from the ram address,address is	in the
+		   td_ptd_map->ptd_header_addr */
+		isp1763_mem_read(hcd->dev, td_ptd_map->ptd_header_addr,	0,
+				 (u32 *) (qha),	PHCI_QHA_LENGTH, 0);
+
+#ifdef PTD_DUMP_COMPLETE
+		printk("ATL PTD header after COMPLETION\n");
+		printk("CDW0: 0x%08X\n", qha->td_info1);
+		printk("CDW1: 0x%08X\n", qha->td_info2);
+		printk("CDW2: 0x%08X\n", qha->td_info3);
+		printk("CDW3: 0x%08X\n", qha->td_info4);
+#endif
+
+#ifdef MSEC_INT_BASED
+		/*since	we are running on skipmap
+		   tds will be checked for completion state
+		 */
+		if ((qha->td_info1 & QHA_VALID)) {
+
+			pehci_check
+				("pendign map %x, donemap %x, tempskipmap %x\n",
+				 pendingmap, donemap, tempskipmap);
+			/*this could be	one of the unprotected urbs, clear it */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			/*here also we need to increment the tds timeout count */
+			urbpriv->timeout++;
+			continue;
+		} else {
+			/*this td is going to be done,
+			   this	td could be the	one un-skipped but no donemap or
+			   maybe it could be one of those where	we get unprotected urbs,
+			   so checking against tempskipmap may not give	us correct td
+			 */
+
+			skipmap	|= td_ptd_map->ptd_bitmap;
+			isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+					    skipmap);
+
+			/*of course this is going to be	as good
+			   as td that is done and donemap is set
+			   also	skipmap	is set
+			 */
+			donemap	|= td_ptd_map->ptd_bitmap;
+		}
+#endif
+		/*clear	the corrosponding mask register	*/
+		ormask &= ((~td_ptd_map->ptd_bitmap) & 0xffff);
+		isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+			ormask);
+
+		ptd_map_buff->active_ptds--;
+
+		urbpriv->timeout = 0;
+
+		/*take care of errors */
+		pehci_hcd_update_error_status(qha->td_info4, urb);
+		/*halted, need to finish all the transfer on this endpoint */
+		if (qha->td_info4 & PTD_STATUS_HALTED) {
+
+			printk(KERN_NOTICE "Endpoint is	halted\n");
+			qtd->state |= QTD_STATE_LAST;
+
+			donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			/*in case pending */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			/*in case of halt, next	transfer will start with toggle
+			   zero,USB speck, 5.8.5 */
+			qh->datatoggle = td_ptd_map->datatoggle	= 0;
+			/*cleanup the ping */
+			qh->ping = 0;
+			/*force	cleanup	after this */
+			dontschedule = 1;
+			goto copylength;
+		}
+
+
+
+		/*read the reload count	*/
+		rl = (qha->td_info3 >> 23);
+		rl &= 0xf;
+
+
+
+		/*if there is a	transaction error and the status is not	halted,
+		 * process whatever the	length we got.if the length is what we
+		 * expected complete the transfer*/
+		if ((qha->td_info4 & PTD_XACT_ERROR) &&
+			!(qha->td_info4 & PTD_STATUS_HALTED) &&
+			(qha->td_info4 & QHA_ACTIVE)) {
+
+			if (PTD_XFERRED_LENGTH(qha->td_info4) == qtd->length) {
+				;	/*nothing to do	its fake */
+			} else {
+
+				pehci_print
+					("xact error, info1 0x%08x,info4 0x%08x\n",
+					 qha->td_info1,	qha->td_info4);
+
+				/*if this is the case then we need to
+				   resubmit the	td again */
+				qha->td_info1 |= QHA_VALID;
+				skipmap	&= ~td_ptd_map->ptd_bitmap;
+				ormask |= td_ptd_map->ptd_bitmap;
+				donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+
+				/*set the retry	count to 3 again */
+				qha->td_info4 |= (rl <<	19);
+				/*set the active bit, if cleared, will be cleared if we	have some length */
+				qha->td_info4 |= QHA_ACTIVE;
+
+				/*clear	the xact error */
+				qha->td_info4 &= ~PTD_XACT_ERROR;
+				isp1763_reg_write16(hcd->dev,
+						    hcd->regs.atl_irq_mask_or,
+						    ormask);
+
+				/*copy back into the header, payload is	already
+				 * present no need to write again
+				 */
+				isp1763_mem_write(hcd->dev,
+						  td_ptd_map->ptd_header_addr,
+						  0, (u32 *) (qha),
+						  PHCI_QHA_LENGTH, 0);
+				/*unskip this td */
+				isp1763_reg_write16(hcd->dev,
+						    hcd->regs.atltdskipmap,
+						    skipmap);
+				continue;
+			}
+			goto copylength;
+		}
+
+		/*check	for the	nak count and active condition
+		 * to reload the ptd if	needed*/
+		nakcount = qha->td_info4 >> 19;
+		nakcount &= 0xf;
+		active = qha->td_info4 & QHA_ACTIVE;
+		/*if nak count is zero and active bit is set , it
+		 *means	that device is naking and need to reload
+		 *the same td*/
+		if (!nakcount && active) {
+			pehci_info("%s:	ptd is going for reload,length %d\n",
+				   __FUNCTION__, length);
+			/*make this td valid */
+			qha->td_info1 |= QHA_VALID;
+			donemap	&= ((~td_ptd_map->ptd_bitmap & 0xffff));
+			/*just like fresh td */
+
+			/*set the retry	count to 3 again */
+			qha->td_info4 |= (rl <<	19);
+			qha->td_info4 &= ~0x3;
+			qha->td_info4 |= (0x2 << 23);
+			ptd_map_buff->active_ptds++;
+			skipmap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+			ormask |= td_ptd_map->ptd_bitmap;
+			isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or,
+					    ormask);
+			/*copy back into the header, payload is	already
+			 * present no need to write again */
+			isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr,
+					  0, (u32 *) (qha), PHCI_QHA_LENGTH, 0);
+			/*unskip this td */
+			isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+					    skipmap);
+			continue;
+		}
+
+		copylength:
+		/*read the length transferred */
+		length = PTD_XFERRED_LENGTH(qha->td_info4);
+
+
+		/*short	complete in case of BULK only */
+		if ((length < qtd->length) && usb_pipebulk(urb->pipe)) {
+
+			/*if current ptd is not	able to	fetech enough data as
+			 * been	asked then device has no data, so complete this	transfer
+			 * */
+			/*can we complete our transfer here */
+			if ((urb->transfer_flags & URB_SHORT_NOT_OK)) {
+				pehci_check
+					("short	read, length %d(expected %d)\n",
+					 length, qtd->length);
+				urb->status = -EREMOTEIO;
+				/*if this is the only td,donemap will be cleared
+				   at completion, otherwise take the next one
+				 */
+				donemap	&= ((~td_ptd_map->ptd_bitmap) &	0xffff);
+				ptd_map_buff->pending_ptd_bitmap &=
+					((~td_ptd_map->ptd_bitmap) & 0xffff);
+				/*force	the cleanup from here */
+				dontschedule = 1;
+			}
+
+			/*this will be the last	td,in case of short read/write */
+			/*donemap, pending maps	will be	handled	at the while scheduling	or completion */
+			qtd->state |= QTD_STATE_LAST;
+
+		}
+		/*preserve the current data toggle */
+		qh->datatoggle = td_ptd_map->datatoggle	=
+			PTD_NEXTTOGGLE(qha->td_info4);
+		qh->ping = PTD_PING_STATE(qha->td_info4);
+		/*copy data from */
+		switch (PTD_PID(qha->td_info2))	{
+		case IN_PID:
+			qh->ping = 0;
+			/*do read only when there is some data */
+			if (length && (length <= HC_ATL_PL_SIZE)) {
+				isp1763_mem_read(hcd->dev,
+						 (u32) mem_addr->phy_addr, 0,
+						 (u32*) (le32_to_cpu(qtd->hw_buf[0])), length, 0);
+#if 0
+			//	printk("IN PayLoad length:%d\n", length); 
+			if(length<=4)	{
+					int i=0;
+					int *data_addr= qtd->hw_buf[0];
+					printk("\n");
+					for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++);
+					printk("\n");
+				}
+#endif
+			}
+
+		case OUT_PID:
+			urb->actual_length += length;
+			qh->hw_current = qtd->hw_next;
+			phci_hcd_mem_free(&qtd->mem_addr);
+			qtd->state |= QTD_STATE_DONE;
+
+			break;
+		case SETUP_PID:
+			qh->hw_current = qtd->hw_next;
+			phci_hcd_mem_free(&qtd->mem_addr);
+			qtd->state |= QTD_STATE_DONE;
+			break;
+		}
+
+		if (qtd->state & QTD_STATE_LAST) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, regs);
+#else
+			pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+			if (dontschedule) {	/*cleanup will start from drivers */
+				dontschedule = 0;
+				/*so that we can take next one */
+				qh->qh_state = QH_STATE_TAKE_NEXT;
+				continue;
+			}
+			/*take the next	if in the queue	*/
+			if (!list_empty(&qh->qtd_list))	{
+				struct list_head *head;
+				/*last td of previous urb */
+				head = &qh->qtd_list;
+				qtd = list_entry(head->next, struct ehci_qtd,
+						 qtd_list);
+				td_ptd_map->qtd	= qtd;
+				qh->hw_current = cpu_to_le32(qtd);
+				qh->qh_state = QH_STATE_LINKED;
+
+			} else {
+				td_ptd_map->qtd	=
+					(struct	ehci_qtd *) le32_to_cpu(0);
+				qh->hw_current = cpu_to_le32(0);
+				qh->qh_state = QH_STATE_TAKE_NEXT;
+				donemap	&= ((~td_ptd_map->ptd_bitmap & 0xffff));
+				ptd_map_buff->pending_ptd_bitmap &=
+					((~td_ptd_map->ptd_bitmap) & 0xffff);
+				continue;
+			}
+		}
+
+#ifdef MSEC_INT_BASED
+		schedule:
+#endif
+		{
+			/*current td comes from	qh->hw_current */
+			ptd_map_buff->pending_ptd_bitmap &=
+				((~td_ptd_map->ptd_bitmap) & 0xffff);
+			td_ptd_map->qtd	=
+				(struct	ehci_qtd
+				 *) (le32_to_cpu(qh->hw_current));
+			qtd = td_ptd_map->qtd;
+			ormask |= td_ptd_map->ptd_bitmap;
+			ptd_map_buff->active_ptds++;
+			pehci_hcd_qtd_schedule(hcd, qtd, qh, td_ptd_map);
+		}
+
+	}			/*end of while */
+
+/*clear	all the	tds inside this	routine*/
+	skipmap	&= ((~donemap) & 0xffff);
+	isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap);
+	ormask |= donemap;
+	isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, ormask);
+exit:
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+/*--------------------------------------------------------*
+  root hub functions
+ *--------------------------------------------------------*/
+
+/*return root hub descriptor, can not fail*/
+static void
+pehci_hub_descriptor(phci_hcd *	hcd, struct usb_hub_descriptor *desc)
+{
+	u32 ports = 0;
+	u16 temp = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	ports =	0x11;
+	ports =	ports &	0xf;
+
+	pehci_info("%s:	number of ports	%d\n", __FUNCTION__, ports);
+
+	desc->bDescriptorType =	0x29;
+	desc->bPwrOn2PwrGood = 10;
+
+	desc->bHubContrCurrent = 0;
+
+	desc->bNbrPorts	= ports;
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 +	2 * temp;
+	/* two bitmaps:	 ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+
+	memset(&desc->DeviceRemovable[0], 0, temp);
+	memset(&desc->PortPwrCtrlMask[temp], 0xff, temp);
+
+	temp = 0x0008;		/* per-port overcurrent	reporting */
+	temp |=	0x0001;		/* per-port power control */
+	temp |=	0x0080;		/* per-port indicators (LEDs) */
+	desc->wHubCharacteristics = cpu_to_le16(temp);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+/*after	reset on root hub,
+ * device high speed or	non-high speed
+ * */
+static int
+phci_check_reset_complete(phci_hcd * hcd, int index, int port_status)
+{
+	pehci_print("check reset complete\n");
+	if (!(port_status & PORT_CONNECT)) {
+		hcd->reset_done[index] = 0;
+		return port_status;
+	}
+
+	/* if reset finished and it's still not	enabled	-- handoff */
+	if (!(port_status & PORT_PE)) {
+		printk("port %d	full speed --> companion\n", index + 1);
+		port_status |= PORT_OWNER;
+		isp1763_reg_write32(hcd->dev, hcd->regs.ports[index],
+				    port_status);
+
+	} else {
+		pehci_print("port %d high speed\n", index + 1);
+	}
+
+	return port_status;
+
+}
+
+/*----------------------------------------------*
+  host controller initialization, removal functions
+ *----------------------------------------------*/
+
+
+/*initialize all three buffer(iso/atl/int) type	headers*/
+static void
+pehci_hcd_init_map_buffers(phci_hcd * phci)
+{
+	td_ptd_map_buff_t *ptd_map_buff;
+	u8 buff_type, ptd_index;
+	u32 bitmap;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_print("phci_init_map_buffers(phci	= 0x%p)\n", phci);
+	/* initialize for each buffer type */
+	for (buff_type = 0; buff_type <	TD_PTD_TOTAL_BUFF_TYPES; buff_type++) {
+		ptd_map_buff = &(td_ptd_map_buff[buff_type]);
+		ptd_map_buff->buffer_type = buff_type;
+		ptd_map_buff->active_ptds = 0;
+		ptd_map_buff->total_ptds = 0;
+		/*each bufer type can have atleast 32 ptds */
+		ptd_map_buff->max_ptds = 16;
+		ptd_map_buff->active_ptd_bitmap	= 0;
+		/*everything skipped */
+		/*nothing is pending */
+		ptd_map_buff->pending_ptd_bitmap = 0x00000000;
+
+		/* For each ptd	index of this buffer, set the fiedls */
+		bitmap = 0x00000001;
+		for (ptd_index = 0; ptd_index <	TD_PTD_MAX_BUFF_TDS;
+			ptd_index++) {
+			/*datatoggle zero */
+			ptd_map_buff->map_list[ptd_index].datatoggle = 0;
+			/*td state is not used */
+			ptd_map_buff->map_list[ptd_index].state	= TD_PTD_NEW;
+			/*no endpoint, no qtd */
+			ptd_map_buff->map_list[ptd_index].qh = NULL;
+			ptd_map_buff->map_list[ptd_index].qtd =	NULL;
+			ptd_map_buff->map_list[ptd_index].ptd_header_addr =
+				0xFFFF;
+		}		/* for(	ptd_index */
+	}			/* for(buff_type */
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}				/* phci_init_map_buffers */
+
+
+/*put the host controller into operational mode
+ * called phci_hcd_start routine,
+ * return 0, success else
+ * timeout, fails*/
+
+static int
+pehci_hcd_start_controller(phci_hcd * hcd)
+{
+	u32 temp = 0;
+	u32 command = 0;
+	int retval = 0;
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__);
+
+
+	command	= isp1763_reg_read16(hcd->dev, hcd->regs.command, command);
+	printk(KERN_NOTICE "HC Command Reg val ...1 %x\n", command);
+
+	/*initialize the host controller */
+	command	|= CMD_RUN;
+
+	isp1763_reg_write16(hcd->dev, hcd->regs.command, command);
+
+
+	command	&= 0;
+
+	command	= isp1763_reg_read16(hcd->dev, hcd->regs.command, command);
+	printk(KERN_NOTICE "HC Command Reg val ...2 %x\n", command);
+
+	/*should be in operation in 1000 usecs */
+	if ((retval =
+		pehci_hcd_handshake(hcd, hcd->regs.command, CMD_RUN, CMD_RUN,
+		100000))) {
+		err("Host is not up(CMD_RUN) in	1000 usecs\n");
+		return retval;
+	}
+
+	printk(KERN_NOTICE "ISP1763 HC is running \n");
+
+
+	/*put the host controller to ehci mode */
+	command	&= 0;
+	command	|= 1;
+
+	isp1763_reg_write16(hcd->dev, hcd->regs.configflag, command);
+	mdelay(5);
+
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.configflag, temp);
+	pehci_print("%s: Config	Flag reg value:	0x%08x\n", __FUNCTION__, temp);
+
+	/*check	if ehci	mode switching is correct or not */
+	if ((retval =
+		pehci_hcd_handshake(hcd, hcd->regs.configflag, 1, 1, 100))) {
+		err("Host is not into ehci mode	in 100 usecs\n");
+		return retval;
+	}
+
+	mdelay(5);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	printk(KERN_NOTICE "-- %s: Exit\n", __FUNCTION__);
+	return retval;
+}
+
+
+/*enable the interrupts
+ *called phci_1763_start routine
+ * return void*/
+static void
+pehci_hcd_enable_interrupts(phci_hcd * hcd)
+{
+	u32 temp = 0;
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__);
+	/*disable the interrupt	source */
+	temp &=	0;
+	/*clear	all the	interrupts that	may be there */
+	temp |=	INTR_ENABLE_MASK;
+	isp1763_reg_write16(hcd->dev, hcd->regs.interrupt, temp);
+
+	/*enable interrupts */
+	temp = 0;
+	
+#ifdef OTG_PACKAGE
+	temp |= INTR_ENABLE_MASK | HC_OTG_INT;
+#else
+	temp |= INTR_ENABLE_MASK;
+#endif	
+	pehci_print("%s: enabled mask 0x%08x\n", __FUNCTION__, temp);
+	isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, temp);
+
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.interruptenable, temp);
+	pehci_print("%s: Intr enable reg value:	0x%08x\n", __FUNCTION__, temp);
+	
+#ifdef HCD_PACKAGE
+	temp = 0;
+	temp = isp1763_reg_read32(hcd->dev, HC_INT_THRESHOLD_REG, temp);
+//	temp |= 0x0800000F;
+	temp |= 0x0100000F;//125 micro second minimum width between two edge interrupts, 500ns int will remain low
+	//	15/30MHz=500 ns
+	isp1763_reg_write32(hcd->dev, HC_INT_THRESHOLD_REG, temp);
+#endif
+	/*enable the global interrupt */
+	temp &=	0;
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.hwmodecontrol, temp);
+	temp |=	0x01;		/*enable the global interrupt */
+#ifdef EDGE_INTERRUPT
+	temp |=	0x02;		/*enable the edge interrupt */
+#endif
+
+#ifdef POL_HIGH_INTERRUPT
+	temp |=	0x04;		/* enable interrupt polarity high */
+#endif
+
+	isp1763_reg_write16(hcd->dev, hcd->regs.hwmodecontrol, temp);
+
+	/*maximum rate is one msec */
+	/*enable the atl interrupts OR and AND mask */
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_and, temp);
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, temp);
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_and, temp);
+	temp = 0x0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, temp);
+	temp = 0;
+	isp1763_reg_write16(hcd->dev, hcd->regs.iso_irq_mask_and, temp);
+	temp = 0xffff;
+	isp1763_reg_write16(hcd->dev, hcd->regs.iso_irq_mask_or, temp);
+
+	temp = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or, temp);
+	pehci_print("%s:Iso irq	mask reg value:	0x%08x\n", __FUNCTION__, temp);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+/*initialize the host controller register map from Isp1763 to EHCI */
+static void
+pehci_hcd_init_reg(phci_hcd * hcd)
+{
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	/* scratch pad for the test */
+	hcd->regs.scratch = HC_SCRATCH_REG;
+
+	/*make a copy of our interrupt locations */
+	hcd->regs.command = HC_USBCMD_REG;
+	hcd->regs.usbstatus = HC_USBSTS_REG;
+	hcd->regs.usbinterrupt = HC_INTERRUPT_REG_EHCI;
+
+	hcd->regs.hcsparams = HC_SPARAMS_REG;
+	hcd->regs.frameindex = HC_FRINDEX_REG;
+
+	/*transfer specific registers */
+	hcd->regs.hwmodecontrol	= HC_HWMODECTRL_REG;
+	hcd->regs.interrupt = HC_INTERRUPT_REG;
+	hcd->regs.interruptenable = HC_INTENABLE_REG;
+	hcd->regs.atl_irq_mask_and = HC_ATL_IRQ_MASK_AND_REG;
+	hcd->regs.atl_irq_mask_or = HC_ATL_IRQ_MASK_OR_REG;
+
+	hcd->regs.int_irq_mask_and = HC_INT_IRQ_MASK_AND_REG;
+	hcd->regs.int_irq_mask_or = HC_INT_IRQ_MASK_OR_REG;
+	hcd->regs.iso_irq_mask_and = HC_ISO_IRQ_MASK_AND_REG;
+	hcd->regs.iso_irq_mask_or = HC_ISO_IRQ_MASK_OR_REG;
+	hcd->regs.buffer_status	= HC_BUFFER_STATUS_REG;
+	hcd->regs.interruptthreshold = HC_INT_THRESHOLD_REG;
+	/*initialization specific */
+	hcd->regs.reset	= HC_RESET_REG;
+	hcd->regs.configflag = HC_CONFIGFLAG_REG;
+	hcd->regs.ports[0] = HC_PORTSC1_REG;
+	hcd->regs.ports[1] = 0;	/*port1,port2,port3 status reg are removed */
+	hcd->regs.ports[2] = 0;
+	hcd->regs.ports[3] = 0;
+	hcd->regs.pwrdwn_ctrl =	HC_POWER_DOWN_CONTROL_REG;
+	/*transfer registers */
+	hcd->regs.isotddonemap = HC_ISO_PTD_DONEMAP_REG;
+	hcd->regs.isotdskipmap = HC_ISO_PTD_SKIPMAP_REG;
+	hcd->regs.isotdlastmap = HC_ISO_PTD_LASTPTD_REG;
+
+	hcd->regs.inttddonemap = HC_INT_PTD_DONEMAP_REG;
+
+	hcd->regs.inttdskipmap = HC_INT_PTD_SKIPMAP_REG;
+	hcd->regs.inttdlastmap = HC_INT_PTD_LASTPTD_REG;
+
+	hcd->regs.atltddonemap = HC_ATL_PTD_DONEMAP_REG;
+	hcd->regs.atltdskipmap = HC_ATL_PTD_SKIPMAP_REG;
+	hcd->regs.atltdlastmap = HC_ATL_PTD_LASTPTD_REG;
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+}
+
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static void
+pehci_interrupt_handler(phci_hcd * hcd, struct pt_regs *regs)
+{
+	spin_lock(&hcd->lock);
+#ifdef CONFIG_ISO_SUPPORT
+	phcd_iso_handler(hcd, regs);
+#endif
+	pehci_hcd_intl_worker(hcd, regs);
+	pehci_hcd_atl_worker(hcd, regs);
+	spin_unlock(&hcd->lock);
+	return;
+}
+#else
+static void
+pehci_interrupt_handler(phci_hcd * hcd)
+{
+	spin_lock(&hcd->lock);
+#ifdef CONFIG_ISO_SUPPORT
+	pehci_hcd_iso_worker(hcd);
+#endif
+	pehci_hcd_intl_worker(hcd);
+	pehci_hcd_atl_worker(hcd);
+	spin_unlock(&hcd->lock);
+	return;
+}
+#endif
+irqreturn_t pehci_hcd_irq(struct usb_hcd *usb_hcd)
+{
+
+	int work = 0;
+	phci_hcd *pehci_hcd;
+	struct isp1763_dev *dev;
+	u32 intr = 0;
+	u32 resume=0;
+	u32 temp=0;
+	u32 irq_mask = 0;
+
+	if (!(usb_hcd->state & USB_STATE_READY)) {
+		info("interrupt	handler	state not ready	yet\n");
+	usb_hcd->state=USB_STATE_READY;
+	//	return IRQ_NONE;
+	}
+
+	/*our host */
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	dev = pehci_hcd->dev;
+
+	spin_lock(&pehci_hcd->lock);
+	dev->int_reg = isp1763_reg_read16(dev, HC_INTERRUPT_REG, dev->int_reg);
+	/*Clear the interrupt*/
+	isp1763_reg_write16(dev, HC_INTERRUPT_REG, dev->int_reg);
+
+	irq_mask = isp1763_reg_read16(dev, HC_INTENABLE_REG, irq_mask);
+	dev->int_reg &= irq_mask;
+
+	intr = dev->int_reg;
+
+
+	if (atomic_read(&pehci_hcd->nuofsofs)) {
+		spin_unlock(&pehci_hcd->lock);
+		return IRQ_HANDLED;
+	}
+	atomic_inc(&pehci_hcd->nuofsofs);
+
+	irq_mask=isp1763_reg_read32(dev,HC_USBSTS_REG,0);
+	isp1763_reg_write32(dev,HC_USBSTS_REG,irq_mask);
+	if(irq_mask & 0x4){  // port status register.
+		if(intr & 0x50) {   // OPR register change
+			temp=isp1763_reg_read32(dev,HC_PORTSC1_REG,0);
+			if(temp & 0x4){   // Force resume bit is set
+				if (dev) {
+					if (dev->driver) {
+						if (dev->driver->resume) {
+						dev->driver->resume(dev);
+							resume=1;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags);
+
+#ifndef THREAD_BASED
+/*-----------------------------------------------------------*/
+#ifdef MSEC_INT_BASED
+	work = 1;
+#else
+	if (intr & (HC_MSEC_INT	& INTR_ENABLE_MASK)) {
+		work = 1;	/* phci_iso_worker(hcd); */
+	}
+
+#ifdef USBNET 
+	if (intr & HC_MSOF_INT ) {
+		struct list_head *pos, *q;
+	
+		list_for_each_safe(pos, q, &pehci_hcd->cleanup_urb.urb_list) {
+		struct isp1763_async_cleanup_urb *tmp;
+		
+			tmp = list_entry(pos, struct isp1763_async_cleanup_urb, urb_list);
+			if (tmp) {
+				spin_unlock(&pehci_hcd->lock);
+				usb_hcd_giveback_urb(usb_hcd, tmp->urb, tmp->urb->status);
+				spin_lock(&pehci_hcd->lock);
+
+				list_del(pos);
+				if(tmp)
+				kfree(tmp);
+			}
+		}
+		isp1763_reg_write16(dev, HC_INTENABLE_REG, INTR_ENABLE_MASK );
+	}
+#endif
+
+
+	if (intr & (HC_INTL_INT	& INTR_ENABLE_MASK)) {
+	//	spin_lock(&pehci_hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_intl_worker(pehci_hcd, regs);
+#else
+		pehci_hcd_intl_worker(pehci_hcd);
+#endif
+	//	spin_unlock(&pehci_hcd->lock);
+		work = 0;	/*phci_intl_worker(hcd); */
+	}
+	
+	if (intr & (HC_ATL_INT & INTR_ENABLE_MASK)) {
+	//	spin_lock(&pehci_hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_atl_worker(pehci_hcd, regs);
+#else
+		pehci_hcd_atl_worker(pehci_hcd);
+#endif
+	//	spin_unlock(&pehci_hcd->lock);
+		work = 0;	/*phci_atl_worker(hcd);	*/
+	}
+#ifdef CONFIG_ISO_SUPPORT
+	if (intr & (HC_ISO_INT & INTR_ENABLE_MASK)) {
+	//	spin_lock(&pehci_hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_iso_worker(pehci_hcd);
+#else
+		pehci_hcd_iso_worker(pehci_hcd);
+#endif
+	//	spin_unlock(&pehci_hcd->lock);
+		work = 0;	/*phci_atl_worker(hcd); */
+	}
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	if (work){
+		spin_unlock(&pehci_hcd->lock);
+		pehci_interrupt_handler(pehci_hcd, regs);
+		spin_lock(&pehci_hcd->lock);
+	}
+#else
+	if (work){
+		spin_unlock(&pehci_hcd->lock);
+		pehci_interrupt_handler(pehci_hcd);
+		spin_lock(&pehci_hcd->lock);
+	}
+#endif
+
+/*-----------------------------------------------------------*/
+#else
+	if ((intr & (HC_INTL_INT & INTR_ENABLE_MASK)) ||(intr & (HC_ATL_INT & INTR_ENABLE_MASK)))
+	{ //send
+		st_UsbIt_Msg_Struc *stUsbItMsgSnd ;
+		
+		stUsbItMsgSnd = (st_UsbIt_Msg_Struc *)kmalloc(sizeof(st_UsbIt_Msg_Struc), GFP_ATOMIC);
+		if (!stUsbItMsgSnd) return -ENOMEM;
+		
+		memset(stUsbItMsgSnd, 0, sizeof(stUsbItMsgSnd));
+		
+		stUsbItMsgSnd->usb_hcd = usb_hcd;
+		stUsbItMsgSnd->uIntStatus = NO_SOF_REQ_IN_ISR;
+		list_add_tail(&(stUsbItMsgSnd->list), &(g_messList.list));
+
+		pehci_print("\n------------- send mess : %d------------\n",stUsbItMsgSnd->uIntStatus);
+		if ((g_stUsbItThreadHandler.phThreadTask != NULL) && (g_stUsbItThreadHandler.lThrdWakeUpNeeded == 0))
+		{
+			pehci_print("\n------- wake up thread : %d-----\n",stUsbItMsgSnd->uIntStatus);
+			g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1;
+			wake_up(&(g_stUsbItThreadHandler.ulThrdWaitQhead));
+		}
+	}
+/*-----------------------------------------------------------*/
+#endif
+
+	atomic_dec(&pehci_hcd->nuofsofs);
+	spin_unlock(&pehci_hcd->lock);
+		if(resume){
+			usb_hcd_poll_rh_status(usb_hcd);
+	}
+	return IRQ_HANDLED;
+}
+
+/*reset	the host controller
+ *called phci_hcd_start	routine
+ *return 0, success else
+ *timeout, fails*/
+static int
+pehci_hcd_reset(struct usb_hcd *usb_hcd)
+{
+	u32 command = 0;
+	u32 temp = 0;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+	printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__);
+	pehci_hcd_init_reg(hcd);
+	printk("chipid %x \n", isp1763_reg_read32(hcd->dev, HC_CHIP_ID_REG, temp)); //0x70
+
+	/*reset	the atx controller */
+	temp &=	0;
+	temp |=	8;
+	isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp);
+	mdelay(10);
+	
+	/*reset	the host controller */
+	temp &=	0;
+	temp |=	1;
+	isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp);
+
+	command	= 0;
+	do {
+
+		temp = isp1763_reg_read16(hcd->dev, hcd->regs.reset, temp);
+		mdelay(10);
+		command++;
+		if (command > 100) {
+			printk("not able to reset\n");
+			break;
+		}
+	} while	(temp &	0x01);
+
+
+	/*reset	the ehci controller registers */
+	temp = 0;
+	temp |=	(1 << 1);
+	isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp);
+	command	= 0;
+	do {
+		temp = isp1763_reg_read16(hcd->dev, hcd->regs.reset, temp);
+		mdelay(10);
+		command++;
+		if (command > 100) {
+			printk("not able to reset\n");
+			break;
+		}
+	} while	(temp &	0x02);
+
+	/*read the command register */
+	command	= isp1763_reg_read16(hcd->dev, hcd->regs.command, command);
+
+	command	|= CMD_RESET;
+	/*write	back and wait for, 250 msec */
+	isp1763_reg_write16(hcd->dev, hcd->regs.command, command);
+	/*wait for maximum 250 msecs */
+	mdelay(200);
+	printk("command	%x\n",
+		isp1763_reg_read16(hcd->dev, hcd->regs.command, command));
+	printk(KERN_NOTICE "-- %s: Exit	\n", __FUNCTION__);
+	return 0;
+}
+
+/*host controller initialize routine,
+ *called by phci_hcd_probe
+ * */
+static int
+pehci_hcd_start(struct usb_hcd *usb_hcd)
+{
+
+	int retval;
+	int count = 0;
+	phci_hcd *pehci_hcd = NULL;
+	u32 temp = 0;
+	u32 hwmodectrl = 0;
+	u32 ul_scratchval = 0;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+
+	spin_lock_init(&pehci_hcd->lock);
+	atomic_set(&pehci_hcd->nuofsofs, 0);
+	atomic_set(&pehci_hcd->missedsofs, 0);
+
+	/*Initialize host controller registers */
+	pehci_hcd_init_reg(pehci_hcd);
+
+	/*reset	the host controller */
+	retval = pehci_hcd_reset(usb_hcd);
+	if (retval) {
+		err("phci_1763_start: error failing with status	%x\n", retval);
+		return retval;
+	}
+
+	hwmodectrl =
+		isp1763_reg_read16(pehci_hcd->dev,
+				   pehci_hcd->regs.hwmodecontrol, hwmodectrl);
+#ifdef DATABUS_WIDTH_16
+	printk(KERN_NOTICE "Mode Ctrl Value before 16width: %x\n", hwmodectrl);
+	hwmodectrl &= 0xFFEF;	/*enable the 16	bit bus	*/
+	hwmodectrl |= 0x0400;	/*enable common	int */
+#else
+	printk(KERN_NOTICE "Mode Ctrl Value before 8width : %x\n", hwmodectrl);
+	hwmodectrl |= 0x0010;	/*enable the 8 bit bus */
+	hwmodectrl |= 0x0400;	/*enable common	int */
+#endif
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.hwmodecontrol,
+			    hwmodectrl);
+
+	hwmodectrl =
+		isp1763_reg_read16(pehci_hcd->dev,
+				   pehci_hcd->regs.hwmodecontrol, hwmodectrl);
+	hwmodectrl |=0x9;  //lock interface and enable global interrupt.
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.hwmodecontrol,
+		hwmodectrl);	
+	printk(KERN_NOTICE "Mode Ctrl Value after buswidth: %x\n", hwmodectrl);
+
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.scratch, 0x3344);
+
+	ul_scratchval =
+		isp1763_reg_read16(pehci_hcd->dev, pehci_hcd->regs.scratch,
+				   ul_scratchval);
+	printk(KERN_NOTICE "Scratch Reg	Value :	%x\n", ul_scratchval);
+	if (ul_scratchval != 0x3344) {
+		printk(KERN_NOTICE "Scratch Reg	Value Mismatch:	%x\n",
+		       ul_scratchval);
+
+	}
+
+
+	/*initialize the host controller initial values	*/
+	/*disable all the buffer */
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.buffer_status, 0);
+	/*skip all the transfers */
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltdskipmap,
+			    NO_TRANSFER_ACTIVE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttdskipmap,
+			    NO_TRANSFER_ACTIVE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotdskipmap,
+			    NO_TRANSFER_ACTIVE);
+	/*clear	done map */
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltddonemap,
+			    NO_TRANSFER_DONE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttddonemap,
+			    NO_TRANSFER_DONE);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotddonemap,
+			    NO_TRANSFER_DONE);
+	
+#ifdef HCD_PACKAGE
+	/*port1 as Host */
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0400);
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x0080);
+	/*port2 as Host */
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0000);
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x8000);
+	
+	#if 0 /* do not use bit 1&2 for pure host application */
+	ul_scratchval =	isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,0);
+	ul_scratchval |= 0x006;	
+	isp1763_reg_write32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,ul_scratchval);
+	#endif
+	
+#elif defined(HCD_DCD_PACKAGE)
+
+	/*port1 as device */
+	isp1763_reg_write16(pehci_hcd->dev,OTG_CTRL_SET_REG, 
+			OTG_CTRL_DMPULLDOWN |OTG_CTRL_DPPULLDOWN | 
+			OTG_CTRL_SW_SEL_HC_DC |OTG_CTRL_OTG_DISABLE);	/* pure	Device Mode and	OTG disabled */
+
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0480);
+	/*port2 as host */
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0000);
+	isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x8000);
+	ul_scratchval =
+		isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,
+		0);
+#endif
+
+	/*enable interrupts */
+	pehci_hcd_enable_interrupts(pehci_hcd);
+
+	/*put controller into operational mode */
+	retval = pehci_hcd_start_controller(pehci_hcd);
+	if (retval) {
+		err("phci_1763_start: error failing with status	%x\n", retval);
+		return retval;
+	}
+
+	/*Init the phci	qtd <->	ptd map	buffers	*/
+	pehci_hcd_init_map_buffers(pehci_hcd);
+
+	/*set last maps, for iso its only 1, else 32 tds bitmap	*/
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltdlastmap,
+			    0x8000);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttdlastmap, 0x80);
+	isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotdlastmap, 0x01);
+	/*iso transfers	are not	active */
+	pehci_hcd->next_uframe = -1;
+	pehci_hcd->periodic_sched = 0;
+	hwmodectrl =
+		isp1763_reg_read16(pehci_hcd->dev,
+				   pehci_hcd->regs.hwmodecontrol, hwmodectrl);
+
+	/*initialize the periodic list */
+	for (count = 0; count < PTD_PERIODIC_SIZE; count++) {
+		pehci_hcd->periodic_list[count].framenumber = 0;
+		INIT_LIST_HEAD(&pehci_hcd->periodic_list[count].sitd_itd_head);
+	}
+
+
+	/*set the state	of the host to ready,
+	 * start processing interrupts
+	 * */
+
+	usb_hcd->state = HC_STATE_RUNNING;
+	pehci_hcd->state = HC_STATE_RUNNING;
+
+
+	/*initialize root hub timer */
+	init_timer(&pehci_hcd->rh_timer);
+	/*initialize watchdog */
+	init_timer(&pehci_hcd->watchdog);
+
+	temp = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,
+				  temp);
+	
+	temp = 0x3e81bA0;
+#if 0
+	temp |=	0x306;
+#endif
+	isp1763_reg_write32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG, temp);
+	temp = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,
+				  temp);
+	printk(" Powerdown Reg Val: %x\n", temp);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+
+	return 0;
+}
+
+static void
+pehci_hcd_stop(struct usb_hcd *usb_hcd)
+{
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	/* no more interrupts ... */
+	if (usb_hcd->state == USB_STATE_RUNNING) {
+		mdelay(2);
+	}
+	if (in_interrupt()) {	/* must	not happen!! */
+		pehci_info("stopped in_interrupt!\n");
+
+		return;
+	}
+
+	/*power	off our	root hub */
+	pehci_rh_control(usb_hcd, ClearPortFeature, USB_PORT_FEAT_POWER,
+			 1, NULL, 0);
+
+	/*let the roothub power	go off */
+	mdelay(20);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+
+	return;
+}
+
+
+/*submit urb , other than root hub*/
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+pehci_hcd_urb_enqueue(struct usb_hcd *usb_hcd, struct usb_host_endpoint *ep,
+		struct urb *urb, gfp_t mem_flags)
+#else
+pehci_hcd_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, gfp_t mem_flags)
+#endif
+{
+
+	struct list_head qtd_list;
+	struct ehci_qh *qh = 0;
+	phci_hcd *pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	int status = 0;
+	int temp = 0, max = 0, num_tds = 0, mult = 0;
+	urb_priv_t *urb_priv = NULL;
+	unsigned long  flags;
+	
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	
+	if (unlikely(atomic_read(&urb->reject))) 
+		return -EINVAL;
+	
+	INIT_LIST_HEAD(&qtd_list);
+	urb->transfer_flags &= ~EHCI_STATE_UNLINK;
+
+
+	temp = usb_pipetype(urb->pipe);
+	max = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe));
+	
+
+	if (hcdpowerdown == 1) {
+		printk("Enqueue	hcd power down\n");
+		return -EINVAL;
+	}
+
+
+	/*pathch to get	the otg	device */
+	if (!hubdev || 
+		(urb->dev->parent==usb_hcd->self.root_hub && 
+		hubdev!=urb->dev)) {
+		if(urb->dev->parent== usb_hcd->self.root_hub) {
+			hubdev = urb->dev;
+		}
+	}
+
+	switch (temp) {
+	case PIPE_INTERRUPT:
+		/*only one td */
+		num_tds	= 1;
+		mult = 1 + ((max >> 11)	& 0x03);
+		max &= 0x07ff;
+		max *= mult;
+
+		if (urb->transfer_buffer_length	> max) {
+			err("interrupt urb length is greater then %d\n", max);
+			return -EINVAL;
+		}
+
+		if (hubdev && urb->dev->parent == usb_hcd->self.root_hub) {
+			huburb = urb;
+		}
+
+		break;
+
+	case PIPE_CONTROL:
+		/*calculate the	number of tds, follow 1	pattern	*/
+		if (No_Data_Phase && No_Status_Phase) {
+			printk("Only SetUP Phase\n");
+			num_tds	= (urb->transfer_buffer_length == 0) ? 1 :
+				((urb->transfer_buffer_length -
+				  1) / HC_ATL_PL_SIZE +	1);
+		} else if (!No_Data_Phase && No_Status_Phase) {
+			printk("SetUP Phase and	Data Phase\n");
+			num_tds	= (urb->transfer_buffer_length == 0) ? 2 :
+				((urb->transfer_buffer_length -
+				  1) / HC_ATL_PL_SIZE +	3);
+		} else if (!No_Data_Phase && !No_Status_Phase) {
+			num_tds	= (urb->transfer_buffer_length == 0) ? 2 :
+				((urb->transfer_buffer_length -
+				  1) / HC_ATL_PL_SIZE +	3);
+		}
+		
+		break;
+		
+	case PIPE_BULK:
+		num_tds	=
+			(urb->transfer_buffer_length - 1) / HC_ATL_PL_SIZE + 1;
+		if ((urb->transfer_flags & URB_ZERO_PACKET)
+			&& !(urb->transfer_buffer_length % max)) {
+			num_tds++;
+		}
+		
+		break;
+		
+#ifdef CONFIG_ISO_SUPPORT
+	case PIPE_ISOCHRONOUS:
+		/* Don't need to do anything here */
+		break;
+#endif
+	default:
+		return -EINVAL;	/*not supported	isoc transfers */
+
+
+	}
+
+#ifdef CONFIG_ISO_SUPPORT
+	if (temp != PIPE_ISOCHRONOUS) {
+#endif
+		/*make number of tds required */
+		urb_priv = kmalloc(sizeof(urb_priv_t) +
+				   num_tds * sizeof(struct ehci_qtd),
+				   mem_flags);
+		if (!urb_priv) {
+			err("memory   allocation error\n");
+			return -ENOMEM;
+		}
+
+		memset(urb_priv, 0, sizeof(urb_priv_t) +
+			num_tds * sizeof(struct ehci_qtd));
+		INIT_LIST_HEAD(&urb_priv->qtd_list);
+		urb_priv->qtd[0] = NULL;
+		urb_priv->length = num_tds;
+		{
+			int i =	0;
+			/*allocate number of tds here. better to do this in qtd_make routine */
+			for (i = 0; i <	num_tds; i++) {
+				urb_priv->qtd[i] =
+					phci_hcd_qtd_allocate(mem_flags);
+				if (!urb_priv->qtd[i]) {
+					phci_hcd_urb_free_priv(pehci_hcd,
+							       urb_priv, NULL);
+					return -ENOMEM;
+				}
+			}
+		}
+		/*keep a copy of this */
+		urb->hcpriv = urb_priv;
+#ifdef CONFIG_ISO_SUPPORT
+	}
+#endif
+
+	switch (temp) {
+	case PIPE_INTERRUPT:
+		phci_hcd_make_qtd(pehci_hcd, &urb_priv->qtd_list,	urb, &status);
+		if (status < 0)	{
+			return status;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		qh = phci_hcd_submit_interrupt(pehci_hcd, ep, &urb_priv->qtd_list, urb,
+			&status);
+#else
+		qh = phci_hcd_submit_interrupt(pehci_hcd, &urb_priv->qtd_list, urb,
+			&status);
+#endif
+		if (status < 0)
+			return status;
+		break;
+
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+
+#ifdef THREAD_BASED
+	spin_lock_irqsave (&pehci_hcd->lock, flags);
+#endif
+		phci_hcd_make_qtd(pehci_hcd, &qtd_list,	urb, &status);
+		if (status < 0) {
+			return status;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		qh = phci_hcd_submit_async(pehci_hcd, ep, &qtd_list, urb,
+			&status);
+#else
+		qh = phci_hcd_submit_async(pehci_hcd, &qtd_list, urb, &status);
+#endif
+
+#ifdef THREAD_BASED
+	spin_unlock_irqrestore (&pehci_hcd->lock, flags);
+#endif
+
+		if (status < 0) {
+			return status;
+		}
+		break;
+#ifdef CONFIG_ISO_SUPPORT
+	case PIPE_ISOCHRONOUS:
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_urb_enqueue]: URB Transfer buffer: 0x%08x\n",
+			(long) urb->transfer_buffer);
+		iso_dbg(ISO_DBG_DATA,
+			"[pehci_hcd_urb_enqueue]: URB Buffer Length: %d\n",
+			(long) urb->transfer_buffer_length);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		phcd_submit_iso(pehci_hcd, ep, urb, (unsigned long *) &status);
+#else
+		spin_lock_irqsave(&pehci_hcd->lock, flags);
+		phcd_store_urb_pending(pehci_hcd, 0, urb, (int *) &status);
+		spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+#endif
+
+		return status;
+
+		break;
+#endif
+	default:
+		return -ENODEV;
+	}			/*end of switch	*/
+
+#if (defined MSEC_INT_BASED)
+	return 0;
+#elif (defined THREAD_BASED)
+{ //send
+		st_UsbIt_Msg_Struc *stUsbItMsgSnd ;
+		unsigned long flags;
+		spin_lock_irqsave(&pehci_hcd->lock,flags);
+	
+		//local_irq_save(flags); /*disable interrupt*/
+		stUsbItMsgSnd = (st_UsbIt_Msg_Struc *)kmalloc(sizeof(st_UsbIt_Msg_Struc), GFP_ATOMIC);
+		if (!stUsbItMsgSnd)
+		{
+			return -ENOMEM;
+		}
+		
+		memset(stUsbItMsgSnd, 0, sizeof(stUsbItMsgSnd));
+		
+		stUsbItMsgSnd->usb_hcd = usb_hcd;
+		stUsbItMsgSnd->uIntStatus = NO_SOF_REQ_IN_REQ;
+		spin_lock(&enqueue_lock);
+		if(list_empty(&g_enqueueMessList.list))
+			list_add_tail(&(stUsbItMsgSnd->list), &(g_enqueueMessList.list));
+		spin_unlock(&enqueue_lock);
+		
+		pehci_print("\n------------- send mess : %d------------\n",stUsbItMsgSnd->uIntStatus);
+
+		//local_irq_restore(flags); /*disable interrupt*/
+		
+		spin_lock(&g_stUsbItThreadHandler.lock);
+		if ((g_stUsbItThreadHandler.phThreadTask != NULL) && (g_stUsbItThreadHandler.lThrdWakeUpNeeded == 0))
+		{
+			pehci_print("\n------- wake up thread : %d-----\n",stUsbItMsgSnd->uIntStatus);
+			g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1;
+			wake_up(&(g_stUsbItThreadHandler.ulThrdWaitQhead));
+		}
+		spin_unlock(&g_stUsbItThreadHandler.lock);
+
+		spin_unlock_irqrestore(&pehci_hcd->lock,flags);
+	}
+	pehci_entry("-- %s: Exit\n",__FUNCTION__);
+    return 0;
+#else
+	/*submit tds but iso */
+    if (temp != PIPE_ISOCHRONOUS)
+	pehci_hcd_td_ptd_submit_urb(pehci_hcd, qh, qh->type);
+#endif
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return 0;
+
+}
+
+/*---------------------------------------------*
+  io request handlers
+ *---------------------------------------------*/
+
+/*unlink urb*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static int
+pehci_hcd_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb)
+#else
+static int
+pehci_hcd_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	int status = 0;
+#endif
+	int retval = 0;
+	td_ptd_map_buff_t *td_ptd_buf;
+	td_ptd_map_t *td_ptd_map;
+	struct ehci_qh *qh = 0;
+	u32 skipmap = 0;
+	u32 buffstatus = 0;
+	unsigned long flags;
+	struct ehci_qtd	*qtd = 0;
+	struct usb_host_endpoint *ep;
+
+	struct ehci_qtd	*cancel_qtd = 0;	/*added	for stopping ptd*/
+	struct urb *cancel_urb = 0;	/*added	for stopping ptd*/
+	urb_priv_t *cancel_urb_priv = 0;	/* added for stopping ptd */
+	struct _isp1763_qha atlqha;
+	struct _isp1763_qha *qha;
+	struct isp1763_mem_addr	*mem_addr = 0;
+	u32 ormask = 0;
+	struct list_head *qtd_list = 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	pehci_info("device %d\n", urb->dev->devnum);
+
+	if(urb_priv==NULL){
+		printk("*******urb_priv is NULL*******	%s: Entered\n",	__FUNCTION__);
+		return 0;
+		}
+	spin_lock_irqsave(&hcd->lock, flags);
+
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	//	status = 0;
+		qh = urb_priv->qh;
+		if(qh==NULL)
+			break;
+
+		td_ptd_buf = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_ATL];
+		td_ptd_map = &td_ptd_buf->map_list[qh->qtd_ptd_index];
+
+		/*if its already been removed */
+		if (td_ptd_map->state == TD_PTD_NEW) {
+			break;
+		}
+/* patch added for stopping Full speed PTD */
+/* patch starts	ere */
+		if (urb->dev->speed != USB_SPEED_HIGH) {
+
+			cancel_qtd = td_ptd_map->qtd;
+			if (!qh	|| !cancel_qtd)	{
+				err("Never Error:QH and	QTD must not be	zero\n");
+			} else {
+				cancel_urb = cancel_qtd->urb;
+				cancel_urb_priv	=
+					(urb_priv_t *) cancel_urb->hcpriv;
+				mem_addr = &cancel_qtd->mem_addr;
+				qha = &atlqha;
+				memset(qha, 0, sizeof(struct _isp1763_qha));
+
+				skipmap	=
+					isp1763_reg_read16(hcd->dev,
+							   hcd->regs.
+							   atltdskipmap,
+							   skipmap);
+				skipmap	|= td_ptd_map->ptd_bitmap;
+				isp1763_reg_write16(hcd->dev,
+						    hcd->regs.atltdskipmap,
+						    skipmap);
+
+				/*read this ptd	from the ram address,address is	in the
+				   td_ptd_map->ptd_header_addr */
+				isp1763_mem_read(hcd->dev,
+						 td_ptd_map->ptd_header_addr, 0,
+						 (u32 *) (qha),	PHCI_QHA_LENGTH,
+						 0);
+				if ((qha->td_info1 & QHA_VALID)
+					|| (qha->td_info4 &	QHA_ACTIVE)) {
+
+					qha->td_info2 |= 0x00008000;
+					qha->td_info1 |= QHA_VALID;
+					qha->td_info4 |= QHA_ACTIVE;
+					skipmap	&= ~td_ptd_map->ptd_bitmap;
+					ormask |= td_ptd_map->ptd_bitmap;
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.
+						atl_irq_mask_or,
+						ormask);
+					/* copy back into the header, payload is	already
+					 * present no need to write again */
+					isp1763_mem_write(hcd->dev,
+						td_ptd_map->
+						ptd_header_addr, 0,
+						(u32 *) (qha),
+						PHCI_QHA_LENGTH, 0);
+					/*unskip this td */
+					isp1763_reg_write16(hcd->dev,
+						hcd->regs.
+						atltdskipmap,
+						skipmap);
+					udelay(100);
+				}
+
+				isp1763_mem_read(hcd->dev,
+					td_ptd_map->ptd_header_addr, 0,
+					(u32 *) (qha),	PHCI_QHA_LENGTH,
+					0);
+				if (!(qha->td_info1 & QHA_VALID)
+					&& !(qha->td_info4 & QHA_ACTIVE)) {
+					printk(KERN_NOTICE
+					"ptd has	been retired \n");
+				}
+
+			}
+		}
+
+/*   Patch Ends	*/
+		/* These TDs are not pending anymore */
+		td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+		/*tell atl worker this urb is going to be removed */
+		td_ptd_map->state = TD_PTD_REMOVE;
+		/* These TDs are not pending anymore */
+		td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+		/*tell atl worker this urb is going to be removed */
+		td_ptd_map->state = TD_PTD_REMOVE;
+		urb_priv->state	|= DELETE_URB;
+
+		/*read the skipmap, to see if this transfer has	to be rescheduled */
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap,
+			skipmap);
+		pehci_check("remove skip map %x, ptd map %x\n",	skipmap,
+			td_ptd_map->ptd_bitmap);
+
+		buffstatus =
+			isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,
+			buffstatus);
+
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap,
+			skipmap | td_ptd_map->ptd_bitmap);
+
+		while (!(skipmap & td_ptd_map->ptd_bitmap)) {
+			udelay(125);
+
+			skipmap	= isp1763_reg_read16(hcd->dev,
+				hcd->regs.atltdskipmap,
+				skipmap);
+		}
+
+		/* if all  transfers skipped,
+		 * then	disable	the atl	buffer,
+		 * so that new transfer	can come in
+		 * need	to see the side	effects
+		 * */
+		if (skipmap == NO_TRANSFER_ACTIVE) {
+			/*disable the buffer */
+			pehci_info("disable the	atl buffer\n");
+			buffstatus &= ~ATL_BUFFER;
+			isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status,
+				buffstatus);
+		}
+
+		qtd_list = &qh->qtd_list;
+		/*this should remove all pending transfers */
+		pehci_check("num tds %d, urb length %d,device %d\n",
+			urb_priv->length, urb->transfer_buffer_length,
+			urb->dev->devnum);
+
+		pehci_check("remove first qtd address %p\n", urb_priv->qtd[0]);
+		pehci_check("length of the urb %d, completed %d\n",
+			urb->transfer_buffer_length, urb->actual_length);
+		qtd = urb_priv->qtd[urb_priv->length - 1];
+		pehci_check("qtd state is %x\n", qtd->state);
+
+
+		urb->status=status;
+		status = 0;
+#ifdef USBNET 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_urb_delayed_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+		pehci_hcd_urb_delayed_complete(hcd, qh, urb, td_ptd_map);
+#endif
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+
+#endif
+		break;
+
+	case PIPE_INTERRUPT:
+		pehci_check("phci_1763_urb_dequeue: INTR needs to be done\n");
+		urb->status = status; //-ENOENT;//This will allow to suspend the system. in auto suspend mode
+		status = 0;
+		qh = urb_priv->qh;
+		if(qh==NULL)
+			break;
+
+		td_ptd_buf = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL];
+		td_ptd_map = &td_ptd_buf->map_list[qh->qtd_ptd_index];
+
+		/*urb is already been removed */
+		if (td_ptd_map->state == TD_PTD_NEW) {
+			kfree(urb_priv);
+			break;
+		}
+
+		/* These TDs are not pending anymore */
+		td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+		td_ptd_map->state = TD_PTD_REMOVE;
+		urb_priv->state	|= DELETE_URB;
+
+		/*read the skipmap, to see if this transfer has	to be rescheduled */
+		skipmap	=
+			isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap,
+			skipmap);
+
+		isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap,
+			skipmap | td_ptd_map->ptd_bitmap);
+		qtd_list = &qh->qtd_list;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL);
+#else
+		pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map);
+#endif
+		break;
+#ifdef CONFIG_ISO_SUPPORT
+	case PIPE_ISOCHRONOUS:
+		pehci_info("urb dequeue %x %x\n", urb,urb->pipe);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	if(urb->dev->speed==USB_SPEED_HIGH){
+		retval = usb_hcd_check_unlink_urb(usb_hcd, urb, status);
+		if (!retval) {
+			pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status);
+			usb_hcd_unlink_urb_from_ep(usb_hcd, urb);
+
+
+		}
+	}
+#endif
+
+		
+		status = 0;
+		ep=urb->ep;
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		mdelay(100);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+						if (urb->hcpriv!= periodic_ep[0]){
+#else
+						if (urb->ep != periodic_ep[0]){
+#endif
+	if(!list_empty(&ep->urb_list)){	
+		while(!list_empty(&ep->urb_list)){
+			urb=container_of(ep->urb_list.next,struct urb,urb_list);
+			pehci_info("list is not empty %x %x\n",urb,urb->dev->state);
+			if(urb){
+		retval = usb_hcd_check_unlink_urb(usb_hcd, urb,0);
+		if (!retval) {
+			pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status);
+			usb_hcd_unlink_urb_from_ep(usb_hcd, urb);
+		}
+			urb->status=-ESHUTDOWN;
+	#if LINUX_VERSION_CODE <KERNEL_VERSION(2,6,24)
+			usb_hcd_giveback_urb(usb_hcd,urb);
+	#else
+			usb_hcd_giveback_urb(usb_hcd,urb,urb->status);
+	#endif
+				
+			}
+		}
+		}else{
+	if(urb){
+		pehci_info("list empty %x\n",urb->dev->state);
+		phcd_clean_urb_pending(hcd, urb);
+		retval = usb_hcd_check_unlink_urb(usb_hcd, urb,0);
+		if (!retval) {
+			pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status);
+			usb_hcd_unlink_urb_from_ep(usb_hcd, urb);
+		}
+			urb->status=-ESHUTDOWN;
+	#if LINUX_VERSION_CODE <KERNEL_VERSION(2,6,24)
+			usb_hcd_giveback_urb(usb_hcd,urb);
+	#else
+			usb_hcd_giveback_urb(usb_hcd,urb,urb->status);
+	#endif
+				
+			}
+			
+		}
+	}	
+#endif
+		return 0;
+		/*nothing to do	here, wait till	all transfers are done in iso worker */
+		break;
+	}
+
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	pehci_info("status %d\n", status);
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return status;
+}
+
+/* bulk	qh holds the data toggle */
+
+static void
+pehci_hcd_endpoint_disable(struct usb_hcd *usb_hcd,
+			   struct usb_host_endpoint *ep)
+{
+	phci_hcd *ehci = usb_hcd_to_pehci_hcd(usb_hcd);
+	struct urb *urb;
+
+	unsigned long flags;
+	struct ehci_qh *qh;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	/* ASSERT:  any	requests/urbs are being	unlinked */
+	/* ASSERT:  nobody can be submitting urbs for this any more */
+	
+#ifdef CONFIG_ISO_SUPPORT
+	mdelay(100);  //delay for ISO
+#endif
+	spin_lock_irqsave(&ehci->lock, flags);
+
+	qh = ep->hcpriv;
+
+	if (!qh) {
+		goto done;
+	} else {
+#ifdef CONFIG_ISO_SUPPORT
+		pehci_info("disable endpoint %x %x\n", ep->desc.bEndpointAddress,qh->type);
+
+		
+		if (qh->type == TD_PTD_BUFF_TYPE_ISTL) {
+
+			/*wait for urb to get complete*/
+			pehci_info("disable %x \n", list_empty(&ep->urb_list));
+			while (!list_empty(&ep->urb_list)) {
+			
+				urb = container_of(ep->urb_list.next,
+					struct urb, urb_list);
+				if (urb) {
+					phcd_clean_urb_pending(ehci, urb);
+					spin_unlock_irqrestore(&ehci->lock,
+						flags);
+
+					urb->status = -ESHUTDOWN;
+					
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+					usb_hcd_giveback_urb(usb_hcd, urb);
+#else
+					usb_hcd_giveback_urb(usb_hcd, urb,
+						urb->status);
+#endif
+					spin_lock_irqsave(&ehci->lock, flags);
+
+				}
+
+			}
+		}
+#endif
+		/*i will complete whatever left	on this	endpoint */
+		pehci_complete_device_removal(ehci, qh);
+#ifdef CONFIG_ISO_SUPPORT
+		phcd_clean_periodic_ep();
+#endif
+		ep->hcpriv = NULL;
+
+		goto done;
+	}
+	done:
+
+	ep->hcpriv = NULL;
+
+	spin_unlock_irqrestore(&ehci->lock, flags);
+	printk("disable endpoint exit\n");
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	return;
+}
+
+/*called by core, for current frame number*/
+static int
+pehci_hcd_get_frame_number(struct usb_hcd *usb_hcd)
+{
+	u32 framenumber	= 0;
+	phci_hcd *pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	framenumber =
+		isp1763_reg_read16(pehci_hcd->dev, pehci_hcd->regs.frameindex,
+		framenumber);
+	return framenumber;
+}
+
+/*root hub status data,	called by root hub timer
+ *return 0, if no change, else
+ *	1, incase of high speed	device
+ */
+static int
+pehci_rh_status_data(struct usb_hcd *usb_hcd, char *buf)
+{
+
+	u32 temp = 0, status = 0;
+	u32 ports = 0, i, retval = 1;
+	unsigned long flags;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+
+	if (hcdpowerdown == 1)
+		return 0;
+
+	buf[0] = 0;
+	if(portchange==1){
+		printk("Remotewakeup-enumerate again \n");
+		buf[0] |= 2;
+		hcd->reset_done[0] = 0;
+		return 1;
+	}
+	/* init	status to no-changes */
+	buf[0] = 0;
+	/*number of ports */
+	ports =	0x1;
+	spin_lock_irqsave(&hcd->lock, flags);
+	/*read the port	status registers */
+	for (i = 0; i <	ports; i++) {
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[i],	temp);
+		if (temp & PORT_OWNER) {
+			/* dont	report the port	status change in case of CC HCD
+			 * but clear the port status , if there	is any*/
+			if (temp & PORT_CSC) {
+				temp &=	~PORT_CSC;
+				isp1763_reg_write32(hcd->dev,
+						    hcd->regs.ports[i],	temp);
+				continue;
+			}
+		}
+
+		if (!(temp & PORT_CONNECT)) {
+			hcd->reset_done[i] = 0;
+		}
+		if ((temp & (PORT_CSC |	PORT_PEC | PORT_OCC)) != 0) {
+			if (i <	7) {
+				buf[0] |= 1 << (i + 1);
+			} else {
+				buf[1] |= 1 << (i - 7);
+			}
+			status = STS_PCD;
+		}
+	}
+
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	return status ?	retval : 0;
+}
+
+/*root hub control requests*/
+static int
+pehci_rh_control(struct	usb_hcd	*usb_hcd, u16 typeReq, u16 wValue,
+		 u16 wIndex, char *buf,	u16 wLength)
+{
+	u32 ports = 0;
+	u32 temp = 0, status;
+	unsigned long flags;
+	int retval = 0;
+	phci_hcd *hcd =	usb_hcd_to_pehci_hcd(usb_hcd);
+
+	ports =	0x11;
+
+	printk("%s: request %x,wValuse:0x%x, wIndex:0x%x \n",__func__, typeReq,wValue,wIndex);
+	
+	spin_lock_irqsave(&hcd->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+		switch (wValue)	{
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case ClearPortFeature:
+		pehci_print("ClearPortFeature:0x%x\n", ClearPortFeature);
+		if (!wIndex || wIndex >	(ports & 0xf)) {
+			pehci_info
+				("ClearPortFeature not valid port number %d, should be %d\n",
+				 wIndex, (ports	& 0xf));
+			goto error;
+		}
+		wIndex--;
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex],
+					  temp);
+		if (temp & PORT_OWNER) {
+			printk("port is	owned by the CC	host\n");
+			break;
+		}
+
+		switch (wValue)	{
+		case USB_PORT_FEAT_ENABLE:
+			pehci_print("enable the	port\n");
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp & ~PORT_PE);
+
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			printk("disable	the port\n");
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp | PORT_PEC);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+		case USB_PORT_FEAT_C_SUSPEND:
+			printk("clear feature suspend  \n");
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (ports & 0x10) {	/*port has has power control switches */
+				isp1763_reg_write32(hcd->dev,
+						    hcd->regs.ports[wIndex],
+						    temp & ~PORT_POWER);
+			}
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			pehci_print("connect change, status is 0x%08x\n", temp);
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp | PORT_CSC);
+			break;
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp | PORT_OCC);
+			break;
+		default:
+			goto error;
+
+		}
+		break;
+
+	case GetHubDescriptor:
+		pehci_hub_descriptor(hcd, (struct usb_hub_descriptor *)	buf);
+		break;
+
+	case GetHubStatus:
+		pehci_print("GetHubStatus:0x%x\n", GetHubStatus);
+		/* no hub-wide feature/status flags */
+		memset(buf, 0, 4);
+		break;
+	case GetPortStatus:
+		pehci_print("GetPortStatus:0x%x\n", GetPortStatus);
+		if (!wIndex || wIndex >	(ports & 0xf)) {
+			pehci_info
+				("GetPortStatus,not valid port number %d, should be %d\n",
+				 wIndex, (ports	& 0xf));
+			goto error;
+		}
+		wIndex--;
+		status = 0;
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex],
+					  temp);
+		printk("root port status:0x%x\n", temp);
+		/*connect status chnage	*/
+		if (temp & PORT_CSC) {
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+			pehci_print("feature CSC 0x%08x	and status 0x%08x  \n",
+				    temp, status);
+		}
+		if(portchange){
+			portchange=0;
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+		}
+		/*port enable change */
+		if (temp & PORT_PEC) {
+			status |= 1 << USB_PORT_FEAT_C_ENABLE;
+			pehci_print("feature PEC  0x%08x and status 0x%08x  \n",
+				    temp, status);
+		}
+		/*port over-current */
+		if (temp & PORT_OCC) {
+			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
+			pehci_print("feature OCC 0x%08x	and status 0x%08x  \n",
+				    temp, status);
+		}
+
+		/* whoever resets must GetPortStatus to	complete it!! */
+		if ((temp & PORT_RESET)	&& jiffies > hcd->reset_done[wIndex]) {
+			status |= 1 << USB_PORT_FEAT_C_RESET;
+			pehci_print("feature reset 0x%08x and status 0x%08x\n",
+				temp, status);
+			printk(KERN_NOTICE
+				"feature	reset 0x%08x and status	0x%08x\n", temp,
+				status);
+			/* force reset to complete */
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+					    temp & ~PORT_RESET);
+			do {
+				mdelay(20);
+				temp = isp1763_reg_read32(hcd->dev,
+							  hcd->regs.
+							  ports[wIndex], temp);
+			} while	(temp &	PORT_RESET);
+
+			/* see what we found out */
+			printk(KERN_NOTICE "after portreset: %x\n", temp);
+
+			temp = phci_check_reset_complete(hcd, wIndex, temp);
+			printk(KERN_NOTICE "after checkportreset: %x\n", temp);
+		}
+
+		/* don't show wPortStatus if it's owned	by a companion hc */
+
+		if (!(temp & PORT_OWNER)) {
+
+			if (temp & PORT_CONNECT) {
+				status |= 1 << USB_PORT_FEAT_CONNECTION;
+				status |= 1 << USB_PORT_FEAT_HIGHSPEED;
+			}
+			if (temp & PORT_PE) {
+				status |= 1 << USB_PORT_FEAT_ENABLE;
+			}
+			if (temp & PORT_SUSPEND) {
+				status |= 1 << USB_PORT_FEAT_SUSPEND;
+			}
+			if (temp & PORT_OC) {
+				status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+			}
+			if (temp & PORT_RESET) {
+				status |= 1 << USB_PORT_FEAT_RESET;
+			}
+			if (temp & PORT_POWER) {
+				status |= 1 << USB_PORT_FEAT_POWER;
+			}
+		}
+
+		/* This	alignment is good, caller used kmalloc() */
+		*((u32 *) buf) = cpu_to_le32(status);
+		break;
+
+	case SetHubFeature:
+		pehci_print("SetHubFeature:0x%x\n", SetHubFeature);
+		switch (wValue)	{
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case SetPortFeature:
+		pehci_print("SetPortFeature:%x\n", SetPortFeature);
+		if (!wIndex || wIndex >	(ports & 0xf)) {
+			pehci_info
+				("SetPortFeature not valid port	number %d, should be %d\n",
+				 wIndex, (ports	& 0xf));
+			goto error;
+		}
+		wIndex--;
+		temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex],
+					  temp);
+		pehci_print("SetPortFeature:PortSc Val 0x%x\n",	temp);
+		if (temp & PORT_OWNER) {
+			break;
+		}
+		switch (wValue)	{
+		case USB_PORT_FEAT_ENABLE:
+			/*enable the port */
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+				temp | PORT_PE);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			
+			#if 0 /* Port suspend will be added in suspend function */
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+				temp | PORT_SUSPEND);
+			#endif
+			
+			break;
+		case USB_PORT_FEAT_POWER:
+			pehci_print("Set Port Power 0x%x and Ports %x\n",
+				USB_PORT_FEAT_POWER, ports);
+			if (ports & 0x10) {
+				printk(KERN_NOTICE
+					"PortSc Reg %x an Value %x\n",
+					hcd->regs.ports[wIndex],
+					(temp | PORT_POWER));
+
+				isp1763_reg_write32(hcd->dev,
+					hcd->regs.ports[wIndex],
+					temp | PORT_POWER);
+			}
+			break;
+		case USB_PORT_FEAT_RESET:
+			pehci_print("Set Port Reset 0x%x\n",
+				USB_PORT_FEAT_RESET);
+			if ((temp & (PORT_PE | PORT_CONNECT)) == PORT_CONNECT
+				&& PORT_USB11(temp)) {
+				printk("error:port %d low speed	--> companion\n", wIndex + 1);
+				temp |=	PORT_OWNER;
+			} else {
+				temp |=	PORT_RESET;
+				temp &=	~PORT_PE;
+
+				/*
+				 * caller must wait, then call GetPortStatus
+				 * usb 2.0 spec	says 50	ms resets on root
+				 */
+				hcd->reset_done[wIndex]	= jiffies
+					+ ((50 /* msec */  * HZ) / 1000);
+			}
+			isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex],
+				temp);
+			break;
+		default:
+			goto error;
+		}
+		break;
+	default:
+		pehci_print("this request doesnt fit anywhere\n");
+	error:
+		/* "stall" on error */
+		pehci_info
+			("unhandled root hub request: typereq 0x%08x, wValue %d, wIndex	%d\n",
+			 typeReq, wValue, wIndex);
+		retval = -EPIPE;
+	}
+
+	pehci_info("rh_control:exit\n");
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	return retval;
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver pehci_driver = {
+	.description = hcd_name,
+	.product_desc =	"ST-ERICSSON ISP1763",
+	.hcd_priv_size = sizeof(phci_hcd),
+#ifdef LINUX_2620
+	.irq = NULL,
+#else
+	.irq = pehci_hcd_irq,
+#endif
+	/*
+	 * generic hardware linkage
+	 */
+	.flags = HCD_USB2 | HCD_MEMORY,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.reset = pehci_hcd_reset,
+	.start = pehci_hcd_start,
+	.bus_suspend = pehci_bus_suspend,
+	.bus_resume  = pehci_bus_resume,
+	.stop =	pehci_hcd_stop,
+	/*
+	 * managing i/o	requests and associated	device resources
+	 */
+	.urb_enqueue = pehci_hcd_urb_enqueue,
+	.urb_dequeue = pehci_hcd_urb_dequeue,
+	.endpoint_disable = pehci_hcd_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number = pehci_hcd_get_frame_number,
+
+	/*
+	 * root	hub support
+	 */
+	.hub_status_data = pehci_rh_status_data,
+	.hub_control = pehci_rh_control,
+};
+
+/*probe the PCI host*/
+
+#ifdef THREAD_BASED
+int pehci_hcd_process_irq_it_handle(struct usb_hcd* usb_hcd_)
+{
+	int istatus;
+	
+	struct usb_hcd 		*usb_hcd;
+	char					uIntStatus;
+	phci_hcd    *pehci_hcd;
+
+	struct list_head *pos, *lst_tmp;
+	st_UsbIt_Msg_Struc *mess;
+	unsigned long flags;
+	
+	g_stUsbItThreadHandler.phThreadTask = current;
+	siginitsetinv(&((g_stUsbItThreadHandler.phThreadTask)->blocked), sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));		
+	pehci_info("pehci_hcd_process_irq_it_thread ID : %d\n", g_stUsbItThreadHandler.phThreadTask->pid);
+	
+	while (1)
+	{
+		if (signal_pending(g_stUsbItThreadHandler.phThreadTask))
+		{
+	       	printk("thread handler:  Thread received signal\n");
+	       	break;
+		}
+
+		spin_lock(&g_stUsbItThreadHandler.lock);
+		g_stUsbItThreadHandler.lThrdWakeUpNeeded = 0;
+		spin_unlock(&g_stUsbItThreadHandler.lock);
+		
+		/* Wait until a signal arrives or we are woken up or timeout (5second)*/
+		istatus = wait_event_interruptible_timeout(g_stUsbItThreadHandler.ulThrdWaitQhead, (g_stUsbItThreadHandler.lThrdWakeUpNeeded== 1), msecs_to_jiffies(MSEC_INTERVAL_CHECKING));
+
+		local_irq_save(flags); /*disable interrupt*/
+		spin_lock(&g_stUsbItThreadHandler.lock);
+		g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1;
+		spin_unlock(&g_stUsbItThreadHandler.lock);
+		//receive mess	
+		if (!list_empty(&g_messList.list)) //mess list not empty
+		{
+
+			list_for_each_safe(pos, lst_tmp, &(g_messList.list))
+			{
+				mess = list_entry(pos, st_UsbIt_Msg_Struc, list);
+
+				usb_hcd = mess->usb_hcd;
+				uIntStatus = mess->uIntStatus;
+				//pehci_print("-------------receive mess : %d------------\n",uIntStatus);
+				pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+				if((uIntStatus & NO_SOF_REQ_IN_TSK)  || (uIntStatus & NO_SOF_REQ_IN_ISR) || (uIntStatus & NO_SOF_REQ_IN_REQ))
+					pehci_interrupt_handler(pehci_hcd);
+				spin_lock(&g_stUsbItThreadHandler.lock);
+				list_del(pos);
+				kfree(mess);
+				spin_unlock(&g_stUsbItThreadHandler.lock);				
+			}
+		}
+		else if(!list_empty(&g_enqueueMessList.list))
+		{
+			mess = list_first_entry(&(g_enqueueMessList.list), st_UsbIt_Msg_Struc, list);
+			usb_hcd = mess->usb_hcd;
+			uIntStatus = mess->uIntStatus;
+
+			pehci_print("-------------receive mess : %d------------\n",uIntStatus);
+			pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+			if((uIntStatus & NO_SOF_REQ_IN_REQ))
+			{
+				pehci_interrupt_handler(pehci_hcd);
+			}	
+
+			{
+				spin_lock(&enqueue_lock);
+				list_del((g_enqueueMessList.list).next);
+				kfree(mess);
+				spin_unlock(&enqueue_lock);
+			}	
+		}
+		else if(istatus == 0) //timeout
+		{
+			pehci_hcd = NULL;
+			pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd_);
+			pehci_interrupt_handler(pehci_hcd);
+
+		}
+		local_irq_restore(flags);  /*enable interrupt*/
+	}
+
+	flush_signals(g_stUsbItThreadHandler.phThreadTask);
+	g_stUsbItThreadHandler.phThreadTask = NULL;
+	return 0;
+	
+}
+
+int pehci_hcd_process_irq_in_thread(struct usb_hcd* usb_hcd_)
+{
+	
+	//status = msgq_create("usb_it_queue", 10, sizeof(st_UsbIt_Msg_Struc), &uUsbIt_MsgQueId);
+	INIT_LIST_HEAD(&g_messList.list);
+	INIT_LIST_HEAD(&g_enqueueMessList.list);
+	spin_lock_init(&enqueue_lock);
+
+	memset(&g_stUsbItThreadHandler, 0, sizeof(st_UsbIt_Thread));
+	init_waitqueue_head(&(g_stUsbItThreadHandler.ulThrdWaitQhead));
+	g_stUsbItThreadHandler.lThrdWakeUpNeeded = 0;
+	spin_lock_init(&g_stUsbItThreadHandler.lock);
+	kernel_thread(pehci_hcd_process_irq_it_handle, usb_hcd_, 0);
+	
+    return 0;
+}
+#endif
+
+
+/*probe	the PCI	host*/
+int
+pehci_hcd_probe(struct isp1763_dev *isp1763_dev, isp1763_id * ids)
+{
+#ifdef NON_PCI
+    struct platform_device *dev = isp1763_dev->dev;
+#else /* PCI */
+	struct pci_dev *dev = isp1763_dev->pcidev;
+#endif
+	struct usb_hcd *usb_hcd;
+	phci_hcd *pehci_hcd;
+	int status = 0;
+
+#ifndef NON_PCI
+	u32 intcsr=0;
+#endif
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	if (usb_disabled()) {
+		return -ENODEV;
+	}
+
+	usb_hcd	= usb_create_hcd(&pehci_driver,&dev->dev, "ISP1763");
+
+	if (usb_hcd == NULL) {
+		status = -ENOMEM;
+		goto clean;
+	}
+
+	/* this	is our host */
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	pehci_hcd->dev = isp1763_dev;
+	pehci_hcd->iobase = (u8	*) isp1763_dev->baseaddress;
+	pehci_hcd->iolength = isp1763_dev->length;
+
+
+	/* lets	keep our host here */
+	isp1763_dev->driver_data = usb_hcd;
+#ifdef NON_PCI
+//Do nothing
+#else
+	/* Enable the interrupts from PLX to PCI */
+	/* CONFIGURE PCI/PLX interrupt */
+#ifdef DATABUS_WIDTH_16
+	wvalue1	= readw(pehci_hcd->plxiobase + 0x68);
+	wvalue2	= readw(pehci_hcd->plxiobase + 0x68 + 2);
+	intcsr |= wvalue2;
+	intcsr <<= 16;
+	intcsr |= wvalue1;
+	printk(KERN_NOTICE "Enable PCI Intr: %x	\n", intcsr);
+	intcsr |= 0x900;
+	writew((u16) intcsr, pehci_hcd->plxiobase + 0x68);
+	writew((u16) (intcsr >>	16), pehci_hcd->plxiobase + 0x68 + 2);
+#else
+	bvalue1	= readb(pehci_hcd->plxiobase + 0x68);
+	bvalue2	= readb(pehci_hcd->plxiobase + 0x68 + 1);
+	bvalue3	= readb(pehci_hcd->plxiobase + 0x68 + 2);
+	bvalue4	= readb(pehci_hcd->plxiobase + 0x68 + 3);
+	intcsr |= bvalue4;
+	intcsr <<= 8;
+	intcsr |= bvalue3;
+	intcsr <<= 8;
+	intcsr |= bvalue2;
+	intcsr <<= 8;
+	intcsr |= bvalue1;
+	writeb((u8) intcsr, pehci_hcd->plxiobase + 0x68);
+	writeb((u8) (intcsr >> 8), pehci_hcd->plxiobase	+ 0x68 + 1);
+	writeb((u8) (intcsr >> 16), pehci_hcd->plxiobase + 0x68	+ 2);
+	writeb((u8) (intcsr >> 24), pehci_hcd->plxiobase + 0x68	+ 3);
+#endif
+#endif
+
+	No_Data_Phase =	0;
+	No_Status_Phase	= 0;
+	usb_hcd->self.controller->dma_mask = 0;
+	usb_hcd->self.otg_port = 1;
+#if 0
+#ifndef THREAD_BASED 	
+	status = isp1763_request_irq(pehci_hcd_irq, isp1763_dev, usb_hcd);
+#endif
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	if (status == 0) {
+		status = usb_add_hcd(usb_hcd, isp1763_dev->irq, SA_SHIRQ);
+	}
+#else /* Linux 2.6.28*/
+	usb_hcd->self.uses_dma = 0;
+	if (status == 0){
+		status = usb_add_hcd(usb_hcd, isp1763_dev->irq,
+		IRQF_SHARED | IRQF_DISABLED | IRQF_TRIGGER_LOW);
+	}
+#endif
+
+#ifdef THREAD_BASED 	
+	g_pehci_hcd = pehci_hcd;
+#endif
+
+#ifdef USBNET 
+	// initialize clean up urb list
+	INIT_LIST_HEAD(&(pehci_hcd->cleanup_urb.urb_list));
+#endif
+	enable_irq_wake(isp1763_dev->irq);
+	wake_lock_init(&pehci_wake_lock, WAKE_LOCK_SUSPEND,
+						dev_name(&dev->dev));
+	wake_lock(&pehci_wake_lock);
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+	isp1763_hcd=isp1763_dev;
+	return status;
+
+	clean:
+	return status;
+
+}
+/*--------------------------------------------------------------*
+ *
+ *  Module details: pehci_hcd_powerup
+ *
+ *  This function powerdown the chip completely, which make chip works in minimal power
+ *
+ *  Input: struct isp1763_Dev *
+ *
+ *
+ *  
+ *
+ *  Called by: IOCTL function
+ *
+ *
+ --------------------------------------------------------------*/
+void 
+pehci_hcd_powerup(struct	isp1763_dev *dev)
+{
+	printk("%s\n", __FUNCTION__);
+	hcdpowerdown = 0;
+	dev->driver->probe(dev,dev->driver->id);
+
+	
+}
+void
+pehci_hcd_powerdown(struct	isp1763_dev *dev)
+{
+	struct usb_hcd *usb_hcd;
+
+	phci_hcd *hcd = NULL;
+	u32 temp;
+	usb_hcd = (struct usb_hcd *) dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+	
+	printk("%s\n", __FUNCTION__);
+	hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		/* stop the controller first */
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+	printk("++ %s: Entered\n", __FUNCTION__);
+
+//	isp1763_free_irq(dev,usb_hcd);
+	usb_remove_hcd(usb_hcd);
+	dev->driver_data = NULL;
+	
+
+	temp = isp1763_reg_read16(dev, HC_INTENABLE_REG, temp); //0xD6
+	temp &= ~0x400;		/*disable otg interrupt*/
+	isp1763_reg_write16(dev, HC_INTENABLE_REG, temp); //0xD6
+
+	isp1763_reg_write16(dev, HC_UNLOCK_DEVICE, 0xAA37);	/*unlock the device 0x7c*/
+	mdelay(1);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+
+
+	if ((temp & 0x1005) == 0x1005) {
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1000);
+		temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+		mdelay(10);
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1104);
+		mdelay(10);
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1007);
+		temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+		mdelay(10);
+		isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1005);
+
+		temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	}
+	
+	printk("port status %x\n ", temp);
+	temp &= ~0x2;
+	temp &= ~0x40;		/*force port resume*/
+	temp |= 0x80;		/*suspend*/
+
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	printk("port status %x\n ", temp);
+	mdelay(200);
+
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);	/*suspend the device first 0xc*/
+	temp |= 0x2c;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc
+	mdelay(20);
+
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0); //0xc
+	temp = 0xc;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc
+
+	isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, 0xffff0800);
+
+	wake_unlock(&pehci_wake_lock);
+	wake_lock_destroy(&pehci_wake_lock);
+
+	hcdpowerdown = 1;
+	
+}
+
+static int pehci_bus_suspend(struct usb_hcd *usb_hcd)
+{
+	u32 temp=0;
+	unsigned long flags;
+	phci_hcd *pehci_hcd = NULL;
+	struct isp1763_dev *dev = NULL;
+
+	
+	if (!usb_hcd) {
+		return -EBUSY;
+	}
+	
+	printk("++ %s \n",__FUNCTION__);
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+
+	dev = pehci_hcd->dev;
+	
+	spin_lock_irqsave(&pehci_hcd->lock, flags);
+	if(hcdpowerdown){
+		spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+		return 0;
+	}
+
+
+	isp1763_reg_write32(dev, HC_USBSTS_REG, 0x4); //0x90
+	isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x4); //0x94
+	isp1763_reg_write16(dev, HC_INTERRUPT_REG, INTR_ENABLE_MASK); //0xd4
+
+	temp=isp1763_reg_read16(dev, HC_INTERRUPT_REG, 0); //0xd4
+
+	isp1763_reg_write16(dev,HC_INTENABLE_REG,INTR_ENABLE_MASK);
+	temp=isp1763_reg_read16(dev,HC_INTENABLE_REG,0);
+
+	hcdpowerdown = 1;
+	
+	/* stop the controller first */
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+
+	/* suspend root port which will suspend host controller of the ISP1763A */
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp |= (PORT_SUSPEND);//0x80
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	
+	/* suspend device controller of the ISP1763a*/
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);
+	temp |= 0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);
+	mdelay(1); // make sure there will not be huge delay here max is 1 ms
+	temp &= ~0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);
+	/* put host controoler into low power mode */
+	isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_SUSPEND_VALUE);
+
+//	usb_hcd->state = HC_STATE_SUSPENDED;
+
+	spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+
+	printk("-- %s \n",__FUNCTION__);
+
+	wake_unlock(&pehci_wake_lock);
+
+	return 0;
+	
+
+}
+
+static int pehci_bus_resume(struct usb_hcd *usb_hcd)
+{
+	u32 temp,i;
+	phci_hcd *pehci_hcd = NULL;
+	struct isp1763_dev *dev = NULL;
+	unsigned long flags;
+	u32 portsc1;
+
+	printk("%s Enter \n",__func__);
+
+	if (!usb_hcd) {
+		return -EBUSY;
+	}
+
+	if(hcdpowerdown ==0){
+		printk("%s already executed\n ",__func__);
+		return 0;
+	}
+
+	pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd);
+	dev = pehci_hcd->dev;
+	spin_lock_irqsave(&pehci_hcd->lock, flags);
+
+	for (temp = 0; temp < 100; temp++)
+	{
+		i = isp1763_reg_read32(dev, HC_CHIP_ID_REG, 0);
+		if(i==0x176320)
+			break;
+		mdelay(2);
+	}
+	printk("temp=%d, chipid:0x%x \n",temp,i);
+	mdelay(10);
+	isp1763_reg_write16(dev, HC_UNLOCK_DEVICE, 0xAA37);	/*unlock the device 0x7c*/
+	i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0);
+	printk("POWER DOWN CTRL REG value during suspend =0x%x\n", i);
+	for (temp = 0; temp < 100; temp++) {
+		mdelay(1);
+		isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_NORMAL_VALUE);
+		mdelay(1);
+		i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0);
+		if(i==POWER_DOWN_CTRL_NORMAL_VALUE)
+			break;
+	}
+	if (temp == 100) {
+		spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+		pr_err("%s:isp1763a failed to resume\n", __func__);
+		return -1;
+	}
+
+	wake_lock(&pehci_wake_lock);
+
+	printk("%s: Powerdown Reg Val: 0x%08x -- %d\n", __func__, i, temp);
+
+	isp1763_reg_write32(dev, HC_USBSTS_REG,0x0); //0x90
+	isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x0); //0x94
+	isp1763_reg_write16(dev, HC_INTENABLE_REG,0); //0xD6
+
+	portsc1 = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("%s PORTSC1: 0x%x\n", __func__, portsc1);
+
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp |= 0x01;		/* Start the controller */
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+	mdelay(10);
+
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	if (temp & PORT_SUSPEND)
+		pr_err("%s: HC_PORTSC1_REG: 0x%08x\n", __func__, temp);
+	temp |= PORT_SUSPEND;    //0x80;
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	mdelay(50);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp |= PORT_RESUME;     //0x40;
+	temp &= ~(PORT_SUSPEND); //0x80;		/*suspend*/
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp &= ~(PORT_RESUME);  //0x40;
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+
+	temp = INTR_ENABLE_MASK;
+	isp1763_reg_write16(dev, HC_INTENABLE_REG, temp); //0xD6
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("%s resume port status: 0x%x\n", __func__, temp);
+	if(!(temp & 0x4)){ //port is disabled
+		isp1763_reg_write16(dev, HC_INTENABLE_REG, 0x1005); //0xD6
+		mdelay(10);
+	}
+//	phci_resume_wakeup(dev);
+
+	hcdpowerdown = 0;
+	if(hubdev){
+		hubdev->hcd_priv    = NULL;
+		hubdev->hcd_suspend = NULL;
+	}
+
+	spin_unlock_irqrestore(&pehci_hcd->lock, flags);
+	printk("%s Leave\n",__func__);
+
+	return 0;
+}
+
+void
+pehci_hcd_resume(struct	isp1763_dev *dev)
+{
+	struct usb_hcd *usb_hcd;
+	u32 temp,i;
+	usb_hcd = (struct usb_hcd *) dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+
+	if(hcdpowerdown ==0){
+		return ;
+	}
+
+	printk("%s \n",__FUNCTION__);
+
+	for (temp = 0; temp < 10; temp++)
+	{
+	i = isp1763_reg_read32(dev, HC_CHIP_ID_REG, 0);
+	printk("temp=%d, chipid:0x%x \n",temp,i);
+	if(i==0x176320)
+	break;
+	mdelay(1);
+	}
+
+	/* Start the controller */
+	temp = 0x01;		
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+
+	/* update power down control reg value */
+	for (temp = 0; temp < 100; temp++) {
+		isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_NORMAL_VALUE);
+		i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0);
+		if(i==POWER_DOWN_CTRL_NORMAL_VALUE)
+		break;
+	}
+	
+	if (temp == 100) {
+		pr_err("%s:isp1763a failed to resume\n", __func__);
+		return;
+	}
+
+	wake_lock(&pehci_wake_lock);
+
+	isp1763_reg_write16(dev, HC_INTENABLE_REG,0); //0xD6
+	isp1763_reg_write32(dev,HC_INTERRUPT_REG_EHCI,0x4); //0x94 
+	isp1763_reg_write32(dev,HC_INTERRUPT_REG,0xFFFF); //0x94 
+	/* clear suspend bit and resume bit */	
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	temp &= ~(PORT_SUSPEND); //0x80;		/*suspend*/
+	temp &= ~(PORT_RESUME);  // 0x40;
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+	
+	isp1763_reg_write16(dev, HC_INTENABLE_REG, INTR_ENABLE_MASK); //0xD6
+	/*this is just make sure port is resumed back */	
+	mdelay(1);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("after hcd resume :port status %x\n ", temp);
+	
+	hcdpowerdown = 0;	
+
+	phci_resume_wakeup(dev);
+
+	if(hubdev){
+		hubdev->hcd_priv=NULL;
+		hubdev->hcd_suspend=NULL;
+	}
+//	usb_hcd->state = HC_STATE_RUNNING;
+
+}
+
+
+void
+pehci_hcd_suspend(struct isp1763_dev *dev)
+{
+	struct usb_hcd *usb_hcd;
+	u32 temp;
+	usb_hcd = (struct usb_hcd *) dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+	printk("%s \n",__FUNCTION__);
+	if(hcdpowerdown){
+		return ;
+	}
+
+	temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		/* stop the controller first */
+	isp1763_reg_write16(dev, HC_USBCMD_REG, temp);
+
+	isp1763_reg_write32(dev, HC_USBSTS_REG, 0x4); //0x90
+	isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x4); //0x94
+	isp1763_reg_write16(dev, HC_INTERRUPT_REG, INTR_ENABLE_MASK); //0xd4
+	
+	temp=isp1763_reg_read16(dev, HC_INTERRUPT_REG, 0); //0xd4
+
+	printk("suspend :Interrupt Status %x\n",temp);
+	isp1763_reg_write16(dev,HC_INTENABLE_REG,INTR_ENABLE_MASK);
+	temp=isp1763_reg_read16(dev,HC_INTENABLE_REG,0);
+	printk("suspend :Interrupt Enable %x\n",temp);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	
+	printk("suspend :port status %x\n ", temp);
+	temp &= ~0x2;
+	temp &= ~0x40;		/*force port resume*/
+	temp |= 0x80;		/*suspend*/
+//	temp |= 0x700000;	/*WKCNNT_E,WKDSCNNT_E,WKOC_E*/
+	isp1763_reg_write32(dev, HC_PORTSC1_REG, temp);
+  //  mdelay(10);
+	temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0);
+	printk("suspend :port status %x\n ", temp);
+	hcdpowerdown = 1;
+
+
+	temp = isp1763_reg_read16(dev,HC_HW_MODE_REG, 0);	/*suspend the device first 0xc*/
+	temp&=0xff7b;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc
+
+
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);	/*suspend the device first 0xc*/
+	temp |= 0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);//0xc
+	mdelay(2);
+	temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);//0xc
+	temp &= 0xffdf;
+	temp &= ~0x20;
+	isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);//0xc
+
+	isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, 0xffff0830);
+
+	wake_unlock(&pehci_wake_lock);
+	
+}
+
+void 
+pehci_hcd_remotewakeup(struct isp1763_dev *dev){
+	if(hubdev){
+		hubdev->hcd_priv=dev;
+		hubdev->hcd_suspend=(void *)pehci_hcd_suspend;
+		}
+	phci_remotewakeup(dev);
+}
+
+/*remove the host controller*/
+static void
+pehci_hcd_remove(struct	isp1763_dev *isp1763_dev)
+{
+
+	struct usb_hcd *usb_hcd;
+	
+#ifdef NON_PCI
+#else	/* PCI */
+//	struct pci_dev *dev = isp1763_dev->pcidev;
+#endif
+
+	phci_hcd *hcd =	NULL;
+	u32 temp;
+	usb_hcd	= (struct usb_hcd *) isp1763_dev->driver_data;
+	if (!usb_hcd) {
+		return;
+	}
+	hcd=usb_hcd_to_pehci_hcd(usb_hcd);
+	isp1763_reg_write32(hcd->dev,hcd->regs.hwmodecontrol,0);
+	isp1763_reg_write32(hcd->dev,hcd->regs.interruptenable,0);
+	hubdev=0;
+	huburb=0;
+	temp = isp1763_reg_read16(hcd->dev, HC_USBCMD_REG, 0);
+	temp &= ~0x01;		/* stop the controller first */
+	isp1763_reg_write16(hcd->dev, HC_USBCMD_REG, temp);
+//	isp1763_free_irq(isp1763_dev,usb_hcd);
+	usb_remove_hcd(usb_hcd);
+
+	wake_unlock(&pehci_wake_lock);
+	wake_lock_destroy(&pehci_wake_lock);
+
+	return ;
+}
+
+
+static isp1763_id ids =	{
+	.idVendor = 0x04CC,	/*st ericsson isp1763 vendor_id	*/
+	.idProduct = 0x1A64,	/*st ericsson isp1763 product_id */
+	.driver_info = (unsigned long) &pehci_driver,
+};
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct isp1763_driver pehci_hcd_pci_driver = {
+	.name =	(char *) hcd_name,
+	.index = 0,
+	.id = &ids,
+	.probe = pehci_hcd_probe,
+	.remove	= pehci_hcd_remove,
+	.suspend = pehci_hcd_suspend,
+	.resume	= pehci_hcd_resume,
+	.remotewakeup=pehci_hcd_remotewakeup,
+	.powerup	=	pehci_hcd_powerup,
+	.powerdown	=	pehci_hcd_powerdown,
+};
+
+#ifdef HCD_PACKAGE
+int
+usb_hcddev_open(struct inode *inode, struct file *fp)
+{
+
+	return 0;
+}
+
+int
+usb_hcddev_close(struct inode *inode, struct file *fp)
+{
+
+	return 0;
+}
+
+int
+usb_hcddev_fasync(int fd, struct file *fp, int mode)
+{
+
+	return fasync_helper(fd, fp, mode, &fasync_q);
+}
+
+long
+usb_hcddev_ioctl(struct file *fp,
+		 unsigned int cmd, unsigned long arg)
+{
+
+	switch (cmd) {
+	case HCD_IOC_POWERDOWN:	/* SET HCD DEEP SUSPEND MODE */
+		printk("HCD IOC POWERDOWN MODE\n");
+		if(isp1763_hcd->driver->powerdown)
+			isp1763_hcd->driver->powerdown(isp1763_hcd);
+
+		break;
+
+	case HCD_IOC_POWERUP:	/* Set HCD POWER UP */
+		printk("HCD IOC POWERUP MODE\n");
+		if(isp1763_hcd->driver->powerup)
+			isp1763_hcd->driver->powerup(isp1763_hcd);
+
+		break;
+	case HCD_IOC_TESTSE0_NACK:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_SE0_NAK;
+		break;
+	case   HCD_IOC_TEST_J:		
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_J;
+		break;
+	case    HCD_IOC_TEST_K:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_K;
+		break;
+		
+	case   HCD_IOC_TEST_TESTPACKET:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_PACKET;
+		break;
+	case HCD_IOC_TEST_FORCE_ENABLE:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_TEST_FORCE_ENABLE;
+		break;
+	case	HCD_IOC_TEST_SUSPEND_RESUME:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_HS_HOST_PORT_SUSPEND_RESUME;
+		break;
+	case HCD_IOC_TEST_SINGLE_STEP_GET_DEV_DESC:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_SINGLE_STEP_GET_DEV_DESC;		
+		break;
+	case HCD_IOC_TEST_SINGLE_STEP_SET_FEATURE:
+		HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE;
+		HostTest = HOST_COMP_SINGLE_STEP_SET_FEATURE;		
+		break;
+	case HCD_IOC_TEST_STOP:
+		HostComplianceTest = 0;
+		HostTest = 0;		
+		break;
+	case     HCD_IOC_SUSPEND_BUS:
+		printk("isp1763:SUSPEND bus\n");
+		if(isp1763_hcd->driver->suspend)
+			isp1763_hcd->driver->suspend(isp1763_hcd);
+		break;
+	case	HCD_IOC_RESUME_BUS:
+		printk("isp1763:RESUME bus\n");
+		if(isp1763_hcd->driver->resume)
+			isp1763_hcd->driver->resume(isp1763_hcd);		
+		break;
+	case     HCD_IOC_REMOTEWAKEUP_BUS:
+		printk("isp1763:SUSPEND bus\n");
+		if(isp1763_hcd->driver->remotewakeup)
+			isp1763_hcd->driver->remotewakeup(isp1763_hcd);
+		break;		
+	default:
+
+		break;
+
+	}
+	return 0;
+}
+
+
+/* HCD file operations */
+static struct file_operations usb_hcddev_fops = {
+	owner:THIS_MODULE,
+	read:NULL,
+	write:NULL,
+	poll:NULL,
+	unlocked_ioctl:usb_hcddev_ioctl,
+	open:usb_hcddev_open,
+	release:usb_hcddev_close,
+	fasync:usb_hcddev_fasync,
+};
+
+#endif
+
+
+static int __init
+pehci_module_init(void)
+{
+	int result = 0;
+	phci_hcd_mem_init();
+
+	/*register driver */
+	result = isp1763_register_driver(&pehci_hcd_pci_driver);
+	if (!result) {
+		info("Host Driver has been Registered");
+	} else {
+		err("Host Driver has not been Registered with errors : %x",
+			result);
+	}
+
+#ifdef THREAD_BASED 	
+	pehci_hcd_process_irq_in_thread(&(g_pehci_hcd->usb_hcd));
+   	printk("kernel_thread() Enter\n"); 
+#endif
+	
+#ifdef HCD_PACKAGE
+	printk("Register Char Driver for HCD\n");
+	result = register_chrdev(USB_HCD_MAJOR, USB_HCD_MODULE_NAME,
+		&usb_hcddev_fops);
+	
+#endif
+	return result;
+
+}
+
+static void __exit
+pehci_module_cleanup(void)
+{
+#ifdef THREAD_BASED	
+	printk("module exit:  Sending signal to stop thread\n");
+	if (g_stUsbItThreadHandler.phThreadTask != NULL)
+	{
+		send_sig(SIGKILL, g_stUsbItThreadHandler.phThreadTask, 1);
+		mdelay(6);
+	}
+#endif
+
+#ifdef HCD_PACKAGE
+	unregister_chrdev(USB_HCD_MAJOR, USB_HCD_MODULE_NAME);
+#endif
+	isp1763_unregister_driver(&pehci_hcd_pci_driver);
+}
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+module_init(pehci_module_init);
+module_exit(pehci_module_cleanup);
diff --git a/drivers/usb/host/pehci/host/pehci.h b/drivers/usb/host/pehci/host/pehci.h
new file mode 100644
index 0000000..cc6a06b
--- /dev/null
+++ b/drivers/usb/host/pehci/host/pehci.h
@@ -0,0 +1,752 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* Refer to file ~/drivers/usb/host/ehci-dbg.h for copyright owners (kernel version 2.6.9)
+* Code is modified for ST-Ericsson product 
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef	__PEHCI_H__
+#define	__PEHCI_H__
+
+
+#define	DRIVER_AUTHOR	"ST-ERICSSON	  "
+#define	DRIVER_DESC "ISP1763 'Enhanced'	Host Controller	(EHCI) Driver"
+
+/*    bus related stuff	*/
+#define	__ACTIVE		0x01
+#define	__SLEEPY		0x02
+#define	__SUSPEND		0x04
+#define	__TRANSIENT		0x80
+
+#define	USB_STATE_HALT		0
+#define	USB_STATE_RUNNING	(__ACTIVE)
+#define	USB_STATE_READY		(__ACTIVE|__SLEEPY)
+#define	USB_STATE_QUIESCING	(__SUSPEND|__TRANSIENT|__ACTIVE)
+#define	USB_STATE_RESUMING	(__SUSPEND|__TRANSIENT)
+#define	USB_STATE_SUSPENDED	(__SUSPEND)
+
+/* System flags	 */
+#define	HCD_MEMORY		0x0001
+#define	HCD_USB2		0x0020
+#define	HCD_USB11		0x0010
+
+#define	HCD_IS_RUNNING(state) ((state) & __ACTIVE)
+#define	HCD_IS_SUSPENDED(state)	((state) & __SUSPEND)
+
+
+/*---------------------------------------------------
+ *    Host controller related
+ -----------------------------------------------------*/
+/* IRQ line for	the ISP1763 */
+#define	HCD_IRQ			IRQ_GPIO(25)
+#define	CMD_RESET		(1<<1)	/* reset HC not	bus */
+#define	CMD_RUN			(1<<0)	/* start/stop HC */
+#define	STS_PCD			(1<<2)	/* port	change detect */
+/* NOTE:  urb->transfer_flags expected to not use this bit !!! */
+#define	EHCI_STATE_UNLINK	0x8000	/* urb being unlinked */
+
+/*  Bits definations for qha*/
+/* Bits	PID*/
+#define	SETUP_PID		(2)
+#define	OUT_PID			(0)
+#define	IN_PID			(1)
+
+/* Bits	MULTI*/
+#define	MULTI(x)		((x)<< 29)
+#define	XFER_PER_UFRAME(x)	(((x) >> 29) & 0x3)
+
+/*Active, EP type and speed bits */
+#define	QHA_VALID		(1<<0)
+#define	QHA_ACTIVE		(1<<31)
+
+/*1763 error bit maps*/
+#define	HC_MSOF_INT		(1<< 0)
+#define	HC_MSEC_INT		(1 << 1)
+#define	HC_EOT_INT		(1 << 3)
+#define     HC_OPR_REG_INT	(1<<4)
+#define     HC_CLK_RDY_INT	(1<<6)
+#define	HC_INTL_INT		(1 << 7)
+#define	HC_ATL_INT		(1 << 8)
+#define	HC_ISO_INT		(1 << 9)
+#define	HC_OTG_INT		(1 << 10)
+
+/*PTD error codes*/
+#define	PTD_STATUS_HALTED	(1 << 30)
+#define	PTD_XACT_ERROR		(1 << 28)
+#define	PTD_BABBLE		(1 << 29)
+#define PTD_ERROR		(PTD_STATUS_HALTED | PTD_XACT_ERROR | PTD_BABBLE)
+/*ep types*/
+#define	EPTYPE_BULK		(2 << 12)
+#define	EPTYPE_CONTROL		(0 << 12)
+#define	EPTYPE_INT		(3 << 12)
+#define	EPTYPE_ISO		(1 << 12)
+
+#define	PHCI_QHA_LENGTH		32
+
+#define usb_inc_dev_use		usb_get_dev
+#define usb_dec_dev_use		usb_put_dev
+#define usb_free_dev		usb_put_dev
+/*1763 host controller periodic	size*/
+#define PTD_PERIODIC_SIZE	16
+#define MAX_PERIODIC_SIZE	16
+#define PTD_FRAME_MASK		0x1f
+/*periodic list*/
+struct _periodic_list {
+	int framenumber;
+	struct list_head sitd_itd_head;
+	char high_speed;	/*1 - HS ; 0 - FS*/
+	u16 ptdlocation;
+};
+typedef	struct _periodic_list periodic_list;
+
+
+/*iso ptd*/
+struct _isp1763_isoptd {
+	u32 td_info1;
+	u32 td_info2;
+	u32 td_info3;
+	u32 td_info4;
+	u32 td_info5;
+	u32 td_info6;
+	u32 td_info7;
+	u32 td_info8;
+} __attribute__	((aligned(32)));
+
+typedef	struct _isp1763_isoptd isp1763_isoptd;
+
+struct _isp1763_qhint {
+	u32 td_info1;
+	u32 td_info2;
+	u32 td_info3;
+	u32 td_info4;
+	u32 td_info5;
+#define	INT_UNDERRUN (1	<< 2)
+#define	INT_BABBLE    (1 << 1)
+#define	INT_EXACT     (1 << 0)
+	u32 td_info6;
+	u32 td_info7;
+	u32 td_info8;
+} __attribute__	((aligned(32)));
+
+typedef	struct _isp1763_qhint isp1763_qhint;
+
+
+struct _isp1763_qha {
+	u32 td_info1;		/* First 32 bit	*/
+	u32 td_info2;		/* Second 32 bit */
+	u32 td_info3;		/* third 32 bit	*/
+	u32 td_info4;		/* fourth 32 bit */
+	u32 reserved[4];
+};
+typedef	struct _isp1763_qha isp1763_qha, *pisp1763_qha;
+
+
+
+
+/*this does not	cover all interrupts in	1763 chip*/
+typedef	struct _ehci_regs {
+
+	/*standard ehci	registers */
+	u32 command;
+	u32 usbinterrupt;
+	u32 usbstatus;
+	u32 hcsparams;
+	u32 frameindex;
+
+	/*isp1763 interrupt specific registers */
+	u16 hwmodecontrol;
+	u16 interrupt;
+	u16 interruptenable;
+	u32 interruptthreshold;
+	u16 iso_irq_mask_or;
+	u16 int_irq_mask_or;
+	u16 atl_irq_mask_or;
+	u16 iso_irq_mask_and;
+	u16 int_irq_mask_and;
+	u16 atl_irq_mask_and;
+	u16 buffer_status;
+
+	/*isp1763 initialization registers */
+	u32 reset;
+	u32 configflag;
+	u32 ports[4];
+	u32 pwrdwn_ctrl;
+
+	/*isp1763 transfer specific registers */
+	u16 isotddonemap;
+	u16 inttddonemap;
+	u16 atltddonemap;
+	u16 isotdskipmap;
+	u16 inttdskipmap;
+	u16 atltdskipmap;
+	u16 isotdlastmap;
+	u16 inttdlastmap;
+	u16 atltdlastmap;
+	u16 scratch;
+
+} ehci_regs, *pehci_regs;
+
+/*memory management structures*/
+#define MEM_KV
+#ifdef MEM_KV
+typedef struct isp1763_mem_addr {
+	u32 phy_addr;		/* Physical address of the memory */
+	u32 virt_addr;		/* after ioremap() function call */
+	u8 num_alloc;		/* In case n*smaller size is allocated then for clearing purpose */
+	u32 blk_size;		/*block size */
+	u8 blk_num;		/* number of the block */
+	u8 used;		/*used/free */
+} isp1763_mem_addr_t;
+#else
+typedef struct isp1763_mem_addr {
+	void *phy_addr;		/* Physical address of the memory */
+	void *virt_addr;	/* after ioremap() function call */
+	u8 usage;
+	u32 blk_size;		/*block size */
+} isp1763_mem_addr_t;
+
+#endif
+/* type	tag from {qh,itd,sitd,fstn}->hw_next */
+#define	Q_NEXT_TYPE(dma) ((dma)	& __constant_cpu_to_le32 (3 << 1))
+
+/* values for that type	tag */
+#define	Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 1)
+#define	Q_TYPE_QH	__constant_cpu_to_le32 (1 << 1)
+#define	Q_TYPE_SITD	__constant_cpu_to_le32 (2 << 1)
+#define	Q_TYPE_FSTN	__constant_cpu_to_le32 (3 << 1)
+
+/*next queuehead in execution*/
+#define	QH_NEXT(dma)	cpu_to_le32((u32)dma)
+
+struct ehci_qh {
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.6.1 */
+	u32 hw_info1;		/* see EHCI 3.6.2 */
+
+	u32 hw_info2;		/* see EHCI 3.6.2 */
+	u32 hw_current;		/* qtd list - see EHCI 3.6.4 */
+
+	/* qtd overlay (hardware parts of a struct ehci_qtd) */
+	u32 hw_qtd_next;
+	u32 hw_alt_next;
+	u32 hw_token;
+	u32 hw_buf[5];
+	u32 hw_buf_hi[5];
+	
+	/* the rest is HCD-private */
+	dma_addr_t qh_dma;	/* address of qh */
+	struct list_head qtd_list;	/* sw qtd list */
+	struct ehci_qtd	*dummy;
+	struct ehci_qh *reclaim;	/* next	to reclaim */
+
+	atomic_t refcount;
+	wait_queue_head_t waitforcomplete;
+	unsigned stamp;
+
+	u8 qh_state;
+
+	/* periodic schedule info */
+	u8 usecs;		/* intr	bandwidth */
+	u8 gap_uf;		/* uframes split/csplit	gap */
+	u8 c_usecs;		/* ... split completion	bw */
+	unsigned short period;	/* polling interval */
+	unsigned short start;	/* where polling starts	*/
+	u8 datatoggle;		/*data toggle */
+
+	/*handling the ping stuffs */
+	u8 ping;		/*ping bit */
+
+	/*qtd <-> ptd management */
+
+	u32 qtd_ptd_index;	/* Td-PTD map index for	this ptd */
+	u32 type;		/* endpoint type */
+
+	/*iso stuffs */
+	struct usb_host_endpoint *ep;
+	int next_uframe;	/*next uframe for this endpoint	*/
+	struct list_head itd_list;	/*list of tds to this endpoint */
+	isp1763_mem_addr_t memory_addr;
+	struct _periodic_list periodic_list;
+	/*scheduling requirements for this endpoint */
+	u32 ssplit;
+	u32 csplit;
+	u8 totalptds;   // total number of PTDs needed for current URB
+	u8 actualptds;	// scheduled PTDs until now for current URB
+};
+
+/* urb private part for	the driver. */
+typedef	struct {
+	struct ehci_qh *qh;
+	u16 length;		/* number of tds associated with this request */
+	u16 td_cnt;		/* number of tds already serviced */
+	int state;		/* State machine state when URB	is deleted  */
+	int timeout;		/* timeout for bulk transfers */
+	wait_queue_head_t wait;	/* wait	State machine state when URB is	deleted	*/
+	/*FIX solve the	full speed dying */
+	struct timer_list urb_timer;
+	struct list_head qtd_list;
+	struct ehci_qtd	*qtd[0];	/* list	pointer	to all corresponding TDs associated with this request */
+
+} urb_priv_t;
+
+/*
+ * EHCI	Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt	endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear	in both	the async and (for interrupt) periodic schedules.
+ */
+
+
+/*Defination required for the ehci Queuehead */
+#define	QH_HEAD			0x00008000
+#define	QH_STATE_LINKED		1	/* HC sees this	*/
+#define	QH_STATE_UNLINK		2	/* HC may still	see this */
+#define	QH_STATE_IDLE		3	/* HC doesn't see this */
+#define	QH_STATE_UNLINK_WAIT	4	/* LINKED and on reclaim q */
+#define	QH_STATE_COMPLETING	5	/* don't touch token.HALT */
+#define	QH_STATE_TAKE_NEXT	8	/*take the new transfer	from */
+#define	NO_FRAME ((unsigned short)~0)	/* pick	new start */
+
+
+#define EHCI_ITD_TRANLENGTH	0x0fff0000	/*transaction length */
+#define EHCI_ITD_PG		0x00007000	/*page select */
+#define EHCI_ITD_TRANOFFSET	0x00000fff	/*transaction offset */
+#define EHCI_ITD_BUFFPTR	0xfffff000	/*buffer pointer */
+
+struct ehci_sitd {
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.3.1 */
+	u32 hw_transaction[8];	/* see EHCI 3.3.2 */
+#define EHCI_ISOC_ACTIVE	(1<<31)	/* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR	(1<<30)	/* Data buffer error */
+#define EHCI_ISOC_BABBLE	(1<<29)	/* babble detected */
+#define EHCI_ISOC_XACTERR	(1<<28)	/* XactErr - transaction error */
+
+#define EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define EHCI_ITD_IOC		(1 << 15)	/* interrupt on complete */
+
+	u32 hw_bufp[7];		/* see EHCI 3.3.3 */
+	u32 hw_bufp_hi[7];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t sitd_dma;	/* for this itd */
+	struct urb *urb;
+	struct list_head sitd_list;	/* list of urb frames' itds */
+	dma_addr_t buf_dma;	/* frame's buffer address */
+
+	/* for now, only one hw_transaction per itd */
+	u32 transaction;
+	u16 index;		/* in urb->iso_frame_desc */
+	u16 uframe;		/* in periodic schedule */
+	u16 usecs;
+	/*memory address */
+	struct isp1763_mem_addr mem_addr;
+	int length;
+	u32 framenumber;
+	u32 ptdframe;
+	int sitd_index;
+	/*scheduling fields */
+	u32 ssplit;
+	u32 csplit;
+	u32 start_frame;
+};
+
+struct ehci_itd	{
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.3.1 */
+	u32 hw_transaction[8];	/* see EHCI 3.3.2 */
+#define	EHCI_ISOC_ACTIVE	(1<<31)	/* activate transfer this slot */
+#define	EHCI_ISOC_BUF_ERR	(1<<30)	/* Data	buffer error */
+#define	EHCI_ISOC_BABBLE	(1<<29)	/* babble detected */
+#define	EHCI_ISOC_XACTERR	(1<<28)	/* XactErr - transaction error */
+
+#define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define	EHCI_ITD_IOC		(1 << 15)	/* interrupt on	complete */
+
+	u32 hw_bufp[7];		/* see EHCI 3.3.3 */
+	u32 hw_bufp_hi[7];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t itd_dma;	/* for this itd	*/
+	struct urb *urb;
+	struct list_head itd_list;	/* list	of urb frames' itds */
+	dma_addr_t buf_dma;	/* frame's buffer address */
+	u8 num_of_pkts;		/*number of packets for this ITD */
+	/* for now, only one hw_transaction per	itd */
+	u32 transaction;
+	u16 index;		/* in urb->iso_frame_desc */
+	u16 uframe;		/* in periodic schedule	*/
+	u16 usecs;
+	/*memory address */
+	struct isp1763_mem_addr	mem_addr;
+	int length;
+	u32 multi;
+	u32 framenumber;
+	u32 ptdframe;
+	int itd_index;
+	/*scheduling fields */
+	u32 ssplit;
+	u32 csplit;
+};
+
+/*
+ * EHCI	Specification 0.95 Section 3.5
+ * QTD:	describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block	Diagram".
+ *
+ * These are associated	only with "QH" (Queue Head) structures,
+ * used	with control, bulk, and	interrupt transfers.
+ */
+struct ehci_qtd	{
+	/* first part defined by EHCI spec */
+	u32 hw_next;		/* see EHCI 3.5.1 */
+	u32 hw_alt_next;	/* see EHCI 3.5.2 */
+	u32 hw_token;		/* see EHCI 3.5.3 */
+
+	u32 hw_buf[5];		/* see EHCI 3.5.4 */
+	u32 hw_buf_hi[5];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t qtd_dma;	/* qtd address */
+	struct list_head qtd_list;	/* sw qtd list */
+	struct urb *urb;	/* qtd's urb */
+	size_t length;		/* length of buffer */
+	u32 state;		/*state	of the qtd */
+#define	QTD_STATE_NEW			0x100
+#define	QTD_STATE_DONE			0x200
+#define	QTD_STATE_SCHEDULED		0x400
+#define	QTD_STATE_LAST			0x800
+	struct isp1763_mem_addr	mem_addr;
+};
+
+#define	QTD_TOGGLE			(1 << 31)	/* data	toggle */
+#define	QTD_LENGTH(tok)			(((tok)>>16) & 0x7fff)
+#define	QTD_IOC				(1 << 15)	/* interrupt on	complete */
+#define	QTD_CERR(tok)			(((tok)>>10) & 0x3)
+#define	QTD_PID(tok)			(((tok)>>8) & 0x3)
+#define	QTD_STS_ACTIVE			(1 << 7)	/* HC may execute this */
+#define	QTD_STS_HALT			(1 << 6)	/* halted on error */
+#define	QTD_STS_DBE			(1 << 5)	/* data	buffer error (in HC) */
+#define	QTD_STS_BABBLE			(1 << 4)	/* device was babbling (qtd halted) */
+#define	QTD_STS_XACT			(1 << 3)	/* device gave illegal response	*/
+#define	QTD_STS_MMF			(1 << 2)	/* incomplete split transaction	*/
+#define	QTD_STS_STS			(1 << 1)	/* split transaction state */
+#define	QTD_STS_PING			(1 << 0)	/* issue PING? */
+
+/* for periodic/async schedules	and qtd	lists, mark end	of list	*/
+#define	EHCI_LIST_END	__constant_cpu_to_le32(1)	/* "null pointer" to hw	*/
+#define	QTD_NEXT(dma)	cpu_to_le32((u32)dma)
+
+struct _phci_driver;
+struct _isp1763_hcd;
+#define	EHCI_MAX_ROOT_PORTS 1
+
+#include <linux/usb/hcd.h>
+
+#define USBNET
+#ifdef USBNET 
+struct isp1763_async_cleanup_urb {
+        struct list_head urb_list;
+        struct urb *urb;
+};
+#endif
+
+
+/*host controller*/
+typedef	struct _phci_hcd {
+
+	struct usb_hcd usb_hcd;
+	spinlock_t lock;
+
+	/* async schedule support */
+	struct ehci_qh *async;
+	struct ehci_qh *reclaim;
+	/* periodic schedule support */
+	unsigned periodic_size;
+	int next_uframe;	/* scan	periodic, start	here */
+	int periodic_sched;	/* periodic activity count */
+	int periodic_more_urb;
+	struct usb_device *otgdev;	/*otg deice, with address 2 */
+	struct timer_list rh_timer;	/* drives root hub */
+	struct list_head dev_list;	/* devices on this bus */
+	struct list_head urb_list;	/*iso testing */
+
+	/*msec break in	interrupts */
+	atomic_t nuofsofs;
+	atomic_t missedsofs;
+
+	struct isp1763_dev *dev;
+	/*hw info */
+	u8 *iobase;
+	u32 iolength;
+	u8 *plxiobase;
+	u32 plxiolength;
+
+	int irq;		/* irq allocated */
+	int state;		/*state	of the host controller */
+	unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
+	ehci_regs regs;
+
+	struct _isp1763_qha qha;
+	struct _isp1763_qhint qhint;
+	struct _isp1763_isoptd isotd;
+
+	struct tasklet_struct tasklet;
+	/*this timer is	going to run every 20 msec */
+	struct timer_list watchdog;
+	void (*worker_function)	(struct	_phci_hcd * hcd);
+	struct _periodic_list periodic_list[PTD_PERIODIC_SIZE];
+#ifdef USBNET 
+	struct isp1763_async_cleanup_urb cleanup_urb;
+#endif
+} phci_hcd, *pphci_hcd;
+
+/*usb_device->hcpriv, points to	this structure*/
+typedef	struct hcd_dev {
+	struct list_head dev_list;
+	struct list_head urb_list;
+} hcd_dev;
+
+#define	usb_hcd_to_pehci_hcd(hcd)   container_of(hcd, struct _phci_hcd,	usb_hcd)
+
+/*td allocation*/
+#ifdef CONFIG_PHCI_MEM_SLAB
+
+#define	qha_alloc(t,c) kmem_cache_alloc(c,ALLOC_FLAGS)
+#define	qha_free(c,x) kmem_cache_free(c,x)
+static kmem_cache_t *qha_cache,	*qh_cache, *qtd_cache;
+static int
+phci_hcd_mem_init(void)
+{
+	/* qha TDs accessed by controllers and host */
+	qha_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0,
+				      SLAB_HWCACHE_ALIGN, NULL,	NULL);
+	if (!qha_cache)	{
+		printk("no TD cache?");
+		return -ENOMEM;
+	}
+
+	/* qh TDs accessed by controllers and host */
+	qh_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0,
+				     SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!qh_cache) {
+		printk("no TD cache?");
+		return -ENOMEM;
+	}
+
+	/* qtd	accessed by controllers	and host */
+	qtd_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0,
+				      SLAB_HWCACHE_ALIGN, NULL,	NULL);
+	if (!qtd_cache)	{
+		printk("no TD cache?");
+		return -ENOMEM;
+	}
+	return 0;
+}
+static void
+phci_mem_cleanup(void)
+{
+	if (qha_cache && kmem_cache_destroy(qha_cache))
+		err("td_cache remained");
+	qha_cache = 0;
+}
+#else
+
+#define	qha_alloc(t,c)			kmalloc(t,ALLOC_FLAGS)
+#define	qha_free(c,x)			kfree(x)
+#define	qha_cache			0
+
+
+#ifdef CONFIG_ISO_SUPPORT
+/*memory constants*/
+#define BLK_128_	2
+#define BLK_256_	3
+#define BLK_1024_	1
+#define BLK_2048_	3
+#define BLK_4096_	3 //1
+#define BLK_8196_	0 //1
+#define BLK_TOTAL	(BLK_128_+BLK_256_ + BLK_1024_ +BLK_2048_+ BLK_4096_+BLK_8196_)
+
+#define BLK_SIZE_128	128
+#define BLK_SIZE_256	256
+#define BLK_SIZE_1024	1024
+#define BLK_SIZE_2048	2048
+#define BLK_SIZE_4096	4096
+#define BLK_SIZE_8192	8192
+
+#define  COMMON_MEMORY	1
+
+#else
+#define BLK_256_	8
+#define BLK_1024_	6
+#define BLK_4096_	3
+#define BLK_TOTAL	(BLK_256_ + BLK_1024_ + BLK_4096_)
+#define BLK_SIZE_256	256
+#define BLK_SIZE_1024	1024
+#define BLK_SIZE_4096	4096
+#endif
+static void phci_hcd_mem_init(void);
+static inline void
+phci_mem_cleanup(void)
+{
+	return;
+}
+
+#endif
+
+#define	PORT_WKOC_E			(1<<22)	/* wake	on overcurrent (enable)	*/
+#define	PORT_WKDISC_E			(1<<21)	/* wake	on disconnect (enable) */
+#define	PORT_WKCONN_E			(1<<20)	/* wake	on connect (enable) */
+/* 19:16 for port testing */
+/* 15:14 for using port	indicator leds (if HCS_INDICATOR allows) */
+#define	PORT_OWNER			(1<<13)	/* true: companion hc owns this	port */
+#define	PORT_POWER			(1<<12)	/* true: has power (see	PPC) */
+#define	PORT_USB11(x)			(((x)&(3<<10))==(1<<10))	/* USB 1.1 device */
+/* 11:10 for detecting lowspeed	devices	(reset vs release ownership) */
+/* 9 reserved */
+#define	PORT_RESET			(1<<8)	/* reset port */
+#define	PORT_SUSPEND			(1<<7)	/* suspend port	*/
+#define	PORT_RESUME			(1<<6)	/* resume it */
+#define	PORT_OCC			(1<<5)	/* over	current	change */
+
+#define	PORT_OC				(1<<4)	/* over	current	active */
+#define	PORT_PEC			(1<<3)	/* port	enable change */
+#define	PORT_PE				(1<<2)	/* port	enable */
+#define	PORT_CSC			(1<<1)	/* connect status change */
+#define	PORT_CONNECT			(1<<0)	/* device connected */
+#define PORT_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_OCC)	
+/*Legends,
+ * ATL	  control, bulk	transfer
+ * INTL	  interrupt transfer
+ * ISTL	  iso transfer
+ * */
+
+/*buffer(transfer) bitmaps*/
+#define	ATL_BUFFER			0x1
+#define	INT_BUFFER			0x2
+#define	ISO_BUFFER			0x4
+#define	BUFFER_MAP			0x7
+
+/* buffer type for ST-ERICSSON HC */
+#define	TD_PTD_BUFF_TYPE_ATL		0	/* ATL buffer */
+#define	TD_PTD_BUFF_TYPE_INTL		1	/* INTL	buffer */
+#define	TD_PTD_BUFF_TYPE_ISTL		2	/* ISO buffer */
+#define	TD_PTD_TOTAL_BUFF_TYPES		(TD_PTD_BUFF_TYPE_ISTL +1)
+/*maximum number of tds	per transfer type*/
+#define	TD_PTD_MAX_BUFF_TDS		16
+
+/*invalid td index in the headers*/
+#define	TD_PTD_INV_PTD_INDEX		0xFFFF
+/*Host controller buffer defination*/
+#define	INVALID_FRAME_NUMBER		0xFFFFFFFF
+/*per td transfer size*/
+#define	HC_ATL_PL_SIZE			4096
+#define	HC_ISTL_PL_SIZE			1024
+#define	HC_INTL_PL_SIZE			1024
+
+/*TD_PTD_MAP states*/
+#define	TD_PTD_NEW			0x0000
+#define	TD_PTD_ACTIVE			0x0001
+#define	TD_PTD_IDLE			0x0002
+#define	TD_PTD_REMOVE			0x0004
+#define	TD_PTD_RELOAD			0x0008
+#define	TD_PTD_IN_SCHEDULE		0x0010
+#define	TD_PTD_DONE			0x0020
+
+#define	PTD_RETRY(x)			(((x) >> 23) & 0x3)
+#define	PTD_PID(x)			(((x) >> 10) & (0x3))
+#define	PTD_NEXTTOGGLE(x)		(((x) >> 25) & (0x1))
+#define	PTD_XFERRED_LENGTH(x)		((x) & 0x7fff)
+#define	PTD_XFERRED_NONHSLENGTH(x)	((x) & 0x7ff)
+#define	PTD_PING_STATE(x)		(((x) >> 26) & (0x1))
+
+/* urb state*/
+#define	DELETE_URB			0x0008
+#define	NO_TRANSFER_ACTIVE		0xFFFF
+#define	NO_TRANSFER_DONE		0x0000
+#define	MAX_PTD_BUFFER_SIZE		4096	/*max ptd size */
+
+/*information of the td	in headers of host memory*/
+typedef	struct td_ptd_map {
+	u32 state;		/* ACTIVE, NEW,	TO_BE_REMOVED */
+	u8 datatoggle;		/*to preserve the data toggle for ATL/ISTL transfers */
+	u32 ptd_bitmap;		/* Bitmap of this ptd in HC headers */
+	u32 ptd_header_addr;	/* headers address of  this td */
+	u32 ptd_data_addr;	/*data address of this td to write in and read from */
+	/*this is address is actual RAM	address	not the	CPU address
+	 * RAM address = (CPU ADDRESS-0x400) >>	3
+	 * */
+	u32 ptd_ram_data_addr;
+	u8 lasttd;		/*last td , complete the transfer */
+	struct ehci_qh *qh;	/* endpoint */
+	struct ehci_qtd	*qtd;	/* qtds	for this endpoint */
+	struct ehci_itd	*itd;	/*itd pointer */
+	struct ehci_sitd *sitd;	/*itd pointer */
+	/*iso specific only */
+	u32 grouptdmap;		/*if td	need to	complete with error, then process all the tds
+				   in the groupmap    */
+} td_ptd_map_t;
+
+/*buffer(ATL/ISTL/INTL)	managemnet*/
+typedef	struct td_ptd_map_buff {
+	u8 buffer_type;		/* Buffer type:	BUFF_TYPE_ATL/INTL/ISTL0/ISTL1 */
+	u8 active_ptds;		/* number of active td's in the	buffer */
+	u8 total_ptds;		/* Total number	of td's	present	in the buffer (active +	tobe removed + skip) */
+	u8 max_ptds;		/* Maximum number of ptd's(32) this buffer can withstand */
+	u16 active_ptd_bitmap;	/* Active PTD's	bitmap */
+	u16 pending_ptd_bitmap;	/* skip	PTD's bitmap */
+	td_ptd_map_t map_list[TD_PTD_MAX_BUFF_TDS];	/* td_ptd_map list */
+} td_ptd_map_buff_t;
+
+
+#define     USB_HCD_MAJOR           0
+#define     USB_HCD_MODULE_NAME     "isp1763hcd"
+/* static char devpath[] = "/dev/isp1763hcd"; */
+
+#define HCD_IOC_MAGIC	'h'
+
+#define     HCD_IOC_POWERDOWN							_IO(HCD_IOC_MAGIC, 1)
+#define     HCD_IOC_POWERUP								_IO(HCD_IOC_MAGIC, 2)
+#define     HCD_IOC_TESTSE0_NACK						_IO(HCD_IOC_MAGIC, 3)
+#define     HCD_IOC_TEST_J								_IO(HCD_IOC_MAGIC,4)
+#define     HCD_IOC_TEST_K								_IO(HCD_IOC_MAGIC,5)
+#define     HCD_IOC_TEST_TESTPACKET						_IO(HCD_IOC_MAGIC,6)
+#define     HCD_IOC_TEST_FORCE_ENABLE					_IO(HCD_IOC_MAGIC,7)
+#define	  HCD_IOC_TEST_SUSPEND_RESUME				_IO(HCD_IOC_MAGIC,8)
+#define     HCD_IOC_TEST_SINGLE_STEP_GET_DEV_DESC		_IO(HCD_IOC_MAGIC,9)
+#define     HCD_IOC_TEST_SINGLE_STEP_SET_FEATURE		_IO(HCD_IOC_MAGIC,10)
+#define     HCD_IOC_TEST_STOP							_IO(HCD_IOC_MAGIC,11)
+#define     HCD_IOC_SUSPEND_BUS							_IO(HCD_IOC_MAGIC,12)
+#define     HCD_IOC_RESUME_BUS							_IO(HCD_IOC_MAGIC,13)
+#define     HCD_IOC_REMOTEWAKEUP_BUS					_IO(HCD_IOC_MAGIC,14)
+
+#define HOST_COMPILANCE_TEST_ENABLE	1
+#define HOST_COMP_TEST_SE0_NAK	1
+#define HOST_COMP_TEST_J	2
+#define HOST_COMP_TEST_K	3
+#define HOST_COMP_TEST_PACKET		4
+#define HOST_COMP_TEST_FORCE_ENABLE	5
+#define HOST_COMP_HS_HOST_PORT_SUSPEND_RESUME	6
+#define HOST_COMP_SINGLE_STEP_GET_DEV_DESC	7
+#define HOST_COMP_SINGLE_STEP_SET_FEATURE	8
+
+#endif
diff --git a/drivers/usb/host/pehci/host/qtdptd.c b/drivers/usb/host/pehci/host/qtdptd.c
new file mode 100644
index 0000000..093800e
--- /dev/null
+++ b/drivers/usb/host/pehci/host/qtdptd.c
@@ -0,0 +1,1315 @@
+/* 
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010 
+*
+* ISP1763 Linux OTG Controller driver : host
+* 
+* This program is free software; you can redistribute it and/or modify it under the terms of 
+* the GNU General Public License as published by the Free Software Foundation; version 
+* 2 of the License. 
+* 
+* 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. 
+* 
+* You should have received a copy of the GNU General Public License 
+* along with this program; if not, write to the Free Software 
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
+* 
+* This is a host controller driver file.  QTD processing is handled here.
+* 
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+/*   Td	managenment routines  */
+
+#define	QUEUE_HEAD_NOT_EMPTY	0x001
+
+
+/*free the location used by removed urb/endpoint*/
+static void
+phci_hcd_release_td_ptd_index(struct ehci_qh *qh)
+{
+	td_ptd_map_buff_t *td_ptd_buff = &td_ptd_map_buff[qh->type];
+	td_ptd_map_t *td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+	/*hold the global lock here */
+	td_ptd_map->state = TD_PTD_NEW;
+	qh->qh_state = QH_STATE_IDLE;
+	/*
+	   set these values to NULL as schedule
+	   is based on these values,
+	   rather td_ptd_map state
+	 */
+	td_ptd_map->qh = NULL;
+	td_ptd_map->qtd	= NULL;
+
+	td_ptd_buff->active_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+	/* Only	pending	transfers on current QH	must be	cleared	*/
+	td_ptd_buff->pending_ptd_bitmap	&= ~td_ptd_map->ptd_bitmap;
+
+	pehci_entry("--	%s: Exit\n", __FUNCTION__);
+
+}
+
+/*print	ehciqtd*/
+static void
+print_ehci_qtd(struct ehci_qtd *qtd)
+{
+	pehci_print("hwnext 0x%08x, altnext 0x%08x,token 0x%08x, length	%d\n",
+		    qtd->hw_next, qtd->hw_alt_next,
+		    le32_to_cpu(qtd->hw_token),	qtd->length);
+
+	pehci_print("buf[0] 0x%08x\n", qtd->hw_buf[0]);
+
+}
+
+/*delete all qtds linked with this urb*/
+static void
+phci_hcd_qtd_list_free(phci_hcd	* ehci,
+		       struct urb *urb,	struct list_head *qtd_list)
+{
+	struct list_head *entry, *temp;
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	list_for_each_safe(entry, temp,	qtd_list) {
+		struct ehci_qtd	*qtd;
+		qtd = list_entry(entry,	struct ehci_qtd, qtd_list);
+	if(!list_empty(&qtd->qtd_list))
+		list_del_init(&qtd->qtd_list);
+		qha_free(qha_cache, qtd);
+	}
+
+	pehci_entry("--	%s: Exit \n", __FUNCTION__);
+}
+
+
+/*
+ * free	all the	qtds for this transfer,	also
+ * free	the Host memory	to be reused
+ */
+static void
+phci_hcd_urb_free_priv(phci_hcd	* hcd,
+		       urb_priv_t * urb_priv_to_remove,	struct ehci_qh *qh)
+{
+	int i =	0;
+	struct ehci_qtd	*qtd;
+	for (i = 0; i <	urb_priv_to_remove->length; i++) {
+		if (urb_priv_to_remove->qtd[i])	{
+			qtd = urb_priv_to_remove->qtd[i];
+
+			if(!list_empty(&qtd->qtd_list))
+				list_del_init(&qtd->qtd_list);
+
+			/* This	is required when the device is abruptly	disconnected and the
+			 * PTDs	are not	completely processed
+			 */
+			if (qtd->length)
+				phci_hcd_mem_free(&qtd->mem_addr);
+
+			qha_free(qha_cache, qtd);
+			urb_priv_to_remove->qtd[i] = 0;
+			qtd = 0;
+		}
+
+	}
+	
+	return;
+}
+
+
+/*allocate the qtd*/
+struct ehci_qtd	*
+phci_hcd_qtd_allocate(int mem_flags)
+{
+
+	struct ehci_qtd	*qtd = 0;
+	qtd = kmalloc(sizeof *qtd, mem_flags);
+	if (!qtd)
+	{
+		return 0;
+	}
+	
+	memset(qtd, 0, sizeof *qtd);
+	qtd->qtd_dma = cpu_to_le32(qtd);
+	qtd->hw_next = EHCI_LIST_END;
+	qtd->hw_alt_next = EHCI_LIST_END;
+	qtd->state = QTD_STATE_NEW;
+	INIT_LIST_HEAD(&qtd->qtd_list);
+	return qtd;
+}
+
+/*
+ * calculates host memory for current length transfer td,
+ * maximum td length is	4K(custom made)
+ * */
+static int
+phci_hcd_qtd_fill(struct urb *urb,
+		  struct ehci_qtd *qtd,
+		  dma_addr_t buf, size_t len, int token, int *status)
+{
+	int count = 0;
+
+	qtd->hw_buf[0] = (u32) buf;
+	/*max lenggth is HC_ATL_PL_SIZE	*/
+	if (len	> HC_ATL_PL_SIZE) {
+		count =	HC_ATL_PL_SIZE;
+	} else {
+		count =	len;
+	}
+	qtd->hw_token =	cpu_to_le32((count << 16) | token);
+	qtd->length = count;
+
+	pehci_print("%s:qtd %p,	token %8x bytes	%d dma %x\n",
+		__FUNCTION__, qtd, le32_to_cpu(qtd->hw_token), count,
+		qtd->hw_buf[0]);
+
+	return count;
+}
+
+
+/*
+ * makes number	of qtds	required for
+ * interrupt/bulk/control transfer length
+ * and initilize qtds
+ * */
+struct list_head *
+phci_hcd_make_qtd(phci_hcd * hcd,
+		  struct list_head *head, struct urb *urb, int *status)
+{
+
+	struct ehci_qtd	*qtd, *qtd_prev;
+	dma_addr_t buf,	map_buf;
+	int len, maxpacket;
+	int is_input;
+	u32 token;
+	int cnt	= 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+	pehci_entry("++	%s, Entered\n",	__FUNCTION__);
+
+	/*take the qtd from already allocated
+	   structure from hcd_submit_urb
+	 */
+	qtd = urb_priv->qtd[cnt];
+	if (unlikely(!qtd)) {
+		*status	= -ENOMEM;
+		return 0;
+	}
+
+	qtd_prev = 0;
+	list_add_tail(&qtd->qtd_list, head);
+
+	qtd->urb = urb;
+
+	token =	QTD_STS_ACTIVE;
+	token |= (EHCI_TUNE_CERR << 10);
+
+	len = urb->transfer_buffer_length;
+
+	is_input = usb_pipein(urb->pipe);
+
+	if (usb_pipecontrol(urb->pipe))	{
+		/* SETUP pid */
+		if (phci_hcd_qtd_fill(urb, qtd,	cpu_to_le32(urb->setup_packet),
+			sizeof(struct usb_ctrlrequest),
+			token |	(2 /* "setup" */	<< 8),
+			status)	<	0) {
+			goto cleanup;
+		}
+
+		cnt++;		/* increment the index */
+		print_ehci_qtd(qtd);
+		/* ... and always at least one more pid	*/
+		token ^= QTD_TOGGLE;
+		qtd_prev = qtd;
+		qtd = urb_priv->qtd[cnt];
+		if (unlikely(!qtd)) {
+			*status	= -ENOMEM;
+			goto cleanup;
+		}
+		qtd->urb = urb;
+		qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+		list_add_tail(&qtd->qtd_list, head);
+	}
+
+	/*
+	 * data	transfer stage:	 buffer	setup
+	 */
+	len = urb->transfer_buffer_length;
+	if (likely(len > 0)) {
+		/*update the buffer address */
+		buf = cpu_to_le32(urb->transfer_buffer);
+	} else {
+		buf = map_buf =	cpu_to_le32(0);	/*set-up stage has no data. */
+	}
+
+	/* So are we waiting for the ack only or there is a data stage with out. */
+	if (!buf || usb_pipein(urb->pipe)) {
+		token |= (1 /* "in" */	<< 8);
+	}
+	/* else	it's already initted to	"out" pid (0 <<	8) */
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe,
+				  usb_pipeout(urb->pipe)) & 0x07ff;
+
+
+	/*
+	 * buffer gets wrapped in one or more qtds;
+	 * last	one may	be "short" (including zero len)
+	 * and may serve as a control status ack
+	 */
+
+	for (;;) {
+		int this_qtd_len;
+		this_qtd_len =
+			phci_hcd_qtd_fill(urb, qtd, buf, len, token, status);
+		if (this_qtd_len < 0)
+			goto cleanup;
+		print_ehci_qtd(qtd);
+		len -= this_qtd_len;
+		buf += this_qtd_len;
+		cnt++;
+		/* qh makes control packets use	qtd toggle; maybe switch it */
+		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) {
+			token ^= QTD_TOGGLE;
+		}
+
+		if (likely(len <= 0)) {
+			break;
+		}
+		qtd_prev = qtd;
+		qtd = urb_priv->qtd[cnt];
+		if (unlikely(!qtd)) {
+			goto cleanup;
+		}
+		qtd->urb = urb;
+		qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+		list_add_tail(&qtd->qtd_list, head);
+	}
+
+	/*
+	 * control requests may	need a terminating data	"status" ack;
+	 * bulk	ones may need a	terminating short packet (zero length).
+	 */
+	if (likely(buf != 0)) {
+		int one_more = 0;
+		if (usb_pipecontrol(urb->pipe))	{
+			one_more = 1;
+			token ^= 0x0100;	/* "in"	<--> "out"  */
+			token |= QTD_TOGGLE;	/* force DATA1 */
+
+		} else if (usb_pipebulk(urb->pipe)	/* bulk	data exactly terminated	on zero	lenth */
+			&&(urb->transfer_flags & URB_ZERO_PACKET)
+			&& !(urb->transfer_buffer_length % maxpacket)) {
+			one_more = 1;
+		}
+		if (one_more) {
+			qtd_prev = qtd;
+			qtd = urb_priv->qtd[cnt];
+			if (unlikely(!qtd)) {
+				goto cleanup;
+			}
+
+			qtd->urb = urb;
+			qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+			list_add_tail(&qtd->qtd_list, head);
+			phci_hcd_qtd_fill(urb, qtd, 0, 0, token, status);
+			print_ehci_qtd(qtd);
+			cnt++;
+		}
+	}
+
+	/*this is our last td for current transfer */
+	qtd->state |= QTD_STATE_LAST;
+
+	/*number of tds	*/
+	if (urb_priv->length !=	cnt) {
+		err("Never Error: number of tds	allocated %d exceeding %d\n",
+		    urb_priv->length, cnt);
+	}
+	/* by default, enable interrupt	on urb completion */
+	if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) {
+		qtd->hw_token |= __constant_cpu_to_le32(QTD_IOC);
+	}
+
+	pehci_entry("--	%s, Exit\n", __FUNCTION__);
+	return head;
+
+	cleanup:
+	phci_hcd_qtd_list_free(hcd, urb, head);
+	return 0;
+}
+
+/*allocates a queue head(endpoint*/
+struct ehci_qh *
+phci_hcd_qh_alloc(phci_hcd * hcd)
+{
+
+	struct ehci_qh *qh = kmalloc(sizeof(struct ehci_qh), GFP_ATOMIC);
+	if (!qh)
+	{
+		return qh;
+	}
+	
+	memset(qh, 0, sizeof *qh);
+	atomic_set(&qh->refcount, 1);
+	init_waitqueue_head(&qh->waitforcomplete);
+	qh->qh_dma = (u32) qh;
+	INIT_LIST_HEAD(&qh->qtd_list);
+	INIT_LIST_HEAD(&qh->itd_list);
+	qh->next_uframe	= -1;
+	return qh;
+}
+
+/* calculates header address for the tds*/
+static int
+phci_hcd_fill_ptd_addresses(td_ptd_map_t * td_ptd_map, int index, int bufftype)
+{
+	int i =	0;
+	unsigned long tdlocation = 0;
+	/*
+	 * the below payloadlocation and
+	 * payloadsize are redundant
+	 * */
+	unsigned long payloadlocation =	0;
+	unsigned long payloadsize = 0;
+	pehci_entry("++	%s: enter\n", __FUNCTION__);
+	switch (bufftype) {
+		/*atl header starts at 0xc00 */
+	case TD_PTD_BUFF_TYPE_ATL:
+		tdlocation = 0x0c00;
+		/*redundant */
+		payloadsize = 0x1000;
+		payloadlocation	= 0x1000;
+		break;
+	case TD_PTD_BUFF_TYPE_INTL:
+		/*interrupt header
+		 * starts at 0x800
+		 * */
+		tdlocation = 0x0800;
+		/*redundant */
+		payloadlocation	= 0x1000;
+		payloadsize = 0x1000;
+		break;
+
+	case TD_PTD_BUFF_TYPE_ISTL:
+		/*iso header starts
+		 * at 0x400*/
+
+		tdlocation = 0x0400;
+		/*redunndant */
+		payloadlocation	= 0x1000;
+		payloadsize = 0x1000;
+
+		break;
+	}
+
+
+	i = index;
+	payloadlocation	+= (i) * payloadsize;	/*each payload is of 4096 bytes	*/
+	tdlocation += (i) * PHCI_QHA_LENGTH;	/*each td is of	32 bytes */
+	td_ptd_map->ptd_header_addr = tdlocation;
+	td_ptd_map->ptd_data_addr = payloadlocation;
+	td_ptd_map->ptd_ram_data_addr =	((payloadlocation - 0x0400) >> 3);
+	pehci_print
+		("Index: %d, Header: 0x%08x, Payload: 0x%08x,Data start	address: 0x%08x\n",
+		 index,	td_ptd_map->ptd_header_addr, td_ptd_map->ptd_data_addr,
+		 td_ptd_map->ptd_ram_data_addr);
+	pehci_entry("--	%s: Exit", __FUNCTION__);
+	return payloadlocation;
+}
+
+
+/*--------------------------------------------------------------*
+ * calculate the header	location for the current
+ * endpoint, if	found returns a	valid index
+ * else	invalid
+ -----------------------------------------------------------*/
+static void
+phci_hcd_get_qtd_ptd_index(struct ehci_qh *qh,
+			   struct ehci_qtd *qtd, struct	ehci_itd *itd)
+{
+	u8 buff_type = td_ptd_pipe_x_buff_type[qh->type];
+	u8 qtd_ptd_index;	/*, index; */
+	/*this is the location of the ptd's skip map/done map, also
+	   calculating the td header, payload, data start address
+	   location */
+	u8 bitmap = 0x1;
+	u8 max_ptds;
+
+	td_ptd_map_buff_t *ptd_map_buff	= &(td_ptd_map_buff[buff_type]);
+	pehci_entry("++	%s, Entered, buffer type %d\n",	__FUNCTION__,
+		    buff_type);
+
+	/* ATL PTDs can	wait */
+	max_ptds = (buff_type == TD_PTD_BUFF_TYPE_ATL)
+		? TD_PTD_MAX_BUFF_TDS :	ptd_map_buff->max_ptds;
+
+	for (qtd_ptd_index = 0;	qtd_ptd_index <	max_ptds; qtd_ptd_index++) {	/* Find	the first free slot */
+		if (ptd_map_buff->map_list[qtd_ptd_index].state	== TD_PTD_NEW) {
+			/* Found a free	slot */
+			if (qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX) {
+				qh->qtd_ptd_index = qtd_ptd_index;
+			}
+			ptd_map_buff->map_list[qtd_ptd_index].datatoggle = 0;
+			/*put the ptd_index into operational state */
+			ptd_map_buff->map_list[qtd_ptd_index].state =
+				TD_PTD_ACTIVE;
+			ptd_map_buff->map_list[qtd_ptd_index].qtd = qtd;
+			/* No td transfer is in	progress */
+			ptd_map_buff->map_list[qtd_ptd_index].itd = itd;
+			/*initialize endpoint(queuehead) */
+			ptd_map_buff->map_list[qtd_ptd_index].qh = qh;
+			ptd_map_buff->map_list[qtd_ptd_index].ptd_bitmap =
+				bitmap << qtd_ptd_index;
+			phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+				map_list[qtd_ptd_index],
+				qh->qtd_ptd_index,
+				buff_type);
+			ptd_map_buff->map_list[qtd_ptd_index].lasttd = 0;
+			ptd_map_buff->total_ptds++;	/* update # of total td's */
+			/*make the queuehead map, to process in	the phci_schedule_ptds */
+			ptd_map_buff->active_ptd_bitmap	|=
+				(bitmap	<< qtd_ptd_index);
+			break;
+		}
+	}
+	pehci_entry("--	%s, Exit\n", __FUNCTION__);
+	return;
+
+}				/* phci_get_td_ptd_index */
+
+
+
+/*
+ * calculate the header	location for the endpoint and
+ * all tds on this endpoint will use the same
+ * header location for all transfers on	this endpoint.
+ * also	puts the endpoint into the linked state
+ * */
+static void
+phci_hcd_qh_link_async(phci_hcd	* hcd, struct ehci_qh *qh, int *status)
+{
+	struct ehci_qtd	*qtd = 0;
+	struct list_head *qtd_list = &qh->qtd_list;
+
+#ifdef MSEC_INT_BASED
+	td_ptd_map_buff_t *ptd_map_buff;
+	td_ptd_map_t *td_ptd_map;
+#endif
+
+	/*  take the first td, in case we are not able to schedule the new td
+	   and this is going for remove
+	 */
+	qtd = list_entry(qtd_list->next, struct	ehci_qtd, qtd_list);
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	/* Assign a td-ptd index for this ed so	that we	can put	ptd's in the HC	buffers	*/
+
+	qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
+	phci_hcd_get_qtd_ptd_index(qh, qtd, NULL);	/* Get a td-ptd	index */
+	if (qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX) {
+		err("can not find the location in our buffer\n");
+		*status	= -ENOSPC;
+		return;
+	}
+#ifdef MSEC_INT_BASED
+	/*first	transfers in sof interrupt goes	into pending */
+	ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+	td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+	ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
+
+#endif
+	/* open	the halt so that it acessed */
+	qh->hw_token &=	~__constant_cpu_to_le32(QTD_STS_HALT);
+	qh->qh_state = QH_STATE_LINKED;
+	qh->qh_state |=	QH_STATE_TAKE_NEXT;
+	pehci_entry("--	%s: Exit , qh %p\n", __FUNCTION__, qh);
+
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * mainly used for setting up current td on current
+ * endpoint(queuehead),	endpoint may be	new or
+ * halted one
+ * */
+
+static inline void
+phci_hcd_qh_update(phci_hcd * ehci, struct ehci_qh *qh,	struct ehci_qtd	*qtd)
+{
+	/*make this current td */
+	qh->hw_current = QTD_NEXT(qtd->qtd_dma);
+	qh->hw_qtd_next	= QTD_NEXT(qtd->qtd_dma);
+	qh->hw_alt_next	= EHCI_LIST_END;
+	/* HC must see latest qtd and qh data before we	clear ACTIVE+HALT */
+	wmb();
+	qh->hw_token &=	__constant_cpu_to_le32(QTD_TOGGLE | QTD_STS_PING);
+}
+
+/*
+ * used	for ATL, INT transfers
+ * function creates new	endpoint,
+ * calculates bandwidth	for interrupt transfers,
+ * and initialize the qh based on endpoint type/speed
+ * */
+struct ehci_qh *
+phci_hcd_make_qh(phci_hcd * hcd,
+		 struct	urb *urb, struct list_head *qtd_list, int *status)
+{
+	struct ehci_qh *qh = 0;
+	u32 info1 = 0, info2 = 0;
+	int is_input, type;
+	int maxp = 0;
+	int mult = 0;
+	int bustime = 0;
+	struct ehci_qtd	*qtd =
+		list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	qh = phci_hcd_qh_alloc(hcd);
+	if (!qh) {
+		*status	= -ENOMEM;
+		return 0;
+	}
+
+	/*
+	 * init	endpoint/device	data for this QH
+	 */
+	info1 |= usb_pipeendpoint(urb->pipe) <<	8;
+	info1 |= usb_pipedevice(urb->pipe) << 0;
+
+	is_input = usb_pipein(urb->pipe);
+	type = usb_pipetype(urb->pipe);
+	maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+	mult = 1 + ((maxp >> 11) & 0x3);
+
+	/*set this queueheads index to invalid */
+	qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
+
+	switch (type) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+		qh->type = TD_PTD_BUFF_TYPE_ATL;
+		break;
+
+	case PIPE_INTERRUPT:
+		qh->type = TD_PTD_BUFF_TYPE_INTL;
+		break;
+	case PIPE_ISOCHRONOUS:
+		qh->type = TD_PTD_BUFF_TYPE_ISTL;
+		break;
+
+	}
+
+
+
+	if (type == PIPE_INTERRUPT) {
+		/*for this interrupt transfer check how	much bustime in	usecs required */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		bustime = usb_check_bandwidth(urb->dev, urb);
+
+		if (bustime < 0) {
+			*status = -ENOSPC;
+			goto done;
+		}
+
+		usb_claim_bandwidth(urb->dev, urb, bustime,
+			usb_pipeisoc(urb->pipe));
+#else
+#endif
+		qh->usecs = bustime;
+
+		qh->start = NO_FRAME;
+
+		if (urb->dev->speed == USB_SPEED_HIGH) {
+			qh->c_usecs = 0;
+			qh->gap_uf = 0;
+			/*after	how many uframes this interrupt	is to be executed */
+			qh->period = urb->interval >> 3;
+			if (qh->period < 1) {
+				printk("intr period %d uframes,\n",
+				urb->interval);
+			}
+			/*restore the original urb->interval in	qh->period */
+			qh->period = urb->interval;
+
+		} else {
+			/* gap is f(FS/LS transfer times) */
+			qh->gap_uf = 1 + 7;	/*usb_calc_bus_time (urb->dev->speed,
+						   is_input, 0,	maxp) /	(125 * 1000); */
+
+			if (is_input) {	/* SPLIT, gap, CSPLIT+DATA */
+
+				qh->c_usecs = qh->usecs	+ 1;	/*HS_USECS (0);	*/
+				qh->usecs = 10;	/*HS_USECS (1);	*/
+			} else {	/* SPLIT+DATA, gap, CSPLIT */
+				qh->usecs += 10;	/*HS_USECS (1);	*/
+				qh->c_usecs = 1;	/*HS_USECS (0);	*/
+			}
+
+
+			/*take the period ss/cs	scheduling will	be
+			   handled by submit urb
+			 */
+			qh->period = urb->interval;
+		}
+	}
+
+	/* using TT? */
+	switch (urb->dev->speed) {
+	case USB_SPEED_LOW:
+		info1 |= (1 << 12);	/* EPS "low" */
+		/* FALL	THROUGH	*/
+
+	case USB_SPEED_FULL:
+		/* EPS 0 means "full" */
+		if (type != PIPE_INTERRUPT) {
+			info1 |= (EHCI_TUNE_RL_TT << 28);
+		}
+		if (type == PIPE_CONTROL) {
+			info1 |= (1 << 27);	/* for TT */
+			info1 |= 1 << 14;	/* toggle from qtd */
+		}
+		info1 |= maxp << 16;
+
+		info2 |= (EHCI_TUNE_MULT_TT << 30);
+		info2 |= urb->dev->ttport << 23;
+		info2 |= urb->dev->tt->hub->devnum << 16;
+		break;
+
+
+	case USB_SPEED_HIGH:	/* no TT involved */
+		info1 |= (2 << 12);	/* EPS "high" */
+		if (type == PIPE_CONTROL) {
+			info1 |= (EHCI_TUNE_RL_HS << 28);
+			info1 |= 64 << 16;	/* usb2	fixed maxpacket	*/
+
+			info1 |= 1 << 14;	/* toggle from qtd */
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
+		} else if (type	== PIPE_BULK) {
+			info1 |= (EHCI_TUNE_RL_HS << 28);
+			info1 |= 512 <<	16;	/* usb2	fixed maxpacket	*/
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
+		} else {	/* PIPE_INTERRUPT */
+			info1 |= (maxp & 0x7ff)	/*max_packet (maxp) */ <<16;
+			info2 |= mult /*hb_mult	(maxp) */  << 30;
+		}
+		break;
+
+	default:
+		pehci_print("bogus dev %p speed	%d", urb->dev, urb->dev->speed);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	done:
+#else
+#endif
+		qha_free(qha_cache, qh);
+		return 0;
+	}			/*end of switch	*/
+
+	/* NOTE:  if (PIPE_INTERRUPT) {	scheduler sets s-mask }	*/
+
+	/* init	as halted, toggle clear, advance to dummy */
+	qh->qh_state = QH_STATE_IDLE;
+	qh->hw_info1 = cpu_to_le32(info1);
+	qh->hw_info2 = cpu_to_le32(info2);
+	/*link the tds here */
+	list_splice(qtd_list, &qh->qtd_list);
+	phci_hcd_qh_update(hcd,	qh, qtd);
+	qh->hw_token = cpu_to_le32(QTD_STS_HALT);
+	if (!usb_pipecontrol(urb->pipe)) {
+		usb_settoggle(urb->dev,	usb_pipeendpoint(urb->pipe), !is_input,
+			1);
+	}
+	pehci_entry("--	%s: Exit, qh %p\n", __FUNCTION__, qh);
+	return qh;
+}
+
+
+/*-----------------------------------------------------------*/
+/*
+ * Hardware maintains data toggle (like	OHCI) ... here we (re)initialize
+ * the hardware	data toggle in the QH, and set the pseudo-toggle in udev
+ * so we can see if usb_clear_halt() was called.  NOP for control, since
+ * we set up qh->hw_info1 to always use	the QTD	toggle bits.
+ */
+static inline void
+phci_hcd_clear_toggle(struct usb_device	*udev, int ep, int is_out,
+		      struct ehci_qh *qh)
+{
+	pehci_print("clear toggle, dev %d ep 0x%x-%s\n",
+		    udev->devnum, ep, is_out ? "out" : "in");
+	qh->hw_token &=	~__constant_cpu_to_le32(QTD_TOGGLE);
+	usb_settoggle(udev, ep,	is_out,	1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs	appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null	if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+struct ehci_qh *
+phci_hcd_qh_append_tds(phci_hcd	* hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+	struct urb *urb,	struct list_head *qtd_list,
+	void **ptr, int *status)
+{
+
+	int epnum;
+
+	struct ehci_qh *qh = 0;
+	struct ehci_qtd	*qtd =
+		list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+	td_ptd_map_buff_t *ptd_map_buff;
+	td_ptd_map_t *td_ptd_map;
+
+
+
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	epnum = ep->desc.bEndpointAddress;
+#else
+	epnum = urb->ep->desc.bEndpointAddress;
+#endif
+
+	qh = (struct ehci_qh *)	*ptr;
+	if (likely(qh != 0)) {
+		u32 hw_next = QTD_NEXT(qtd->qtd_dma);
+		pehci_print("%Queue head already %p\n",	qh);
+
+		ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+		td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+
+		/* maybe patch the qh used for set_address */
+		if (unlikely
+			(epnum == 0	&& le32_to_cpu(qh->hw_info1 & 0x7f) == 0)) {
+			qh->hw_info1 |=	cpu_to_le32(usb_pipedevice(urb->pipe));
+		}
+
+		/* is an URB is	queued to this qh already? */
+		if (unlikely(!list_empty(&qh->qtd_list))) {
+			struct ehci_qtd	*last_qtd;
+			/* update the last qtd's "next"	pointer	*/
+			last_qtd = list_entry(qh->qtd_list.prev,
+				struct ehci_qtd, qtd_list);
+
+			/*queue	head is	not empty just add the
+			   td at the end of it , and return from here
+			 */
+			last_qtd->hw_next = hw_next;
+
+			/*set the status as positive */
+			*status	= (u32)	QUEUE_HEAD_NOT_EMPTY;
+
+			/* no URB queued */
+		} else {
+
+	//		qh->qh_state = QH_STATE_IDLE;
+
+
+			/* usb_clear_halt() means qh data toggle gets reset */
+			if (usb_pipebulk(urb->pipe)
+				&& unlikely(!usb_gettoggle(urb->dev, (epnum	& 0x0f),
+				!(epnum & 0x80)))) {
+
+				phci_hcd_clear_toggle(urb->dev,
+					epnum & 0x0f,
+					!(epnum &	0x80), qh);
+
+				/*reset	our data toggle	*/
+
+				qh->datatoggle = 0;
+				qh->ping = 0;
+
+			}
+			phci_hcd_qh_update(hcd,	qh, qtd);
+		}
+		/*put everything in pedning, will be cleared during scheduling */
+		ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
+		list_splice(qtd_list, qh->qtd_list.prev);
+	} else {
+		qh = phci_hcd_make_qh(hcd, urb,	qtd_list, status);
+		*ptr = qh;
+	}
+	pehci_entry("--	%s: Exit qh %p\n", __FUNCTION__, qh);
+	return qh;
+}
+
+/*link qtds to endpoint(qh)*/
+struct ehci_qh *
+phci_hcd_submit_async(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+		      struct list_head *qtd_list, struct urb *urb, int *status)
+{
+	struct ehci_qtd	*qtd;
+	struct hcd_dev *dev;
+	int epnum;
+
+#ifndef THREAD_BASED
+	unsigned long flags;
+#endif
+
+	
+	struct ehci_qh *qh = 0;
+
+	urb_priv_t *urb_priv = urb->hcpriv;
+
+	qtd = list_entry(qtd_list->next, struct	ehci_qtd, qtd_list);
+	dev = (struct hcd_dev *) urb->hcpriv;
+	epnum =	usb_pipeendpoint(urb->pipe);
+	if (usb_pipein(urb->pipe) && !usb_pipecontrol(urb->pipe)) {
+		epnum |= 0x10;
+	}
+
+	pehci_entry("++	%s, enter\n", __FUNCTION__);
+
+	/* ehci_hcd->lock guards shared	data against other CPUs:
+	 *   ehci_hcd:	    async, reclaim, periodic (and shadow), ...
+	 *   hcd_dev:	    ep[]
+
+	 *   ehci_qh:	    qh_next, qtd_list
+
+	 *   ehci_qtd:	    qtd_list
+	 *
+	 * Also, hold this lock	when talking to	HC registers or
+	 * when	updating hw_* fields in	shared qh/qtd/... structures.
+	 */
+#ifndef THREAD_BASED
+	spin_lock_irqsave(&hcd->lock, flags);
+#endif
+
+	spin_lock(&hcd_data_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+	usb_hcd_link_urb_to_ep(&hcd->usb_hcd, urb);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	qh = phci_hcd_qh_append_tds(hcd, ep, urb, qtd_list, &ep->hcpriv,
+		status);
+#else
+	qh = phci_hcd_qh_append_tds(hcd, urb, qtd_list, &urb->ep->hcpriv,
+		status);
+#endif
+	if (!qh	|| *status < 0) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+		usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, urb);
+#endif
+		goto cleanup;
+	}
+	/* Control/bulk	operations through TTs don't need scheduling,
+	 * the HC and TT handle	it when	the TT has a buffer ready.
+	 */
+
+	/* now the quehead can not be in the unlink state */
+
+//	printk("qh->qh_state:0x%x \n",qh->qh_state);
+	if (qh->qh_state == QH_STATE_UNLINK) {
+		pehci_info("%s:	free the urb,qh->state %x\n", __FUNCTION__,
+			   qh->qh_state);
+		phci_hcd_qtd_list_free(hcd, urb, &qh->qtd_list);
+		spin_unlock(&hcd_data_lock);
+		
+#ifndef THREAD_BASED			
+		spin_unlock_irqrestore(&hcd->lock, flags);
+#endif
+		*status	= -ENODEV;
+		return 0;
+	}
+
+	if (likely(qh != 0)) {
+		urb_priv->qh = qh;
+		if (likely(qh->qh_state	== QH_STATE_IDLE))
+			phci_hcd_qh_link_async(hcd, qh,	status);
+	}
+
+	cleanup:
+	spin_unlock(&hcd_data_lock);
+
+#ifndef THREAD_BASED			
+	/* free	it from	lock systme can	sleep now */
+	spin_unlock_irqrestore(&hcd->lock, flags);
+#endif
+	
+	/* could not get the QH	terminate and clean. */
+	if (unlikely(qh	== 0) || *status < 0) {
+		phci_hcd_qtd_list_free(hcd, urb, qtd_list);
+		return qh;
+	}
+	return qh;
+}
+
+/*
+ * initilaize the s-mask c-mask	for
+ * interrupt transfers.
+ */
+static int
+phci_hcd_qhint_schedule(phci_hcd * hcd,
+			struct ehci_qh *qh,
+			struct ehci_qtd	*qtd,
+			struct _isp1763_qhint *qha, struct urb *urb)
+{
+	int i =	0;
+	u32 td_info3 = 0;
+	u32 td_info5 = 0;
+	u32 period = 0;
+	u32 usofmask = 1;
+	u32 usof = 0;
+	u32 ssplit = 0,	csplit = 0xFF;
+	int maxpacket;
+	u32 numberofusofs = 0;
+
+	/*and since whol msec frame is empty, i	can schedule in	any uframe */
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe));
+	maxpacket &= 0x7ff;
+	/*length of the	data per uframe	*/
+	maxpacket = XFER_PER_UFRAME(qha->td_info1) * maxpacket;
+
+	/*caculate the number of uframes are required */
+	numberofusofs =	urb->transfer_buffer_length / maxpacket;
+	/*if something left */
+	if (urb->transfer_buffer_length	% maxpacket) {
+		numberofusofs += 1;
+	}
+
+	for (i = 0; i <	numberofusofs; i++) {
+		usofmask <<= i;
+		usof |=	usofmask;
+
+	}
+
+	/*
+	   for full/low	speed devices, as we
+	   have	seperate location for all the endpoints
+	   let the start split goto the	first uframe, means 0 uframe
+	 */
+	if (urb->dev->speed != USB_SPEED_HIGH && usb_pipeint(urb->pipe)) {
+		/*set the complete splits */
+		/*set all the bits and lets see	whats happening	*/
+		/*but this will	be set based on	the maximum packet size	*/
+		ssplit = usof;
+		/*  need to fix	it */
+		csplit = 0x1C;
+		qha->td_info6 =	csplit;
+		period = qh->period;
+		if (period >= 32) {
+			period = qh->period / 2;
+		}
+		td_info3 = period;
+		goto done;
+
+	} else {
+		if (qh->period >= 8) {
+			period = qh->period / 8;
+		} else {
+			period = qh->period;
+		}
+	}
+	/*our limitaion	is maximum of 32 ie 31,	5 bits */
+	if (period >= 32) {
+		period = 32;
+		/*devide by 2 */
+		period >>= 1;
+	}
+	if (qh->period >= 8) {
+		/*millisecond period */
+		td_info3 = (period << 3);
+	} else {
+		/*usof based tranmsfers	*/
+		/*minimum 4 usofs */
+		td_info3 = period;
+		usof = 0x11;
+	}
+
+	done:
+	td_info5 = usof;
+	qha->td_info3 |= td_info3;
+	qha->td_info5 |= usof;
+	return numberofusofs;
+}
+
+/*link interrupts qtds to endpoint*/
+struct ehci_qh *
+phci_hcd_submit_interrupt(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	struct usb_host_endpoint *ep,
+#else
+#endif
+			  struct list_head *qtd_list,
+			  struct urb *urb, int *status)
+{
+	struct ehci_qtd	*qtd;
+	struct _hcd_dev	*dev;
+	int epnum;
+	unsigned long flags;
+	struct ehci_qh *qh = 0;
+	urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+	qtd = list_entry(qtd_list->next, struct	ehci_qtd, qtd_list);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+	dev = (struct hcd_dev *) urb->hcpriv;
+	epnum = ep->desc.bEndpointAddress;
+
+	pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+
+	/*check for more than one urb queued for this endpoint */
+	qh = ep->hcpriv;
+#else
+	dev = (struct _hcd_dev *) (urb->hcpriv);
+	epnum = urb->ep->desc.bEndpointAddress;
+
+	pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+
+	/*check for more than one urb queued for this endpoint */
+	qh = (struct ehci_qh *) urb->ep->hcpriv;
+#endif
+
+	spin_lock_irqsave(&hcd->lock, flags);
+	if (unlikely(qh	!= 0)) {
+		if (!list_empty(&qh->qtd_list))	{
+			*status	= -EBUSY;
+			goto done;
+		} else {
+			td_ptd_map_buff_t *ptd_map_buff;
+			td_ptd_map_t *td_ptd_map;
+			ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+			td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+			ptd_map_buff->pending_ptd_bitmap |=
+				td_ptd_map->ptd_bitmap;
+			 /*NEW*/ td_ptd_map->qtd = qtd;
+			/* maybe reset hardware's data toggle in the qh	*/
+			if (unlikely(!usb_gettoggle(urb->dev, epnum & 0x0f,
+				!(epnum & 0x80)))) {
+
+				/*reset	our data toggle	*/
+				td_ptd_map->datatoggle = 0;
+				usb_settoggle(urb->dev,	epnum &	0x0f,
+					!(epnum &	0x80), 1);
+				qh->datatoggle = 0;
+			}
+			/* trust the QH	was set	up as interrupt	... */
+			list_splice(qtd_list, &qh->qtd_list);
+		}
+	}
+
+
+	if (!qh) {
+		qh = phci_hcd_make_qh(hcd, urb,	qtd_list, status);
+		if (likely(qh == 0)) {
+			*status	= -ENOMEM;
+			goto done;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+		ep->hcpriv = qh;
+#else
+		urb->ep->hcpriv = qh;
+#endif
+	}
+
+	if (likely(qh != 0)) {
+		urb_priv->qh = qh;
+		if (likely(qh->qh_state	== QH_STATE_IDLE)) {
+			phci_hcd_qh_link_async(hcd, qh,	status);
+		}
+	}
+
+
+	done:
+	/* free	it from	lock systme can	sleep now */
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	/* could not get the QH	terminate and clean. */
+	if (unlikely(qh	== 0) || *status < 0) {
+		phci_hcd_qtd_list_free(hcd, urb, qtd_list);
+		return qh;
+	}
+	return qh;
+}
+
+
+
+
+/*
+ * converts original EHCI QTD into PTD(Proprietary transfer descriptor)
+ * we call PTD as qha also for atl transfers
+ * for ATL and INT transfers
+ */
+void *
+phci_hcd_qha_from_qtd(phci_hcd * hcd,
+	struct ehci_qtd *qtd,
+	struct urb *urb,
+	void *ptd, u32 ptd_data_addr, struct ehci_qh *qh)
+{
+	u8 toggle = qh->datatoggle;
+	u32 token = 0;
+	u32 td_info1 = 0;
+	u32 td_info3 = 0;
+	u32 td_info4 = 0;
+	int maxpacket =	0;
+	u32 length = 0,	temp = 0;
+	/*for non high speed devices */
+	u32 portnum = 0;
+	u32 hubnum = 0;
+	u32 se = 0, rl = 0x0, nk = 0x0;
+	u8 datatoggle =	0;
+	struct isp1763_mem_addr	*mem_addr = &qtd->mem_addr;
+	u32 data_addr =	0;
+	u32 multi = 0;
+	struct _isp1763_qha *qha = (isp1763_qha	*) ptd;
+	pehci_entry("++	%s: Entered\n",	__FUNCTION__);
+
+	maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+	multi =	1 + ((maxpacket	>> 11) & 0x3);
+
+	maxpacket &= 0x7ff;
+
+	/************************first word*********************************/
+	length = qtd->length;
+	td_info1 = QHA_VALID;
+	td_info1 |= (length << 3);
+	td_info1 |= (maxpacket << 18);
+	td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+	td_info1 |= MULTI(multi);
+	/*set the first	dword */
+	qha->td_info1 =	td_info1;
+
+	pehci_print("%s: length	%d, 1st	word 0x%08x\n",	__FUNCTION__, length,
+		    qha->td_info1);
+
+	/*******************second word***************************************/
+	temp = qtd->hw_token;
+
+	/*take the pid,	thats of only interest to me from qtd,
+	 */
+
+	temp = temp & 0x0300;
+	temp = temp >> 8;
+	/*take the endpoint and	its 3 bits */
+	token =	(usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+	token |= usb_pipedevice(urb->pipe) << 3;
+
+	if (urb->dev->speed != USB_SPEED_HIGH) {
+		pehci_print("device is full/low	speed, %d\n", urb->dev->speed);
+		token |= 1 << 14;
+		portnum	= urb->dev->ttport;
+		 /*IMMED*/ hubnum = urb->dev->tt->hub->devnum;
+		token |= portnum << 18;
+		token |= hubnum	<< 25;
+		/*for non-high speed transfer
+		   reload and nak counts are zero
+		 */
+		rl = 0x0;
+		nk = 0x0;
+
+	}
+
+	/*se should be 0x2 for only low	speed devices */
+	if (urb->dev->speed == USB_SPEED_LOW) {
+		se = 0x2;
+	}
+
+	if (usb_pipeint(urb->pipe)) {
+		/*	reload count and nakcount is
+		   required for	only async transfers
+		 */
+		rl = 0x0;
+	}
+
+	/*set the se field, should be zero for all
+	   but low speed devices
+	 */
+	token |= se << 16;
+	/*take the pid */
+	token |= temp << 10;
+
+	if (usb_pipebulk(urb->pipe)) {
+		token |= EPTYPE_BULK;
+	} else if (usb_pipeint(urb->pipe)) {
+		token |= EPTYPE_INT;
+	} else if (usb_pipeisoc(urb->pipe)) {
+		token |= EPTYPE_ISO;
+	}
+
+
+	qha->td_info2 =	token;
+
+	pehci_print("%s: second	word 0x%08x, qtd token 0x%08x\n",
+		    __FUNCTION__, qha->td_info2, temp);
+
+	/***********************Third word*************************************/
+
+	/*calculate the	data start address from	mem_addr for qha */
+
+	data_addr = ((u32) (mem_addr->phy_addr)	& 0xffff) - 0x400;
+	data_addr >>= 3;
+	pehci_print("data start	address	%x\n", data_addr);
+	/*use this field only if there
+	 * is something	to transfer
+	 * */
+	if (length) {
+		td_info3 = data_addr <<	8;
+	}
+	/*RL Count, 16 */
+	td_info3 |= (rl	<< 25);
+	qha->td_info3 =	td_info3;
+
+	pehci_print("%s: third word 0x%08x, tdinfo 0x%08x\n",
+		__FUNCTION__, qha->td_info3, td_info3);
+
+
+	/**************************fourt word*************************************/
+
+	if (usb_pipecontrol(urb->pipe))	{
+		datatoggle = qtd->hw_token >> 31;
+	} else {
+		/*take the data	toggle from the	previous completed transfer
+		   or zero in case of fresh */
+		datatoggle = toggle;
+	}
+
+	td_info4 = QHA_ACTIVE;
+	/*dt */
+	td_info4 |= datatoggle << 25;	/*QHA_DATA_TOGGLE; */
+	/*3 retry count	for setup else forever */
+	if (PTD_PID(qha->td_info2) == SETUP_PID) {
+		td_info4 |= (3 << 23);
+	} else {
+		td_info4 |= (0 << 23);
+	}
+	
+	/*nak count */
+	td_info4 |= (nk	<< 19);
+
+	td_info4 |= (qh->ping << 26);
+	qha->td_info4 =	td_info4;
+#ifdef PTD_DUMP_SCHEDULE
+	printk("SCHEDULE PTD DUMPE\n") ;
+	printk("SDW0: 0x%08x\n",qha->td_info1);
+	printk("SDW1: 0x%08x\n",qha->td_info2);
+	printk("SDW2: 0x%08x\n",qha->td_info3);
+	printk("SDW3: 0x%08x\n",qha->td_info4);
+#endif
+	pehci_print("%s: fourt word 0x%08x\n", __FUNCTION__, qha->td_info4);
+	pehci_entry("--	%s: Exit, qha %p\n", __FUNCTION__, qha);
+	return qha;
+
+}
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 1bfcd02..13828e0 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -218,6 +218,21 @@
 	  See <http://www.linux-usb.org/usbtest/> for more information,
 	  including sample test device firmware and "how to use it".
 
+config USB_EHSET_TEST_FIXTURE
+	tristate "USB EHSET Test Fixture Driver"
+	depends on USB && USB_EHCI_EHSET
+	default n
+	help
+	  Say Y here if you want to use EHSET Test Fixture device for host
+	  compliance testing.
+
+	  This driver initiates test modes on the downstream port to which the
+	  test fixture is attached.
+
+	  See <http://www.usb.org/developers/onthego/EHSET_v1.01.pdf>
+	  for more information.
+
+
 config USB_ISIGHTFW
 	tristate "iSight firmware loading support"
 	depends on USB
@@ -244,3 +259,37 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called yurex.
 
+config USB_QCOM_DIAG_BRIDGE
+	tristate "USB Qualcomm diagnostic bridge driver"
+	depends on USB
+	help
+	  Say Y here if you have a Qualcomm modem device connected via USB that
+	  will be bridged in kernel space. This driver communicates with the
+	  diagnostic interface and allows for bridging with the diag forwarding
+	  driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called diag_bridge.  If unsure, choose N.
+
+config USB_QCOM_DIAG_BRIDGE_TEST
+	tristate "USB Qualcomm diagnostic bridge driver test"
+	depends on USB && USB_QCOM_DIAG_BRIDGE
+	help
+	  Say Y here if you want to enable the test hook for the
+	  Qualcomm diag bridge driver. When enabled, this will create
+	  a debugfs file entry named "diag_bridge_test" which can be used
+	  to send a ping command to the diag port of the modem over USB.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called diag_bridge_test.  If unsure, choose N.
+
+config USB_QCOM_MDM_BRIDGE
+	tristate "USB Qualcomm modem bridge driver for DUN and RMNET"
+	depends on USB
+	help
+	  Say Y here if you have a Qualcomm modem device connected via USB that
+	  will be bridged in kernel space. This driver works as a bridge to pass
+	  control and data packets between the modem and peripheral usb gadget
+	  driver for dial up network and RMNET.
+	  To compile this driver as a module, choose M here: the module
+	  will be called mdm_bridge. If unsure, choose N.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 796ce7e..b4aee65 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -21,9 +21,15 @@
 obj-$(CONFIG_USB_LEGOTOWER)		+= legousbtower.o
 obj-$(CONFIG_USB_RIO500)		+= rio500.o
 obj-$(CONFIG_USB_TEST)			+= usbtest.o
+obj-$(CONFIG_USB_EHSET_TEST_FIXTURE)	+= ehset.o
 obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
 obj-$(CONFIG_USB_USS720)		+= uss720.o
 obj-$(CONFIG_USB_SEVSEG)		+= usbsevseg.o
 obj-$(CONFIG_USB_YUREX)			+= yurex.o
 
 obj-$(CONFIG_USB_SISUSBVGA)		+= sisusbvga/
+
+obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE)	+= diag_bridge.o
+obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE_TEST)	+= diag_bridge_test.o
+mdm_bridge-y				:= mdm_ctrl_bridge.o mdm_data_bridge.o
+obj-$(CONFIG_USB_QCOM_MDM_BRIDGE) 	+= mdm_bridge.o
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
new file mode 100644
index 0000000..96e5a90
--- /dev/null
+++ b/drivers/usb/misc/diag_bridge.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <mach/diag_bridge.h>
+
+#define DRIVER_DESC	"USB host diag bridge driver"
+#define DRIVER_VERSION	"1.0"
+
+struct diag_bridge {
+	struct usb_device	*udev;
+	struct usb_interface	*ifc;
+	struct usb_anchor	submitted;
+	__u8			in_epAddr;
+	__u8			out_epAddr;
+	int			err;
+	struct kref		kref;
+	struct diag_bridge_ops	*ops;
+	struct platform_device	*pdev;
+
+	/* debugging counters */
+	unsigned long		bytes_to_host;
+	unsigned long		bytes_to_mdm;
+	unsigned		pending_reads;
+	unsigned		pending_writes;
+};
+struct diag_bridge *__dev;
+
+int diag_bridge_open(struct diag_bridge_ops *ops)
+{
+	struct diag_bridge	*dev = __dev;
+
+	if (!dev) {
+		err("dev is null");
+		return -ENODEV;
+	}
+
+	dev->ops = ops;
+	dev->err = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(diag_bridge_open);
+
+void diag_bridge_close(void)
+{
+	struct diag_bridge	*dev = __dev;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	usb_kill_anchored_urbs(&dev->submitted);
+
+	dev->ops = 0;
+}
+EXPORT_SYMBOL(diag_bridge_close);
+
+static void diag_bridge_read_cb(struct urb *urb)
+{
+	struct diag_bridge	*dev = urb->context;
+	struct diag_bridge_ops	*cbs = dev->ops;
+
+	dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
+			urb->status, urb->actual_length);
+
+	if (urb->status == -EPROTO) {
+		dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
+		/* save error so that subsequent read/write returns ESHUTDOWN */
+		dev->err = urb->status;
+		return;
+	}
+
+	if (cbs && cbs->read_complete_cb)
+		cbs->read_complete_cb(cbs->ctxt,
+			urb->transfer_buffer,
+			urb->transfer_buffer_length,
+			urb->status < 0 ? urb->status : urb->actual_length);
+
+	dev->bytes_to_host += urb->actual_length;
+	dev->pending_reads--;
+}
+
+int diag_bridge_read(char *data, int size)
+{
+	struct urb		*urb = NULL;
+	unsigned int		pipe;
+	struct diag_bridge	*dev = __dev;
+	int			ret;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	if (!size) {
+		dev_err(&dev->udev->dev, "invalid size:%d\n", size);
+		return -EINVAL;
+	}
+
+	if (!dev->ifc) {
+		dev_err(&dev->udev->dev, "device is disconnected\n");
+		return -ENODEV;
+	}
+
+	/* if there was a previous unrecoverable error, just quit */
+	if (dev->err)
+		return -ESHUTDOWN;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&dev->udev->dev, "unable to allocate urb\n");
+		return -ENOMEM;
+	}
+
+	ret = usb_autopm_get_interface(dev->ifc);
+	if (ret < 0) {
+		dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
+		usb_free_urb(urb);
+		return ret;
+	}
+
+	pipe = usb_rcvbulkpipe(dev->udev, dev->in_epAddr);
+	usb_fill_bulk_urb(urb, dev->udev, pipe, data, size,
+				diag_bridge_read_cb, dev);
+	usb_anchor_urb(urb, &dev->submitted);
+	dev->pending_reads++;
+
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+		dev->pending_reads--;
+		usb_unanchor_urb(urb);
+		usb_free_urb(urb);
+		usb_autopm_put_interface(dev->ifc);
+		return ret;
+	}
+
+	usb_autopm_put_interface(dev->ifc);
+	usb_free_urb(urb);
+
+	return 0;
+}
+EXPORT_SYMBOL(diag_bridge_read);
+
+static void diag_bridge_write_cb(struct urb *urb)
+{
+	struct diag_bridge	*dev = urb->context;
+	struct diag_bridge_ops	*cbs = dev->ops;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	usb_autopm_put_interface_async(dev->ifc);
+
+	if (urb->status == -EPROTO) {
+		dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
+		/* save error so that subsequent read/write returns ESHUTDOWN */
+		dev->err = urb->status;
+		return;
+	}
+
+	if (cbs && cbs->write_complete_cb)
+		cbs->write_complete_cb(cbs->ctxt,
+			urb->transfer_buffer,
+			urb->transfer_buffer_length,
+			urb->status < 0 ? urb->status : urb->actual_length);
+
+	dev->bytes_to_mdm += urb->actual_length;
+	dev->pending_writes--;
+}
+
+int diag_bridge_write(char *data, int size)
+{
+	struct urb		*urb = NULL;
+	unsigned int		pipe;
+	struct diag_bridge	*dev = __dev;
+	int			ret;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	if (!size) {
+		dev_err(&dev->udev->dev, "invalid size:%d\n", size);
+		return -EINVAL;
+	}
+
+	if (!dev->ifc) {
+		dev_err(&dev->udev->dev, "device is disconnected\n");
+		return -ENODEV;
+	}
+
+	/* if there was a previous unrecoverable error, just quit */
+	if (dev->err)
+		return -ESHUTDOWN;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		err("unable to allocate urb");
+		return -ENOMEM;
+	}
+
+	ret = usb_autopm_get_interface(dev->ifc);
+	if (ret < 0) {
+		dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
+		usb_free_urb(urb);
+		return ret;
+	}
+
+	pipe = usb_sndbulkpipe(dev->udev, dev->out_epAddr);
+	usb_fill_bulk_urb(urb, dev->udev, pipe, data, size,
+				diag_bridge_write_cb, dev);
+	usb_anchor_urb(urb, &dev->submitted);
+	dev->pending_writes++;
+
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+		dev->pending_writes--;
+		usb_unanchor_urb(urb);
+		usb_free_urb(urb);
+		usb_autopm_put_interface(dev->ifc);
+		return ret;
+	}
+
+	usb_free_urb(urb);
+
+	return 0;
+}
+EXPORT_SYMBOL(diag_bridge_write);
+
+static void diag_bridge_delete(struct kref *kref)
+{
+	struct diag_bridge *dev =
+		container_of(kref, struct diag_bridge, kref);
+
+	usb_put_dev(dev->udev);
+	__dev = 0;
+	kfree(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	512
+static ssize_t diag_read_stats(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	struct diag_bridge	*dev = __dev;
+	char			*buf;
+	int			ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+			"epin:%d, epout:%d\n"
+			"bytes to host: %lu\n"
+			"bytes to mdm: %lu\n"
+			"pending reads: %u\n"
+			"pending writes: %u\n"
+			"last error: %d\n",
+			dev->in_epAddr, dev->out_epAddr,
+			dev->bytes_to_host, dev->bytes_to_mdm,
+			dev->pending_reads, dev->pending_writes,
+			dev->err);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t diag_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct diag_bridge	*dev = __dev;
+
+	dev->bytes_to_host = dev->bytes_to_mdm = 0;
+	dev->pending_reads = dev->pending_writes = 0;
+
+	return count;
+}
+
+const struct file_operations diag_stats_ops = {
+	.read = diag_read_stats,
+	.write = diag_reset_stats,
+};
+
+static struct dentry *dent;
+
+static void diag_bridge_debugfs_init(void)
+{
+	struct dentry *dfile;
+
+	dent = debugfs_create_dir("diag_bridge", 0);
+	if (IS_ERR(dent))
+		return;
+
+	dfile = debugfs_create_file("status", 0444, dent, 0, &diag_stats_ops);
+	if (!dfile || IS_ERR(dfile))
+		debugfs_remove(dent);
+}
+
+static void diag_bridge_debugfs_cleanup(void)
+{
+	if (dent) {
+		debugfs_remove_recursive(dent);
+		dent = NULL;
+	}
+}
+#else
+static inline void diag_bridge_debugfs_init(void) { }
+static inline void diag_bridge_debugfs_cleanup(void) { }
+#endif
+
+static int
+diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)
+{
+	struct diag_bridge		*dev;
+	struct usb_host_interface	*ifc_desc;
+	struct usb_endpoint_descriptor	*ep_desc;
+	int				i;
+	int				ret = -ENOMEM;
+	__u8				ifc_num;
+
+	dbg("%s: id:%lu", __func__, id->driver_info);
+
+	ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
+
+	/* is this interface supported ? */
+	if (ifc_num != id->driver_info)
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		pr_err("%s: unable to allocate dev\n", __func__);
+		return -ENOMEM;
+	}
+	dev->pdev = platform_device_alloc("diag_bridge", -1);
+	if (!dev->pdev) {
+		pr_err("%s: unable to allocate platform device\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+	__dev = dev;
+
+	dev->udev = usb_get_dev(interface_to_usbdev(ifc));
+	dev->ifc = ifc;
+	kref_init(&dev->kref);
+	init_usb_anchor(&dev->submitted);
+
+	ifc_desc = ifc->cur_altsetting;
+	for (i = 0; i < ifc_desc->desc.bNumEndpoints; i++) {
+		ep_desc = &ifc_desc->endpoint[i].desc;
+
+		if (!dev->in_epAddr && usb_endpoint_is_bulk_in(ep_desc))
+			dev->in_epAddr = ep_desc->bEndpointAddress;
+
+		if (!dev->out_epAddr && usb_endpoint_is_bulk_out(ep_desc))
+			dev->out_epAddr = ep_desc->bEndpointAddress;
+	}
+
+	if (!(dev->in_epAddr && dev->out_epAddr)) {
+		err("could not find bulk in and bulk out endpoints");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	usb_set_intfdata(ifc, dev);
+	diag_bridge_debugfs_init();
+	platform_device_add(dev->pdev);
+
+	dev_dbg(&dev->udev->dev, "%s: complete\n", __func__);
+
+	return 0;
+
+error:
+	if (dev)
+		kref_put(&dev->kref, diag_bridge_delete);
+
+	return ret;
+}
+
+static void diag_bridge_disconnect(struct usb_interface *ifc)
+{
+	struct diag_bridge	*dev = usb_get_intfdata(ifc);
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	platform_device_del(dev->pdev);
+	diag_bridge_debugfs_cleanup();
+	kref_put(&dev->kref, diag_bridge_delete);
+	usb_set_intfdata(ifc, NULL);
+}
+
+static int diag_bridge_suspend(struct usb_interface *ifc, pm_message_t message)
+{
+	struct diag_bridge	*dev = usb_get_intfdata(ifc);
+	struct diag_bridge_ops	*cbs = dev->ops;
+	int ret = 0;
+
+	if (cbs && cbs->suspend) {
+		ret = cbs->suspend(cbs->ctxt);
+		if (ret) {
+			dev_dbg(&dev->udev->dev,
+				"%s: diag veto'd suspend\n", __func__);
+			return ret;
+		}
+
+		usb_kill_anchored_urbs(&dev->submitted);
+	}
+
+	return ret;
+}
+
+static int diag_bridge_resume(struct usb_interface *ifc)
+{
+	struct diag_bridge	*dev = usb_get_intfdata(ifc);
+	struct diag_bridge_ops	*cbs = dev->ops;
+
+
+	if (cbs && cbs->resume)
+		cbs->resume(cbs->ctxt);
+
+	return 0;
+}
+
+#define VALID_INTERFACE_NUM	0
+static const struct usb_device_id diag_bridge_ids[] = {
+	{ USB_DEVICE(0x5c6, 0x9001),
+	.driver_info = VALID_INTERFACE_NUM, },
+	{ USB_DEVICE(0x5c6, 0x9034),
+	.driver_info = VALID_INTERFACE_NUM, },
+	{ USB_DEVICE(0x5c6, 0x9048),
+	.driver_info = VALID_INTERFACE_NUM, },
+	{ USB_DEVICE(0x5c6, 0x904C),
+	.driver_info = VALID_INTERFACE_NUM, },
+
+	{} /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, diag_bridge_ids);
+
+static struct usb_driver diag_bridge_driver = {
+	.name =		"diag_bridge",
+	.probe =	diag_bridge_probe,
+	.disconnect =	diag_bridge_disconnect,
+	.suspend =	diag_bridge_suspend,
+	.resume =	diag_bridge_resume,
+	.id_table =	diag_bridge_ids,
+	.supports_autosuspend = 1,
+};
+
+static int __init diag_bridge_init(void)
+{
+	int ret;
+
+	ret = usb_register(&diag_bridge_driver);
+	if (ret) {
+		err("%s: unable to register diag driver", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit diag_bridge_exit(void)
+{
+	usb_deregister(&diag_bridge_driver);
+}
+
+module_init(diag_bridge_init);
+module_exit(diag_bridge_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/diag_bridge_test.c b/drivers/usb/misc/diag_bridge_test.c
new file mode 100644
index 0000000..5bc0828
--- /dev/null
+++ b/drivers/usb/misc/diag_bridge_test.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/crc-ccitt.h>
+#include <mach/diag_bridge.h>
+
+#define DRIVER_DESC	"USB host diag bridge driver test"
+#define DRIVER_VERSION	"1.0"
+
+#define RD_BUF_SIZE	2048
+#define DIAG_TEST_CONNECTED	0
+
+struct diag_test_dev {
+	char *read_buf;
+	struct work_struct read_w;
+	unsigned long	flags;
+
+	struct diag_bridge_ops	ops;
+};
+static struct diag_test_dev *__dev;
+static struct dentry *dent;
+
+static void
+diag_test_read_complete_cb(void *d, char *buf, size_t size, size_t actual)
+{
+	if (actual < 0) {
+		pr_err("%s: read complete err\n", __func__);
+		return;
+	}
+
+	print_hex_dump(KERN_INFO, "to_host:", 0, 1, 1, buf, actual, false);
+}
+static void diag_test_read_work(struct work_struct *w)
+{
+	struct diag_test_dev *dev =
+		container_of(w, struct diag_test_dev, read_w);
+
+	memset(dev->read_buf, 0, RD_BUF_SIZE);
+	diag_bridge_read(dev->read_buf, RD_BUF_SIZE);
+}
+
+static void
+diag_test_write_complete_cb(void *d, char *buf, size_t size, size_t actual)
+{
+	struct diag_test_dev *dev = d;
+
+	if (actual > 0)
+		schedule_work(&dev->read_w);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t send_ping_cmd(struct file *file, const char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct diag_test_dev	*dev = __dev;
+	unsigned char		*buf;
+	int			temp = sizeof(unsigned char) * 4;
+
+	if (!dev)
+		return -ENODEV;
+
+	buf = kmalloc(temp, GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: unable to allocate mem for ping cmd\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	/* hdlc encoded ping command */
+	buf[0] = 0x0C;
+	buf[1] = 0x14;
+	buf[2] = 0x3A;
+	buf[3] = 0x7E;
+
+	diag_bridge_write(buf, temp);
+
+	return count;
+}
+
+const struct file_operations diag_test_ping_ops = {
+	.write = send_ping_cmd,
+};
+
+static void diag_test_debug_init(void)
+{
+	struct dentry *dfile;
+
+	dent = debugfs_create_dir("diag_test", 0);
+	if (IS_ERR(dent))
+		return;
+
+	dfile = debugfs_create_file("send_ping", 0444, dent,
+			0, &diag_test_ping_ops);
+	if (!dfile || IS_ERR(dfile))
+		debugfs_remove(dent);
+}
+#else
+static void diag_test_debug_init(void) { }
+#endif
+
+static int diag_test_remove(struct platform_device *pdev)
+{
+	diag_bridge_close();
+
+	if (dent) {
+		debugfs_remove_recursive(dent);
+		dent = NULL;
+	}
+
+	return 0;
+}
+
+static int diag_test_probe(struct platform_device *pdev)
+{
+	struct diag_test_dev	*dev = __dev;
+	int			ret = 0;
+
+	pr_info("%s:\n", __func__);
+
+	ret = diag_bridge_open(&dev->ops);
+	if (ret)
+		pr_err("diag open failed: %d", ret);
+
+
+	diag_test_debug_init();
+
+	return ret;
+}
+
+static struct platform_driver diag_test = {
+	.remove = diag_test_remove,
+	.probe	= diag_test_probe,
+	.driver = {
+		.name = "diag_bridge",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init diag_test_init(void)
+{
+	struct diag_test_dev	*dev;
+	int ret = 0;
+
+	pr_info("%s\n", __func__);
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	__dev = dev;
+
+	dev->ops.read_complete_cb = diag_test_read_complete_cb;
+	dev->ops.write_complete_cb = diag_test_write_complete_cb;
+	dev->read_buf = kmalloc(RD_BUF_SIZE, GFP_KERNEL);
+	if (!dev->read_buf) {
+		pr_err("%s: unable to allocate read buffer\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	dev->ops.ctxt = dev;
+	INIT_WORK(&dev->read_w, diag_test_read_work);
+
+	ret = platform_driver_register(&diag_test);
+	if (ret)
+		pr_err("%s: platform driver %s register failed %d\n",
+				__func__, diag_test.driver.name, ret);
+
+	return ret;
+}
+
+static void __exit diag_test_exit(void)
+{
+	struct diag_test_dev *dev = __dev;
+
+	pr_info("%s:\n", __func__);
+
+	if (test_bit(DIAG_TEST_CONNECTED, &dev->flags))
+		diag_bridge_close();
+
+	kfree(dev->read_buf);
+	kfree(dev);
+
+}
+
+module_init(diag_test_init);
+module_exit(diag_test_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c
new file mode 100644
index 0000000..30879e0
--- /dev/null
+++ b/drivers/usb/misc/ehset.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/hcd.h>
+
+#define TEST_SE0_NAK_PID		0x0101
+#define TEST_J_PID			0x0102
+#define TEST_K_PID			0x0103
+#define TEST_PACKET_PID			0x0104
+#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106
+#define TEST_SINGLE_STEP_GET_DEV_DESC	0x0107
+#define TEST_SINGLE_STEP_SET_FEATURE	0x0108
+
+static int ehset_probe(struct usb_interface *intf,
+		       const struct usb_device_id *id)
+{
+	int status = -1;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_device *rh_udev = dev->bus->root_hub;
+	struct usb_device *hub_udev = dev->parent;
+	int port1 = dev->portnum;
+	int test_mode = le16_to_cpu(dev->descriptor.idProduct);
+
+	switch (test_mode) {
+	case TEST_SE0_NAK_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(3 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_J_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(1 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_K_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(2 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_PACKET_PID:
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(4 << 8) | port1, NULL, 0, 1000);
+		break;
+	case TEST_HS_HOST_PORT_SUSPEND_RESUME:
+		/* Test: wait for 15secs -> suspend -> 15secs delay -> resume */
+		msleep(15 * 1000);
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT,
+			USB_PORT_FEAT_SUSPEND, port1, NULL, 0, 1000);
+		if (status < 0)
+			break;
+		msleep(15 * 1000);
+		status = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+			USB_REQ_CLEAR_FEATURE, USB_RT_PORT,
+			USB_PORT_FEAT_SUSPEND, port1, NULL, 0, 1000);
+		break;
+	case TEST_SINGLE_STEP_GET_DEV_DESC:
+		/* Test: wait for 15secs -> GetDescriptor request */
+		msleep(15 * 1000);
+		{
+			struct usb_device_descriptor *buf;
+			buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+			if (!buf)
+				return -ENOMEM;
+
+			status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+				USB_DT_DEVICE << 8, 0,
+				buf, USB_DT_DEVICE_SIZE,
+				USB_CTRL_GET_TIMEOUT);
+			kfree(buf);
+		}
+		break;
+	case TEST_SINGLE_STEP_SET_FEATURE:
+		/* GetDescriptor's SETUP request -> 15secs delay -> IN & STATUS
+		 * Issue request to ehci root hub driver with portnum = 1
+		 */
+		status = usb_control_msg(rh_udev, usb_sndctrlpipe(rh_udev, 0),
+			USB_REQ_SET_FEATURE, USB_RT_PORT, USB_PORT_FEAT_TEST,
+			(6 << 8) | 1, NULL, 0, 60 * 1000);
+
+		break;
+	default:
+		pr_err("%s: undefined test mode ( %X )\n", __func__, test_mode);
+		return -EINVAL;
+	}
+
+	return (status < 0) ? status : 0;
+}
+
+static void ehset_disconnect(struct usb_interface *intf)
+{
+}
+
+static struct usb_device_id ehset_id_table[] = {
+	{ USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_J_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_K_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_PACKET_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) },
+	{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) },
+	{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) },
+	{ }			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ehset_id_table);
+
+static struct usb_driver ehset_driver = {
+	.name =		"usb_ehset_test",
+	.probe =	ehset_probe,
+	.disconnect =	ehset_disconnect,
+	.id_table =	ehset_id_table,
+};
+
+static int __init ehset_init(void)
+{
+	return usb_register(&ehset_driver);
+}
+
+static void __exit ehset_exit(void)
+{
+	usb_deregister(&ehset_driver);
+}
+
+module_init(ehset_init);
+module_exit(ehset_exit);
+
+MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
new file mode 100644
index 0000000..49591cd
--- /dev/null
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -0,0 +1,757 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/termios.h>
+#include <asm/unaligned.h>
+#include <mach/usb_bridge.h>
+
+static const char *ctrl_bridge_names[] = {
+	"dun_ctrl_hsic0",
+	"rmnet_ctrl_hsic0"
+};
+
+/* polling interval for Interrupt ep */
+#define HS_INTERVAL		7
+#define FS_LS_INTERVAL		3
+
+#define ACM_CTRL_DTR		(1 << 0)
+#define DEFAULT_READ_URB_LENGTH	4096
+
+#define SUSPENDED		BIT(0)
+
+struct ctrl_bridge {
+	struct usb_device	*udev;
+	struct usb_interface	*intf;
+
+	unsigned int		int_pipe;
+	struct urb		*inturb;
+	void			*intbuf;
+
+	struct urb		*readurb;
+	void			*readbuf;
+
+	struct usb_anchor	tx_submitted;
+	struct usb_anchor	tx_deferred;
+	struct usb_ctrlrequest	*in_ctlreq;
+
+	struct bridge		*brdg;
+	struct platform_device	*pdev;
+
+	unsigned long		flags;
+
+	/* input control lines (DSR, CTS, CD, RI) */
+	unsigned int		cbits_tohost;
+
+	/* output control lines (DTR, RTS) */
+	unsigned int		cbits_tomdm;
+
+	/* counters */
+	unsigned int		snd_encap_cmd;
+	unsigned int		get_encap_res;
+	unsigned int		resp_avail;
+	unsigned int		set_ctrl_line_sts;
+	unsigned int		notify_ser_state;
+};
+
+static struct ctrl_bridge	*__dev[MAX_BRIDGE_DEVICES];
+
+/* counter used for indexing ctrl bridge devices */
+static int	ch_id;
+
+unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	return dev->cbits_tohost;
+}
+EXPORT_SYMBOL(ctrl_bridge_get_cbits_tohost);
+
+int ctrl_bridge_set_cbits(unsigned int id, unsigned int cbits)
+{
+	struct ctrl_bridge	*dev;
+	struct bridge		*brdg;
+	int			retval;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	pr_debug("%s: dev[id] =%u cbits : %u\n", __func__, id, cbits);
+
+	brdg = dev->brdg;
+	if (!brdg)
+		return -ENODEV;
+
+	dev->cbits_tomdm = cbits;
+
+	retval = ctrl_bridge_write(id, NULL, 0);
+
+	/* if DTR is high, update latest modem info to host */
+	if (brdg && (cbits & ACM_CTRL_DTR) && brdg->ops.send_cbits)
+		brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost);
+
+	return retval;
+}
+EXPORT_SYMBOL(ctrl_bridge_set_cbits);
+
+static void resp_avail_cb(struct urb *urb)
+{
+	struct ctrl_bridge	*dev = urb->context;
+	struct usb_device	*udev;
+	int			status = 0;
+	int			resubmit_urb = 1;
+	struct bridge		*brdg = dev->brdg;
+
+	udev = interface_to_usbdev(dev->intf);
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		dev->get_encap_res++;
+		if (brdg && brdg->ops.send_pkt)
+			brdg->ops.send_pkt(brdg->ctx, urb->transfer_buffer,
+				urb->actual_length);
+		break;
+
+	/*do not resubmit*/
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+		/* unplug */
+	case -EPROTO:
+		/*babble error*/
+		resubmit_urb = 0;
+	/*resubmit*/
+	case -EOVERFLOW:
+	default:
+		dev_dbg(&udev->dev, "%s: non zero urb status = %d\n",
+			__func__, urb->status);
+	}
+
+	if (resubmit_urb) {
+		/*re- submit int urb to check response available*/
+		usb_anchor_urb(dev->inturb, &dev->tx_submitted);
+		status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+		if (status) {
+			dev_err(&udev->dev,
+				"%s: Error re-submitting Int URB %d\n",
+				__func__, status);
+			usb_unanchor_urb(dev->inturb);
+		}
+	}
+}
+
+static void notification_available_cb(struct urb *urb)
+{
+	int				status;
+	struct usb_cdc_notification	*ctrl;
+	struct usb_device		*udev;
+	struct ctrl_bridge		*dev = urb->context;
+	struct bridge			*brdg = dev->brdg;
+	unsigned int			ctrl_bits;
+	unsigned char			*data;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		break;
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		 /* unplug */
+		 return;
+	case -EPIPE:
+		dev_err(&udev->dev, "%s: stall on int endpoint\n", __func__);
+		/* TBD : halt to be cleared in work */
+	case -EOVERFLOW:
+	default:
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+					__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	ctrl = (struct usb_cdc_notification *)urb->transfer_buffer;
+	data = (unsigned char *)(ctrl + 1);
+
+	switch (ctrl->bNotificationType) {
+	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+		dev->resp_avail++;
+		usb_fill_control_urb(dev->readurb, udev,
+					usb_rcvctrlpipe(udev, 0),
+					(unsigned char *)dev->in_ctlreq,
+					dev->readbuf,
+					DEFAULT_READ_URB_LENGTH,
+					resp_avail_cb, dev);
+
+		usb_anchor_urb(dev->readurb, &dev->tx_submitted);
+		status = usb_submit_urb(dev->readurb, GFP_ATOMIC);
+		if (status) {
+			dev_err(&udev->dev,
+				"%s: Error submitting Read URB %d\n",
+				__func__, status);
+			usb_unanchor_urb(dev->readurb);
+			goto resubmit_int_urb;
+		}
+		return;
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		dev_dbg(&udev->dev, "%s network\n", ctrl->wValue ?
+					"connected to" : "disconnected from");
+		break;
+	case USB_CDC_NOTIFY_SERIAL_STATE:
+		dev->notify_ser_state++;
+		ctrl_bits = get_unaligned_le16(data);
+		dev_dbg(&udev->dev, "serial state: %d\n", ctrl_bits);
+		dev->cbits_tohost = ctrl_bits;
+		if (brdg && brdg->ops.send_cbits)
+			brdg->ops.send_cbits(brdg->ctx, ctrl_bits);
+		break;
+	default:
+		dev_err(&udev->dev, "%s: unknown notification %d received:"
+			"index %d len %d data0 %d data1 %d",
+			__func__, ctrl->bNotificationType, ctrl->wIndex,
+			ctrl->wLength, data[0], data[1]);
+	}
+
+resubmit_int_urb:
+	usb_anchor_urb(urb, &dev->tx_submitted);
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err(&udev->dev, "%s: Error re-submitting Int URB %d\n",
+		__func__, status);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static int ctrl_bridge_start_read(struct ctrl_bridge *dev)
+{
+	int	retval = 0;
+
+	if (!dev->inturb) {
+		dev_err(&dev->udev->dev, "%s: inturb is NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!dev->inturb->anchor) {
+		usb_anchor_urb(dev->inturb, &dev->tx_submitted);
+		retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
+		if (retval < 0) {
+			dev_err(&dev->udev->dev,
+				"%s error submitting int urb %d\n",
+				__func__, retval);
+			usb_unanchor_urb(dev->inturb);
+		}
+	}
+
+	return retval;
+}
+
+int ctrl_bridge_open(struct bridge *brdg)
+{
+	struct ctrl_bridge	*dev;
+
+	if (!brdg) {
+		err("bridge is null\n");
+		return -EINVAL;
+	}
+
+	if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[brdg->ch_id];
+	if (!dev) {
+		err("dev is null\n");
+		return -ENODEV;
+	}
+
+	dev->brdg = brdg;
+	dev->snd_encap_cmd = 0;
+	dev->get_encap_res = 0;
+	dev->resp_avail = 0;
+	dev->set_ctrl_line_sts = 0;
+	dev->notify_ser_state = 0;
+
+	if (brdg->ops.send_cbits)
+		brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost);
+
+	return 0;
+}
+EXPORT_SYMBOL(ctrl_bridge_open);
+
+void ctrl_bridge_close(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return;
+
+	dev  = __dev[id];
+	if (!dev || !dev->brdg)
+		return;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	ctrl_bridge_set_cbits(dev->brdg->ch_id, 0);
+	usb_unlink_anchored_urbs(&dev->tx_submitted);
+
+	dev->brdg = NULL;
+}
+EXPORT_SYMBOL(ctrl_bridge_close);
+
+static void ctrl_write_callback(struct urb *urb)
+{
+	struct ctrl_bridge	*dev = urb->context;
+
+	if (urb->status) {
+		pr_debug("Write status/size %d/%d\n",
+			urb->status, urb->actual_length);
+	}
+
+	kfree(urb->transfer_buffer);
+	kfree(urb->setup_packet);
+	usb_free_urb(urb);
+	usb_autopm_put_interface_async(dev->intf);
+}
+
+int ctrl_bridge_write(unsigned int id, char *data, size_t size)
+{
+	int			result;
+	struct urb		*writeurb;
+	struct usb_ctrlrequest	*out_ctlreq;
+	struct usb_device	*udev;
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES) {
+		result = -EINVAL;
+		goto free_data;
+	}
+
+	dev = __dev[id];
+
+	if (!dev) {
+		result = -ENODEV;
+		goto free_data;
+	}
+
+	udev = interface_to_usbdev(dev->intf);
+
+	dev_dbg(&udev->dev, "%s:[id]:%u: write (%d bytes)\n",
+		__func__, id, size);
+
+	writeurb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!writeurb) {
+		dev_err(&udev->dev, "%s: error allocating read urb\n",
+			__func__);
+		result = -ENOMEM;
+		goto free_data;
+	}
+
+	out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_ATOMIC);
+	if (!out_ctlreq) {
+		dev_err(&udev->dev,
+			"%s: error allocating setup packet buffer\n",
+			__func__);
+		result = -ENOMEM;
+		goto free_urb;
+	}
+
+	/* CDC Send Encapsulated Request packet */
+	out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE);
+	if (!data && !size) {
+		out_ctlreq->bRequest = USB_CDC_REQ_SET_CONTROL_LINE_STATE;
+		out_ctlreq->wValue = dev->cbits_tomdm;
+		dev->set_ctrl_line_sts++;
+	} else {
+		out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+		out_ctlreq->wValue = 0;
+		dev->snd_encap_cmd++;
+	}
+	out_ctlreq->wIndex =
+		dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	out_ctlreq->wLength = cpu_to_le16(size);
+
+	usb_fill_control_urb(writeurb, udev,
+				 usb_sndctrlpipe(udev, 0),
+				 (unsigned char *)out_ctlreq,
+				 (void *)data, size,
+				 ctrl_write_callback, dev);
+
+	result = usb_autopm_get_interface_async(dev->intf);
+	if (result < 0) {
+		dev_err(&udev->dev, "%s: unable to resume interface: %d\n",
+			__func__, result);
+
+		/*
+		  * Revisit: if (result == -EPERM)
+		  * bridge_suspend(dev->intf, PMSG_SUSPEND);
+		  */
+
+		goto free_ctrlreq;
+	}
+
+	if (test_bit(SUSPENDED, &dev->flags)) {
+		usb_anchor_urb(writeurb, &dev->tx_deferred);
+		goto deferred;
+	}
+
+	usb_anchor_urb(writeurb, &dev->tx_submitted);
+	result = usb_submit_urb(writeurb, GFP_ATOMIC);
+	if (result < 0) {
+		dev_err(&udev->dev, "%s: submit URB error %d\n",
+			__func__, result);
+		usb_autopm_put_interface_async(dev->intf);
+		goto unanchor_urb;
+	}
+deferred:
+	return size;
+
+unanchor_urb:
+	usb_unanchor_urb(writeurb);
+free_ctrlreq:
+	kfree(out_ctlreq);
+free_urb:
+	usb_free_urb(writeurb);
+free_data:
+	kfree(data);
+
+	return result;
+}
+EXPORT_SYMBOL(ctrl_bridge_write);
+
+int ctrl_bridge_suspend(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	set_bit(SUSPENDED, &dev->flags);
+	usb_kill_anchored_urbs(&dev->tx_submitted);
+
+	return 0;
+}
+
+int ctrl_bridge_resume(unsigned int id)
+{
+	struct ctrl_bridge	*dev;
+	struct urb		*urb;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev)
+		return -ENODEV;
+
+	if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+		return 0;
+
+	/* submit pending write requests */
+	while ((urb = usb_get_from_anchor(&dev->tx_deferred))) {
+		int ret;
+		usb_anchor_urb(urb, &dev->tx_submitted);
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret < 0) {
+			usb_unanchor_urb(urb);
+			kfree(urb->setup_packet);
+			kfree(urb->transfer_buffer);
+			usb_free_urb(urb);
+			usb_autopm_put_interface_async(dev->intf);
+		}
+	}
+
+	return ctrl_bridge_start_read(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+static ssize_t ctrl_bridge_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct ctrl_bridge	*dev;
+	char			*buf;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName#%s dev %p\n"
+				"snd encap cmd cnt: %u\n"
+				"get encap res cnt: %u\n"
+				"res available cnt: %u\n"
+				"set ctrlline sts cnt: %u\n"
+				"notify ser state cnt: %u\n"
+				"cbits_tomdm: %d\n"
+				"cbits_tohost: %d\n"
+				"suspended: %d\n",
+				dev->pdev->name, dev,
+				dev->snd_encap_cmd,
+				dev->get_encap_res,
+				dev->resp_avail,
+				dev->set_ctrl_line_sts,
+				dev->notify_ser_state,
+				dev->cbits_tomdm,
+				dev->cbits_tohost,
+				test_bit(SUSPENDED, &dev->flags));
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ctrl_bridge_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ctrl_bridge	*dev;
+	int			i;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		dev->snd_encap_cmd = 0;
+		dev->get_encap_res = 0;
+		dev->resp_avail = 0;
+		dev->set_ctrl_line_sts = 0;
+		dev->notify_ser_state = 0;
+	}
+	return count;
+}
+
+const struct file_operations ctrl_stats_ops = {
+	.read = ctrl_bridge_read_stats,
+	.write = ctrl_bridge_reset_stats,
+};
+
+struct dentry	*ctrl_dent;
+struct dentry	*ctrl_dfile;
+static void ctrl_bridge_debugfs_init(void)
+{
+	ctrl_dent = debugfs_create_dir("ctrl_hsic_bridge", 0);
+	if (IS_ERR(ctrl_dent))
+		return;
+
+	ctrl_dfile =
+		debugfs_create_file("status", 0644, ctrl_dent, 0,
+			&ctrl_stats_ops);
+	if (!ctrl_dfile || IS_ERR(ctrl_dfile))
+		debugfs_remove(ctrl_dent);
+}
+
+static void ctrl_bridge_debugfs_exit(void)
+{
+	debugfs_remove(ctrl_dfile);
+	debugfs_remove(ctrl_dent);
+}
+
+#else
+static void ctrl_bridge_debugfs_init(void) { }
+static void ctrl_bridge_debugfs_exit(void) { }
+#endif
+
+int
+ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in,
+		int id)
+{
+	struct ctrl_bridge		*dev;
+	struct usb_device		*udev;
+	struct usb_endpoint_descriptor	*ep;
+	u16				wMaxPacketSize;
+	int				retval = 0;
+	int				interval;
+
+	udev = interface_to_usbdev(ifc);
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		dev_err(&udev->dev, "%s: unable to allocate dev\n",
+			__func__);
+		return -ENOMEM;
+	}
+	dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id);
+	if (!dev->pdev) {
+		dev_err(&dev->udev->dev,
+			"%s: unable to allocate platform device\n", __func__);
+		retval = -ENOMEM;
+		goto nomem;
+	}
+
+	dev->udev = udev;
+	dev->int_pipe = usb_rcvintpipe(udev,
+		int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->intf = ifc;
+
+	init_usb_anchor(&dev->tx_submitted);
+	init_usb_anchor(&dev->tx_deferred);
+
+	/*use max pkt size from ep desc*/
+	ep = &dev->intf->cur_altsetting->endpoint[0].desc;
+
+	dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->inturb) {
+		dev_err(&udev->dev, "%s: error allocating int urb\n", __func__);
+		retval = -ENOMEM;
+		goto pdev_del;
+	}
+
+	wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+
+	dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL);
+	if (!dev->intbuf) {
+		dev_err(&udev->dev, "%s: error allocating int buffer\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_inturb;
+	}
+
+	interval =
+		(udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL : FS_LS_INTERVAL;
+
+	usb_fill_int_urb(dev->inturb, udev, dev->int_pipe,
+				dev->intbuf, wMaxPacketSize,
+				notification_available_cb, dev, interval);
+
+	dev->readurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->readurb) {
+		dev_err(&udev->dev, "%s: error allocating read urb\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_intbuf;
+	}
+
+	dev->readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+	if (!dev->readbuf) {
+		dev_err(&udev->dev, "%s: error allocating read buffer\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_rurb;
+	}
+
+	dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL);
+	if (!dev->in_ctlreq) {
+		dev_err(&udev->dev,
+			"%s:error allocating setup packet buffer\n",
+			__func__);
+		retval = -ENOMEM;
+		goto free_rbuf;
+	}
+
+	dev->in_ctlreq->bRequestType =
+			(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+	dev->in_ctlreq->bRequest  = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+	dev->in_ctlreq->wValue = 0;
+	dev->in_ctlreq->wIndex =
+		dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
+
+	__dev[id] = dev;
+
+	platform_device_add(dev->pdev);
+
+	ch_id++;
+
+	return ctrl_bridge_start_read(dev);
+
+free_rbuf:
+	kfree(dev->readbuf);
+free_rurb:
+	usb_free_urb(dev->readurb);
+free_intbuf:
+	kfree(dev->intbuf);
+free_inturb:
+	usb_free_urb(dev->inturb);
+pdev_del:
+	platform_device_del(dev->pdev);
+nomem:
+	kfree(dev);
+
+	return retval;
+}
+
+void ctrl_bridge_disconnect(unsigned int id)
+{
+	struct ctrl_bridge	*dev = __dev[id];
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	platform_device_del(dev->pdev);
+
+	kfree(dev->in_ctlreq);
+	kfree(dev->readbuf);
+	kfree(dev->intbuf);
+
+	usb_free_urb(dev->readurb);
+	usb_free_urb(dev->inturb);
+
+	__dev[id] = NULL;
+	ch_id--;
+
+	kfree(dev);
+}
+
+static int __init ctrl_bridge_init(void)
+{
+	ctrl_bridge_debugfs_init();
+
+	return 0;
+}
+module_init(ctrl_bridge_init);
+
+static void __exit ctrl_bridge_exit(void)
+{
+	ctrl_bridge_debugfs_exit();
+}
+module_exit(ctrl_bridge_exit);
+
+MODULE_DESCRIPTION("Qualcomm modem control bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
new file mode 100644
index 0000000..bf8e5f4
--- /dev/null
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -0,0 +1,1080 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+#include <mach/usb_bridge.h>
+
+#define MAX_RX_URBS			50
+#define RMNET_RX_BUFSIZE		2048
+
+#define STOP_SUBMIT_URB_LIMIT		500
+#define FLOW_CTRL_EN_THRESHOLD		500
+#define FLOW_CTRL_DISABLE		300
+#define FLOW_CTRL_SUPPORT		1
+
+static const char	*data_bridge_names[] = {
+	"dun_data_hsic0",
+	"rmnet_data_hsic0"
+};
+
+static struct workqueue_struct	*bridge_wq;
+
+static unsigned int	fctrl_support = FLOW_CTRL_SUPPORT;
+module_param(fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int	fctrl_en_thld = FLOW_CTRL_EN_THRESHOLD;
+module_param(fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int	fctrl_dis_thld = FLOW_CTRL_DISABLE;
+module_param(fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int	max_rx_urbs = MAX_RX_URBS;
+module_param(max_rx_urbs, uint, S_IRUGO | S_IWUSR);
+
+unsigned int	stop_submit_urb_limit = STOP_SUBMIT_URB_LIMIT;
+module_param(stop_submit_urb_limit, uint, S_IRUGO | S_IWUSR);
+
+static unsigned tx_urb_mult = 20;
+module_param(tx_urb_mult, uint, S_IRUGO|S_IWUSR);
+
+#define TX_HALT   BIT(0)
+#define RX_HALT   BIT(1)
+#define SUSPENDED BIT(2)
+
+struct data_bridge {
+	struct usb_interface		*intf;
+	struct usb_device		*udev;
+	int				id;
+
+	unsigned int			bulk_in;
+	unsigned int			bulk_out;
+	int				err;
+
+	/* keep track of in-flight URBs */
+	struct usb_anchor		tx_active;
+	struct usb_anchor		rx_active;
+
+	/* keep track of outgoing URBs during suspend */
+	struct usb_anchor		delayed;
+
+	struct list_head		rx_idle;
+	struct sk_buff_head		rx_done;
+
+	struct workqueue_struct		*wq;
+	struct work_struct		process_rx_w;
+
+	struct bridge			*brdg;
+
+	/* work queue function for handling halt conditions */
+	struct work_struct		kevent;
+
+	unsigned long			flags;
+
+	struct platform_device		*pdev;
+
+	/* counters */
+	atomic_t			pending_txurbs;
+	unsigned int			txurb_drp_cnt;
+	unsigned long			to_host;
+	unsigned long			to_modem;
+	unsigned int			tx_throttled_cnt;
+	unsigned int			tx_unthrottled_cnt;
+	unsigned int			rx_throttled_cnt;
+	unsigned int			rx_unthrottled_cnt;
+};
+
+static struct data_bridge	*__dev[MAX_BRIDGE_DEVICES];
+
+/* counter used for indexing data bridge devices */
+static int	ch_id;
+
+static unsigned int get_timestamp(void);
+static void dbg_timestamp(char *, struct sk_buff *);
+static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
+		gfp_t flags);
+
+static inline  bool rx_halted(struct data_bridge *dev)
+{
+	return test_bit(RX_HALT, &dev->flags);
+}
+
+static inline bool rx_throttled(struct bridge *brdg)
+{
+	return test_bit(RX_THROTTLED, &brdg->flags);
+}
+
+int data_bridge_unthrottle_rx(unsigned int id)
+{
+	struct data_bridge	*dev;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[id];
+	if (!dev || !dev->brdg)
+		return -ENODEV;
+
+	dev->rx_unthrottled_cnt++;
+	queue_work(dev->wq, &dev->process_rx_w);
+
+	return 0;
+}
+EXPORT_SYMBOL(data_bridge_unthrottle_rx);
+
+static void data_bridge_process_rx(struct work_struct *work)
+{
+	int			retval;
+	unsigned long		flags;
+	struct urb		*rx_idle;
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	struct data_bridge	*dev =
+		container_of(work, struct data_bridge, process_rx_w);
+
+	struct bridge		*brdg = dev->brdg;
+
+	if (!brdg || !brdg->ops.send_pkt || rx_halted(dev))
+		return;
+
+	while (!rx_throttled(brdg) && (skb = skb_dequeue(&dev->rx_done))) {
+		dev->to_host++;
+		info = (struct timestamp_info *)skb->cb;
+		info->rx_done_sent = get_timestamp();
+		/* hand off sk_buff to client,they'll need to free it */
+		retval = brdg->ops.send_pkt(brdg->ctx, skb, skb->len);
+		if (retval == -ENOTCONN || retval == -EINVAL) {
+			return;
+		} else if (retval == -EBUSY) {
+			dev->rx_throttled_cnt++;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&dev->rx_done.lock, flags);
+	while (!list_empty(&dev->rx_idle)) {
+		if (dev->rx_done.qlen > stop_submit_urb_limit)
+			break;
+
+		rx_idle = list_first_entry(&dev->rx_idle, struct urb, urb_list);
+		list_del(&rx_idle->urb_list);
+		spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+		retval = submit_rx_urb(dev, rx_idle, GFP_KERNEL);
+		spin_lock_irqsave(&dev->rx_done.lock, flags);
+		if (retval) {
+			list_add_tail(&rx_idle->urb_list, &dev->rx_idle);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+}
+
+static void data_bridge_read_cb(struct urb *urb)
+{
+	struct bridge		*brdg;
+	struct sk_buff		*skb = urb->context;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	struct data_bridge	*dev = info->dev;
+	bool			queue = 0;
+
+	brdg = dev->brdg;
+	skb_put(skb, urb->actual_length);
+
+	switch (urb->status) {
+	case 0: /* success */
+		queue = 1;
+		info->rx_done = get_timestamp();
+		spin_lock(&dev->rx_done.lock);
+		__skb_queue_tail(&dev->rx_done, skb);
+		spin_unlock(&dev->rx_done.lock);
+		break;
+
+	/*do not resubmit*/
+	case -EPIPE:
+		set_bit(RX_HALT, &dev->flags);
+		dev_err(&dev->udev->dev, "%s: epout halted\n", __func__);
+		schedule_work(&dev->kevent);
+		/* FALLTHROUGH */
+	case -ESHUTDOWN:
+	case -ENOENT: /* suspended */
+	case -ECONNRESET: /* unplug */
+	case -EPROTO:
+		dev_kfree_skb_any(skb);
+		break;
+
+	/*resubmit */
+	case -EOVERFLOW: /*babble error*/
+	default:
+		queue = 1;
+		dev_kfree_skb_any(skb);
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+			__func__, urb->status);
+		break;
+	}
+
+	spin_lock(&dev->rx_done.lock);
+	list_add_tail(&urb->urb_list, &dev->rx_idle);
+	spin_unlock(&dev->rx_done.lock);
+
+	if (queue)
+		queue_work(dev->wq, &dev->process_rx_w);
+}
+
+static int submit_rx_urb(struct data_bridge *dev, struct urb *rx_urb,
+	gfp_t flags)
+{
+	struct sk_buff		*skb;
+	struct timestamp_info	*info;
+	int			retval = -EINVAL;
+	unsigned int		created;
+
+	created = get_timestamp();
+	skb = alloc_skb(RMNET_RX_BUFSIZE, flags);
+	if (!skb)
+		return -ENOMEM;
+
+	info = (struct timestamp_info *)skb->cb;
+	info->dev = dev;
+	info->created = created;
+
+	usb_fill_bulk_urb(rx_urb, dev->udev, dev->bulk_in,
+			  skb->data, RMNET_RX_BUFSIZE,
+			  data_bridge_read_cb, skb);
+
+	if (test_bit(SUSPENDED, &dev->flags))
+		goto suspended;
+
+	usb_anchor_urb(rx_urb, &dev->rx_active);
+	info->rx_queued = get_timestamp();
+	retval = usb_submit_urb(rx_urb, flags);
+	if (retval)
+		goto fail;
+
+	usb_mark_last_busy(dev->udev);
+	return 0;
+fail:
+	usb_unanchor_urb(rx_urb);
+suspended:
+	dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+static int data_bridge_prepare_rx(struct data_bridge *dev)
+{
+	int		i;
+	struct urb	*rx_urb;
+
+	for (i = 0; i < max_rx_urbs; i++) {
+		rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!rx_urb)
+			return -ENOMEM;
+
+		list_add_tail(&rx_urb->urb_list, &dev->rx_idle);
+	}
+	 return 0;
+}
+
+int data_bridge_open(struct bridge *brdg)
+{
+	struct data_bridge	*dev;
+
+	if (!brdg) {
+		err("bridge is null\n");
+		return -EINVAL;
+	}
+
+	if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
+		return -EINVAL;
+
+	dev = __dev[brdg->ch_id];
+	if (!dev) {
+		err("dev is null\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(&dev->udev->dev, "%s: dev:%p\n", __func__, dev);
+
+	dev->brdg = brdg;
+	dev->err = 0;
+	atomic_set(&dev->pending_txurbs, 0);
+	dev->to_host = 0;
+	dev->to_modem = 0;
+	dev->txurb_drp_cnt = 0;
+	dev->tx_throttled_cnt = 0;
+	dev->tx_unthrottled_cnt = 0;
+	dev->rx_throttled_cnt = 0;
+	dev->rx_unthrottled_cnt = 0;
+
+	queue_work(dev->wq, &dev->process_rx_w);
+
+	return 0;
+}
+EXPORT_SYMBOL(data_bridge_open);
+
+void data_bridge_close(unsigned int id)
+{
+	struct data_bridge	*dev;
+	struct sk_buff		*skb;
+	unsigned long		flags;
+
+	if (id >= MAX_BRIDGE_DEVICES)
+		return;
+
+	dev  = __dev[id];
+	if (!dev || !dev->brdg)
+		return;
+
+	dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+	usb_unlink_anchored_urbs(&dev->tx_active);
+	usb_unlink_anchored_urbs(&dev->rx_active);
+	usb_unlink_anchored_urbs(&dev->delayed);
+
+	spin_lock_irqsave(&dev->rx_done.lock, flags);
+	while ((skb = __skb_dequeue(&dev->rx_done)))
+		dev_kfree_skb_any(skb);
+	spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+
+	dev->brdg = NULL;
+}
+EXPORT_SYMBOL(data_bridge_close);
+
+static void defer_kevent(struct work_struct *work)
+{
+	int			status;
+	struct data_bridge	*dev =
+		container_of(work, struct data_bridge, kevent);
+
+	if (!dev)
+		return;
+
+	if (test_bit(TX_HALT, &dev->flags)) {
+		usb_unlink_anchored_urbs(&dev->tx_active);
+
+		status = usb_autopm_get_interface(dev->intf);
+		if (status < 0) {
+			dev_err(&dev->udev->dev,
+				"can't acquire interface, status %d\n", status);
+			return;
+		}
+
+		status = usb_clear_halt(dev->udev, dev->bulk_out);
+		usb_autopm_put_interface(dev->intf);
+		if (status < 0 && status != -EPIPE && status != -ESHUTDOWN)
+			dev_err(&dev->udev->dev,
+				"can't clear tx halt, status %d\n", status);
+		else
+			clear_bit(TX_HALT, &dev->flags);
+	}
+
+	if (test_bit(RX_HALT, &dev->flags)) {
+		usb_unlink_anchored_urbs(&dev->rx_active);
+
+		status = usb_autopm_get_interface(dev->intf);
+		if (status < 0) {
+			dev_err(&dev->udev->dev,
+				"can't acquire interface, status %d\n", status);
+			return;
+		}
+
+		status = usb_clear_halt(dev->udev, dev->bulk_in);
+		usb_autopm_put_interface(dev->intf);
+		if (status < 0 && status != -EPIPE && status != -ESHUTDOWN)
+			dev_err(&dev->udev->dev,
+				"can't clear rx halt, status %d\n", status);
+		else {
+			clear_bit(RX_HALT, &dev->flags);
+			if (dev->brdg)
+				queue_work(dev->wq, &dev->process_rx_w);
+		}
+	}
+}
+
+static void data_bridge_write_cb(struct urb *urb)
+{
+	struct sk_buff		*skb = urb->context;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	struct data_bridge	*dev = info->dev;
+	struct bridge		*brdg = dev->brdg;
+	int			pending;
+
+	pr_debug("%s: dev:%p\n", __func__, dev);
+
+	switch (urb->status) {
+	case 0: /*success*/
+		dbg_timestamp("UL", skb);
+		break;
+	case -EPROTO:
+		dev->err = -EPROTO;
+		break;
+	case -EPIPE:
+		set_bit(TX_HALT, &dev->flags);
+		dev_err(&dev->udev->dev, "%s: epout halted\n", __func__);
+		schedule_work(&dev->kevent);
+		/* FALLTHROUGH */
+	case -ESHUTDOWN:
+	case -ENOENT: /* suspended */
+	case -ECONNRESET: /* unplug */
+	case -EOVERFLOW: /*babble error*/
+		/* FALLTHROUGH */
+	default:
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+					__func__, urb->status);
+	}
+
+	usb_free_urb(urb);
+	dev_kfree_skb_any(skb);
+
+	pending = atomic_dec_return(&dev->pending_txurbs);
+
+	/*flow ctrl*/
+	if (brdg && fctrl_support && pending <= fctrl_dis_thld &&
+		test_and_clear_bit(TX_THROTTLED, &brdg->flags)) {
+		pr_debug_ratelimited("%s: disable flow ctrl: pend urbs:%u\n",
+			__func__, pending);
+		dev->tx_unthrottled_cnt++;
+		if (brdg->ops.unthrottle_tx)
+			brdg->ops.unthrottle_tx(brdg->ctx);
+	}
+
+	usb_autopm_put_interface_async(dev->intf);
+}
+
+int data_bridge_write(unsigned int id, struct sk_buff *skb)
+{
+	int			result;
+	int			size = skb->len;
+	int			pending;
+	struct urb		*txurb;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+	struct data_bridge	*dev = __dev[id];
+	struct bridge		*brdg;
+
+	if (!dev || !dev->brdg || dev->err || !usb_get_intfdata(dev->intf))
+		return -ENODEV;
+
+	brdg = dev->brdg;
+	if (!brdg)
+		return -ENODEV;
+
+	dev_dbg(&dev->udev->dev, "%s: write (%d bytes)\n", __func__, skb->len);
+
+	result = usb_autopm_get_interface(dev->intf);
+	if (result < 0) {
+		dev_err(&dev->udev->dev, "%s: resume failure\n", __func__);
+		goto error;
+	}
+
+	txurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!txurb) {
+		dev_err(&dev->udev->dev, "%s: error allocating read urb\n",
+			__func__);
+		result = -ENOMEM;
+		goto error;
+	}
+
+	/* store dev pointer in skb */
+	info->dev = dev;
+	info->tx_queued = get_timestamp();
+
+	usb_fill_bulk_urb(txurb, dev->udev, dev->bulk_out,
+			skb->data, skb->len, data_bridge_write_cb, skb);
+
+	if (test_bit(SUSPENDED, &dev->flags)) {
+		usb_anchor_urb(txurb, &dev->delayed);
+		goto free_urb;
+	}
+
+	pending = atomic_inc_return(&dev->pending_txurbs);
+	usb_anchor_urb(txurb, &dev->tx_active);
+
+	if (atomic_read(&dev->pending_txurbs) % tx_urb_mult)
+		txurb->transfer_flags |= URB_NO_INTERRUPT;
+
+	result = usb_submit_urb(txurb, GFP_KERNEL);
+	if (result < 0) {
+		usb_unanchor_urb(txurb);
+		atomic_dec(&dev->pending_txurbs);
+		dev_err(&dev->udev->dev, "%s: submit URB error %d\n",
+			__func__, result);
+		goto free_urb;
+	}
+
+	dev->to_modem++;
+	dev_dbg(&dev->udev->dev, "%s: pending_txurbs: %u\n", __func__, pending);
+
+	/* flow control: last urb submitted but return -EBUSY */
+	if (fctrl_support && pending > fctrl_en_thld) {
+		set_bit(TX_THROTTLED, &brdg->flags);
+		dev->tx_throttled_cnt++;
+		pr_debug_ratelimited("%s: enable flow ctrl pend txurbs:%u\n",
+					__func__, pending);
+		return -EBUSY;
+	}
+
+	return size;
+
+free_urb:
+	usb_free_urb(txurb);
+error:
+	dev->txurb_drp_cnt++;
+	usb_autopm_put_interface(dev->intf);
+
+	return result;
+}
+EXPORT_SYMBOL(data_bridge_write);
+
+static int data_bridge_resume(struct data_bridge *dev)
+{
+	struct urb	*urb;
+	int		retval;
+
+	if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+		return 0;
+
+	while ((urb = usb_get_from_anchor(&dev->delayed))) {
+		usb_anchor_urb(urb, &dev->tx_active);
+		atomic_inc(&dev->pending_txurbs);
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
+		if (retval < 0) {
+			atomic_dec(&dev->pending_txurbs);
+			usb_unanchor_urb(urb);
+
+			/* TODO: need to free urb data */
+			usb_scuttle_anchored_urbs(&dev->delayed);
+			break;
+		}
+		dev->to_modem++;
+		dev->txurb_drp_cnt--;
+	}
+
+	if (dev->brdg)
+		queue_work(dev->wq, &dev->process_rx_w);
+
+	return 0;
+}
+
+static int bridge_resume(struct usb_interface *iface)
+{
+	int			retval = 0;
+	int			oldstate;
+	struct data_bridge	*dev = usb_get_intfdata(iface);
+
+	oldstate = iface->dev.power.power_state.event;
+	iface->dev.power.power_state.event = PM_EVENT_ON;
+
+	if (oldstate & PM_EVENT_SUSPEND) {
+		retval = data_bridge_resume(dev);
+		if (!retval)
+			retval = ctrl_bridge_resume(dev->id);
+	}
+
+	return retval;
+}
+
+static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message)
+{
+	if (atomic_read(&dev->pending_txurbs) &&
+		(message.event & PM_EVENT_AUTO))
+		return -EBUSY;
+
+	set_bit(SUSPENDED, &dev->flags);
+
+	usb_kill_anchored_urbs(&dev->tx_active);
+	usb_kill_anchored_urbs(&dev->rx_active);
+
+	return 0;
+}
+
+static int bridge_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	int			retval;
+	struct data_bridge	*dev = usb_get_intfdata(intf);
+
+	retval = data_bridge_suspend(dev, message);
+	if (!retval) {
+		retval = ctrl_bridge_suspend(dev->id);
+		intf->dev.power.power_state.event = message.event;
+	}
+
+	return retval;
+}
+
+static int data_bridge_probe(struct usb_interface *iface,
+		struct usb_host_endpoint *bulk_in,
+		struct usb_host_endpoint *bulk_out, int id)
+{
+	struct data_bridge	*dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		err("%s: unable to allocate dev\n", __func__);
+		return -ENOMEM;
+	}
+
+	dev->pdev = platform_device_alloc(data_bridge_names[id], id);
+	if (!dev->pdev) {
+		err("%s: unable to allocate platform device\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	init_usb_anchor(&dev->tx_active);
+	init_usb_anchor(&dev->rx_active);
+	init_usb_anchor(&dev->delayed);
+
+	INIT_LIST_HEAD(&dev->rx_idle);
+	skb_queue_head_init(&dev->rx_done);
+
+	dev->wq = bridge_wq;
+	dev->id = id;
+	dev->udev = interface_to_usbdev(iface);
+	dev->intf = iface;
+
+	dev->bulk_in = usb_rcvbulkpipe(dev->udev,
+		bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	dev->bulk_out = usb_sndbulkpipe(dev->udev,
+		bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	usb_set_intfdata(iface, dev);
+
+	INIT_WORK(&dev->kevent, defer_kevent);
+	INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
+
+	__dev[id] = dev;
+
+	/*allocate list of rx urbs*/
+	data_bridge_prepare_rx(dev);
+
+	platform_device_add(dev->pdev);
+
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+
+static unsigned int	record_timestamp;
+module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
+
+static struct timestamp_buf dbg_data = {
+	.idx = 0,
+	.lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+/*get_timestamp - returns time of day in us */
+static unsigned int get_timestamp(void)
+{
+	struct timeval	tval;
+	unsigned int	stamp;
+
+	if (!record_timestamp)
+		return 0;
+
+	do_gettimeofday(&tval);
+	/* 2^32 = 4294967296. Limit to 4096s. */
+	stamp = tval.tv_sec & 0xFFF;
+	stamp = stamp * 1000000 + tval.tv_usec;
+	return stamp;
+}
+
+static void dbg_inc(unsigned *idx)
+{
+	*idx = (*idx + 1) & (DBG_DATA_MAX-1);
+}
+
+/**
+* dbg_timestamp - Stores timestamp values of a SKB life cycle
+*	to debug buffer
+* @event: "UL": Uplink Data
+* @skb: SKB used to store timestamp values to debug buffer
+*/
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	unsigned long		flags;
+	struct timestamp_info	*info = (struct timestamp_info *)skb->cb;
+
+	if (!record_timestamp)
+		return;
+
+	write_lock_irqsave(&dbg_data.lck, flags);
+
+	scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
+		  "%p %u[%s] %u %u %u %u %u %u\n",
+		  skb, skb->len, event, info->created, info->rx_queued,
+		  info->rx_done, info->rx_done_sent, info->tx_queued,
+		  get_timestamp());
+
+	dbg_inc(&dbg_data.idx);
+
+	write_unlock_irqrestore(&dbg_data.lck, flags);
+}
+
+/* show_timestamp: displays the timestamp buffer */
+static ssize_t show_timestamp(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	unsigned long	flags;
+	unsigned	i;
+	unsigned	j = 0;
+	char		*buf;
+	int		ret = 0;
+
+	if (!record_timestamp)
+		return 0;
+
+	buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	read_lock_irqsave(&dbg_data.lck, flags);
+
+	i = dbg_data.idx;
+	for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
+		if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
+			continue;
+		j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+			       "%s\n", dbg_data.buf[i]);
+	}
+
+	read_unlock_irqrestore(&dbg_data.lck, flags);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, j);
+
+	kfree(buf);
+
+	return ret;
+}
+
+const struct file_operations data_timestamp_ops = {
+	.read = show_timestamp,
+};
+
+static ssize_t data_bridge_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	struct data_bridge	*dev;
+	char			*buf;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\nName#%s dev %p\n"
+				"pending tx urbs:    %u\n"
+				"tx urb drp cnt:     %u\n"
+				"to host:            %lu\n"
+				"to mdm:             %lu\n"
+				"tx throttled cnt:   %u\n"
+				"tx unthrottled cnt: %u\n"
+				"rx throttled cnt:   %u\n"
+				"rx unthrottled cnt: %u\n"
+				"rx done skb qlen:   %u\n"
+				"dev err:            %d\n"
+				"suspended:          %d\n"
+				"TX_HALT:            %d\n"
+				"RX_HALT:            %d\n",
+				dev->pdev->name, dev,
+				atomic_read(&dev->pending_txurbs),
+				dev->txurb_drp_cnt,
+				dev->to_host,
+				dev->to_modem,
+				dev->tx_throttled_cnt,
+				dev->tx_unthrottled_cnt,
+				dev->rx_throttled_cnt,
+				dev->rx_unthrottled_cnt,
+				dev->rx_done.qlen,
+				dev->err,
+				test_bit(SUSPENDED, &dev->flags),
+				test_bit(TX_HALT, &dev->flags),
+				test_bit(RX_HALT, &dev->flags));
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t data_bridge_reset_stats(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct data_bridge	*dev;
+	int			i;
+
+	for (i = 0; i < ch_id; i++) {
+		dev = __dev[i];
+		if (!dev)
+			continue;
+
+		dev->to_host = 0;
+		dev->to_modem = 0;
+		dev->txurb_drp_cnt = 0;
+		dev->tx_throttled_cnt = 0;
+		dev->tx_unthrottled_cnt = 0;
+		dev->rx_throttled_cnt = 0;
+		dev->rx_unthrottled_cnt = 0;
+	}
+	return count;
+}
+
+const struct file_operations data_stats_ops = {
+	.read = data_bridge_read_stats,
+	.write = data_bridge_reset_stats,
+};
+
+static struct dentry	*data_dent;
+static struct dentry	*data_dfile_stats;
+static struct dentry	*data_dfile_tstamp;
+
+static void data_bridge_debugfs_init(void)
+{
+	data_dent = debugfs_create_dir("data_hsic_bridge", 0);
+	if (IS_ERR(data_dent))
+		return;
+
+	data_dfile_stats = debugfs_create_file("status", 0644, data_dent, 0,
+				&data_stats_ops);
+	if (!data_dfile_stats || IS_ERR(data_dfile_stats)) {
+		debugfs_remove(data_dent);
+		return;
+	}
+
+	data_dfile_tstamp = debugfs_create_file("timestamp", 0644, data_dent,
+				0, &data_timestamp_ops);
+	if (!data_dfile_tstamp || IS_ERR(data_dfile_tstamp))
+		debugfs_remove(data_dent);
+}
+
+static void data_bridge_debugfs_exit(void)
+{
+	debugfs_remove(data_dfile_stats);
+	debugfs_remove(data_dfile_tstamp);
+	debugfs_remove(data_dent);
+}
+
+#else
+static void data_bridge_debugfs_init(void) { }
+static void data_bridge_debugfs_exit(void) { }
+static void dbg_timestamp(char *event, struct sk_buff * skb)
+{
+	return;
+}
+
+static unsigned int get_timestamp(void)
+{
+	return 0;
+}
+
+#endif
+
+static int __devinit
+bridge_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+	struct usb_host_endpoint	*endpoint = NULL;
+	struct usb_host_endpoint	*bulk_in = NULL;
+	struct usb_host_endpoint	*bulk_out = NULL;
+	struct usb_host_endpoint	*int_in = NULL;
+	struct usb_device		*udev;
+	int				i;
+	int				status = 0;
+	int				numends;
+	unsigned int			iface_num;
+
+	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+
+	if (iface->num_altsetting != 1) {
+		err("%s invalid num_altsetting %u\n",
+				__func__, iface->num_altsetting);
+		return -EINVAL;
+	}
+
+	udev = interface_to_usbdev(iface);
+	usb_get_dev(udev);
+
+	if (!test_bit(iface_num, &id->driver_info))
+		return -ENODEV;
+
+	numends = iface->cur_altsetting->desc.bNumEndpoints;
+	for (i = 0; i < numends; i++) {
+		endpoint = iface->cur_altsetting->endpoint + i;
+		if (!endpoint) {
+			dev_err(&udev->dev, "%s: invalid endpoint %u\n",
+					__func__, i);
+			status = -EINVAL;
+			goto out;
+		}
+
+		if (usb_endpoint_is_bulk_in(&endpoint->desc))
+			bulk_in = endpoint;
+		else if (usb_endpoint_is_bulk_out(&endpoint->desc))
+			bulk_out = endpoint;
+		else if (usb_endpoint_is_int_in(&endpoint->desc))
+			int_in = endpoint;
+	}
+
+	if (!bulk_in || !bulk_out || !int_in) {
+		dev_err(&udev->dev, "%s: invalid endpoints\n", __func__);
+		status = -EINVAL;
+		goto out;
+	}
+
+	status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id);
+	if (status < 0) {
+		dev_err(&udev->dev, "data_bridge_probe failed %d\n", status);
+		goto out;
+	}
+
+	status = ctrl_bridge_probe(iface, int_in, ch_id);
+	if (status < 0) {
+		dev_err(&udev->dev, "ctrl_bridge_probe failed %d\n", status);
+		goto free_data_bridge;
+	}
+
+	ch_id++;
+
+	return 0;
+
+free_data_bridge:
+	platform_device_del(__dev[ch_id]->pdev);
+	usb_set_intfdata(iface, NULL);
+	kfree(__dev[ch_id]);
+	__dev[ch_id] = NULL;
+out:
+	usb_put_dev(udev);
+
+	return status;
+}
+
+static void bridge_disconnect(struct usb_interface *intf)
+{
+	struct data_bridge	*dev = usb_get_intfdata(intf);
+	struct list_head	*head;
+	struct urb		*rx_urb;
+	unsigned long		flags;
+
+	if (!dev) {
+		err("%s: data device not found\n", __func__);
+		return;
+	}
+
+	ch_id--;
+	ctrl_bridge_disconnect(ch_id);
+	platform_device_del(dev->pdev);
+	usb_set_intfdata(intf, NULL);
+	__dev[ch_id] = NULL;
+
+	cancel_work_sync(&dev->process_rx_w);
+	cancel_work_sync(&dev->kevent);
+
+	/*free rx urbs*/
+	head = &dev->rx_idle;
+	spin_lock_irqsave(&dev->rx_done.lock, flags);
+	while (!list_empty(head)) {
+		rx_urb = list_entry(head->next, struct urb, urb_list);
+		list_del(&rx_urb->urb_list);
+		usb_free_urb(rx_urb);
+	}
+	spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+
+	usb_put_dev(dev->udev);
+	kfree(dev);
+}
+
+/*bit position represents interface number*/
+#define PID9001_IFACE_MASK	0xC
+#define PID9034_IFACE_MASK	0xC
+#define PID9048_IFACE_MASK	0x18
+#define PID904C_IFACE_MASK	0x28
+
+static const struct usb_device_id bridge_ids[] = {
+	{ USB_DEVICE(0x5c6, 0x9001),
+	.driver_info = PID9001_IFACE_MASK,
+	},
+	{ USB_DEVICE(0x5c6, 0x9034),
+	.driver_info = PID9034_IFACE_MASK,
+	},
+	{ USB_DEVICE(0x5c6, 0x9048),
+	.driver_info = PID9048_IFACE_MASK,
+	},
+	{ USB_DEVICE(0x5c6, 0x904c),
+	.driver_info = PID904C_IFACE_MASK,
+	},
+
+	{ } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, bridge_ids);
+
+static struct usb_driver bridge_driver = {
+	.name =			"mdm_bridge",
+	.probe =		bridge_probe,
+	.disconnect =		bridge_disconnect,
+	.id_table =		bridge_ids,
+	.suspend =		bridge_suspend,
+	.resume =		bridge_resume,
+	.supports_autosuspend =	1,
+};
+
+static int __init bridge_init(void)
+{
+	int	ret;
+
+	ret = usb_register(&bridge_driver);
+	if (ret) {
+		err("%s: unable to register mdm_bridge driver", __func__);
+		return ret;
+	}
+
+	bridge_wq  = create_singlethread_workqueue("mdm_bridge");
+	if (!bridge_wq) {
+		usb_deregister(&bridge_driver);
+		pr_err("%s: Unable to create workqueue:bridge\n", __func__);
+		return -ENOMEM;
+	}
+
+	data_bridge_debugfs_init();
+
+	return 0;
+}
+
+static void __exit bridge_exit(void)
+{
+	data_bridge_debugfs_exit();
+	destroy_workqueue(bridge_wq);
+	usb_deregister(&bridge_driver);
+}
+
+module_init(bridge_init);
+module_exit(bridge_exit);
+
+MODULE_DESCRIPTION("Qualcomm modem data bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index c2902a8..e6c823d 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -74,6 +74,34 @@
 	  This transceiver supports high and full speed devices plus,
 	  in host mode, low speed.
 
+config USB_MSM_OTG_72K
+	bool "OTG support for Legcay Qualcomm on-chip USB controller"
+	depends on ARCH_MSM
+	select USB_OTG_UTILS
+	default USB_MSM_72K
+	help
+	  Enable this to support the USB OTG transceiver on MSM chips. It
+	  handles PHY initialization, clock management, low power mode and
+	  workarounds required after resetting the hardware. This driver is
+	  required for even peripheral only or host only mode configuration.
+	  Supports SRP and HNP when both gadget and Host are selected.
+
+config MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT
+	bool "Enable A-device timeout for B-device connection"
+	depends on USB_MSM_OTG_72K
+	default n
+	help
+	   OTG specification allows A-device to turn off VBUS if B-device
+	   fails to signal connect event before TA_WAIT_BCON (1.1 - 30 sec).
+	   SRP detection is enabled and hardware is put into low power mode
+	   upon this timeout.
+
+	   If you say yes, VBUS will be turned off if B-device does not signal
+	   connect in 30 sec. Otherwise VBUS is not turned off when Micro-A
+	   cable is connected. But hardware is put into LPM. Say no if leakage
+	   currents in your system are minimum.
+
+
 config TWL6030_USB
 	tristate "TWL6030 USB Transceiver Driver"
 	depends on TWL4030_CORE
@@ -107,6 +135,25 @@
 	  This driver is not supported on boards like trout which
 	  has an external PHY.
 
+config USB_MSM_ACA
+	bool "Support for Accessory Charger Adapter (ACA)"
+	depends on (USB_MSM_OTG || USB_MSM_OTG_72K) && ARCH_MSM
+	default n
+	help
+	  Accesory Charger Adapter is a charger specified in USB Battery
+	  Charging Specification(1.1). It enables OTG devices to charge
+	  while operating as a host or peripheral at the same time.
+
+config USB_MSM_STANDARD_ACA
+	bool "Support for Standard ACA"
+	depends on USB_MSM_ACA
+	default USB_MSM_OTG_72K
+	help
+	  A Standard ACA has a Standard-A receptacle on the Accessory Port,
+	  and can only be attached to a B-device.  RID_A and RID_GND states
+	  are only possible with Standard ACA.  Select this feature if the
+	  board is intended to support only Standard ACA.
+
 config AB8500_USB
 	tristate "AB8500 USB Transceiver Driver"
 	depends on AB8500_CORE
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 638d040..5afb02e 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -17,6 +17,7 @@
 obj-$(CONFIG_NOP_USB_XCEIV)	+= nop-usb-xceiv.o
 obj-$(CONFIG_USB_ULPI)		+= ulpi.o
 obj-$(CONFIG_USB_ULPI_VIEWPORT)	+= ulpi_viewport.o
+obj-$(CONFIG_USB_MSM_OTG_72K)	+= msm72k_otg.o
 obj-$(CONFIG_USB_MSM_OTG)	+= msm_otg.o
 obj-$(CONFIG_AB8500_USB)	+= ab8500-usb.o
 fsl_usb2_otg-objs		:= fsl_otg.o otg_fsm.o
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
new file mode 100644
index 0000000..f62ae76
--- /dev/null
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -0,0 +1,3036 @@
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/device.h>
+#include <linux/pm_qos.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/msm72k_otg.h>
+#include <mach/msm_hsusb.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <mach/clk.h>
+#include <mach/msm_xo.h>
+
+#define MSM_USB_BASE	(dev->regs)
+#define USB_LINK_RESET_TIMEOUT	(msecs_to_jiffies(10))
+#define DRIVER_NAME	"msm_otg"
+static void otg_reset(struct usb_phy *phy, int phy_reset);
+static void msm_otg_set_vbus_state(int online);
+#ifdef CONFIG_USB_EHCI_MSM_72K
+static void msm_otg_set_id_state(int id);
+#else
+static void msm_otg_set_id_state(int id)
+{
+}
+#endif
+
+struct msm_otg *the_msm_otg;
+
+static int is_host(void)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	if (dev->pdata->otg_mode == OTG_ID)
+		return (OTGSC_ID & readl(USB_OTGSC)) ? 0 : 1;
+	else
+		return !test_bit(ID, &dev->inputs);
+}
+
+static int is_b_sess_vld(void)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	if (dev->pdata->otg_mode == OTG_ID)
+		return (OTGSC_BSV & readl(USB_OTGSC)) ? 1 : 0;
+	else
+		return test_bit(B_SESS_VLD, &dev->inputs);
+}
+
+static unsigned ulpi_read(struct msm_otg *dev, unsigned reg)
+{
+	unsigned ret, timeout = 100000;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* initiate read operation */
+	writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout))
+		cpu_relax();
+
+	if (timeout == 0) {
+		pr_err("%s: timeout %08x\n", __func__,
+				 readl(USB_ULPI_VIEWPORT));
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0xffffffff;
+	}
+	ret = ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return ret;
+}
+
+static int ulpi_write(struct msm_otg *dev, unsigned val, unsigned reg)
+{
+	unsigned timeout = 10000;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* initiate write operation */
+	writel(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout))
+		;
+
+	if (timeout == 0) {
+		pr_err("%s: timeout\n", __func__);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return -1;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static int usb_ulpi_write(struct usb_phy *xceiv, u32 val, u32 reg)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+	return ulpi_write(dev, val, reg);
+}
+
+static int usb_ulpi_read(struct usb_phy *xceiv, u32 reg)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+	return ulpi_read(dev, reg);
+}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+static void enable_idgnd(struct msm_otg *dev)
+{
+	unsigned temp;
+
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+
+	ulpi_write(dev, (1<<4), 0x0E);
+	ulpi_write(dev, (1<<4), 0x11);
+	ulpi_write(dev, (1<<0), 0x0B);
+	temp = OTGSC_IDIE | OTGSC_IDPU;
+	writel_relaxed(readl_relaxed(USB_OTGSC) | temp, USB_OTGSC);
+}
+
+static void disable_idgnd(struct msm_otg *dev)
+{
+	unsigned temp;
+
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+	temp = OTGSC_IDIE | OTGSC_IDPU;
+	writel_relaxed(readl_relaxed(USB_OTGSC) & ~temp, USB_OTGSC);
+	ulpi_write(dev, (1<<4), 0x0F);
+	ulpi_write(dev, (1<<4), 0x12);
+	ulpi_write(dev, (1<<0), 0x0C);
+}
+#else
+static void enable_idgnd(struct msm_otg *dev)
+{
+}
+static void disable_idgnd(struct msm_otg *dev)
+{
+}
+#endif
+
+static void enable_idabc(struct msm_otg *dev)
+{
+#ifdef CONFIG_USB_MSM_ACA
+	ulpi_write(dev, (1<<5), 0x0E);
+	ulpi_write(dev, (1<<5), 0x11);
+#endif
+}
+static void disable_idabc(struct msm_otg *dev)
+{
+#ifdef CONFIG_USB_MSM_ACA
+	ulpi_write(dev, (1<<5), 0x0F);
+	ulpi_write(dev, (1<<5), 0x12);
+#endif
+}
+
+static void enable_sess_valid(struct msm_otg *dev)
+{
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+
+	ulpi_write(dev, (1<<2), 0x0E);
+	ulpi_write(dev, (1<<2), 0x11);
+	writel(readl(USB_OTGSC) | OTGSC_BSVIE, USB_OTGSC);
+}
+
+static void disable_sess_valid(struct msm_otg *dev)
+{
+	/* Do nothing if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return;
+
+	ulpi_write(dev, (1<<2), 0x0F);
+	ulpi_write(dev, (1<<2), 0x12);
+	writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
+}
+#ifdef CONFIG_USB_MSM_ACA
+static void set_aca_id_inputs(struct msm_otg *dev)
+{
+	u8		phy_ints;
+
+	phy_ints = ulpi_read(dev, 0x13);
+	if (phy_ints == -ETIMEDOUT)
+		return;
+
+	pr_debug("phy_ints = %x\n", phy_ints);
+	clear_bit(ID_A, &dev->inputs);
+	clear_bit(ID_B, &dev->inputs);
+	clear_bit(ID_C, &dev->inputs);
+	if (phy_id_state_a(phy_ints)) {
+		pr_debug("ID_A set\n");
+		set_bit(ID_A, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+	} else if (phy_id_state_b(phy_ints)) {
+		pr_debug("ID_B set\n");
+		set_bit(ID_B, &dev->inputs);
+	} else if (phy_id_state_c(phy_ints)) {
+		pr_debug("ID_C set\n");
+		set_bit(ID_C, &dev->inputs);
+	}
+	if (is_b_sess_vld())
+		set_bit(B_SESS_VLD, &dev->inputs);
+	else
+		clear_bit(B_SESS_VLD, &dev->inputs);
+}
+#define get_aca_bmaxpower(dev)		(dev->b_max_power)
+#define set_aca_bmaxpower(dev, power)	(dev->b_max_power = power)
+#else
+static void set_aca_id_inputs(struct msm_otg *dev)
+{
+}
+#define get_aca_bmaxpower(dev)		0
+#define set_aca_bmaxpower(dev, power)
+#endif
+static inline void set_pre_emphasis_level(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->pemp_level == PRE_EMPHASIS_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_CONFIG_REG3);
+	res &= ~(ULPI_PRE_EMPHASIS_MASK);
+	if (dev->pdata->pemp_level != PRE_EMPHASIS_DISABLE)
+		res |= dev->pdata->pemp_level;
+	ulpi_write(dev, res, ULPI_CONFIG_REG3);
+}
+
+static inline void set_hsdrv_slope(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->hsdrvslope == HS_DRV_SLOPE_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_CONFIG_REG3);
+	res &= ~(ULPI_HSDRVSLOPE_MASK);
+	res |= (dev->pdata->hsdrvslope & ULPI_HSDRVSLOPE_MASK);
+	ulpi_write(dev, res, ULPI_CONFIG_REG3);
+}
+
+static inline void set_cdr_auto_reset(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->cdr_autoreset == CDR_AUTO_RESET_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_DIGOUT_CTRL);
+	if (dev->pdata->cdr_autoreset == CDR_AUTO_RESET_ENABLE)
+		res &=  ~ULPI_CDR_AUTORESET;
+	else
+		res |=  ULPI_CDR_AUTORESET;
+	ulpi_write(dev, res, ULPI_DIGOUT_CTRL);
+}
+
+static inline void set_se1_gating(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->se1_gating == SE1_GATING_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_DIGOUT_CTRL);
+	if (dev->pdata->se1_gating == SE1_GATING_ENABLE)
+		res &=  ~ULPI_SE1_GATE;
+	else
+		res |=  ULPI_SE1_GATE;
+	ulpi_write(dev, res, ULPI_DIGOUT_CTRL);
+}
+static inline void set_driver_amplitude(struct msm_otg *dev)
+{
+	unsigned res = 0;
+
+	if (!dev->pdata || dev->pdata->drv_ampl == HS_DRV_AMPLITUDE_DEFAULT)
+		return;
+
+	res = ulpi_read(dev, ULPI_CONFIG_REG2);
+	res &= ~ULPI_DRV_AMPL_MASK;
+	if (dev->pdata->drv_ampl != HS_DRV_AMPLITUDE_ZERO_PERCENT)
+		res |= dev->pdata->drv_ampl;
+	ulpi_write(dev, res, ULPI_CONFIG_REG2);
+}
+
+static const char *state_string(enum usb_otg_state state)
+{
+	switch (state) {
+	case OTG_STATE_A_IDLE:		return "a_idle";
+	case OTG_STATE_A_WAIT_VRISE:	return "a_wait_vrise";
+	case OTG_STATE_A_WAIT_BCON:	return "a_wait_bcon";
+	case OTG_STATE_A_HOST:		return "a_host";
+	case OTG_STATE_A_SUSPEND:	return "a_suspend";
+	case OTG_STATE_A_PERIPHERAL:	return "a_peripheral";
+	case OTG_STATE_A_WAIT_VFALL:	return "a_wait_vfall";
+	case OTG_STATE_A_VBUS_ERR:	return "a_vbus_err";
+	case OTG_STATE_B_IDLE:		return "b_idle";
+	case OTG_STATE_B_SRP_INIT:	return "b_srp_init";
+	case OTG_STATE_B_PERIPHERAL:	return "b_peripheral";
+	case OTG_STATE_B_WAIT_ACON:	return "b_wait_acon";
+	case OTG_STATE_B_HOST:		return "b_host";
+	default:			return "UNDEFINED";
+	}
+}
+
+static const char *timer_string(int bit)
+{
+	switch (bit) {
+	case A_WAIT_VRISE:		return "a_wait_vrise";
+	case A_WAIT_VFALL:		return "a_wait_vfall";
+	case B_SRP_FAIL:		return "b_srp_fail";
+	case A_WAIT_BCON:		return "a_wait_bcon";
+	case A_AIDL_BDIS:		return "a_aidl_bdis";
+	case A_BIDL_ADIS:		return "a_bidl_adis";
+	case B_ASE0_BRST:		return "b_ase0_brst";
+	default:			return "UNDEFINED";
+	}
+}
+
+/* Prevent idle power collapse(pc) while operating in peripheral mode */
+static void otg_pm_qos_update_latency(struct msm_otg *dev, int vote)
+{
+	struct msm_otg_platform_data *pdata = dev->pdata;
+	u32 swfi_latency = 0;
+
+	if (pdata)
+		swfi_latency = pdata->swfi_latency + 1;
+
+	if (vote)
+		pm_qos_update_request(&pdata->pm_qos_req_dma,
+				swfi_latency);
+	else
+		pm_qos_update_request(&pdata->pm_qos_req_dma,
+				PM_QOS_DEFAULT_VALUE);
+}
+
+/* Controller gives interrupt for every 1 mesc if 1MSIE is set in OTGSC.
+ * This interrupt can be used as a timer source and OTG timers can be
+ * implemented. But hrtimers on MSM hardware can give atleast 1/32 KHZ
+ * precision. This precision is more than enough for OTG timers.
+ */
+static enum hrtimer_restart msm_otg_timer_func(struct hrtimer *_timer)
+{
+	struct msm_otg *dev = container_of(_timer, struct msm_otg, timer);
+
+	/* Phy lockup issues are observed when VBUS Valid interrupt is
+	 * enabled. Hence set A_VBUS_VLD upon timer exipration.
+	 */
+	if (dev->active_tmout == A_WAIT_VRISE)
+		set_bit(A_VBUS_VLD, &dev->inputs);
+	else
+		set_bit(dev->active_tmout, &dev->tmouts);
+
+	pr_debug("expired %s timer\n", timer_string(dev->active_tmout));
+	queue_work(dev->wq, &dev->sm_work);
+	return HRTIMER_NORESTART;
+}
+
+static void msm_otg_del_timer(struct msm_otg *dev)
+{
+	int bit = dev->active_tmout;
+
+	pr_debug("deleting %s timer. remaining %lld msec \n", timer_string(bit),
+			div_s64(ktime_to_us(hrtimer_get_remaining(&dev->timer)),
+					1000));
+	hrtimer_cancel(&dev->timer);
+	clear_bit(bit, &dev->tmouts);
+}
+
+static void msm_otg_start_timer(struct msm_otg *dev, int time, int bit)
+{
+	clear_bit(bit, &dev->tmouts);
+	dev->active_tmout = bit;
+	pr_debug("starting %s timer\n", timer_string(bit));
+	hrtimer_start(&dev->timer,
+			ktime_set(time / 1000, (time % 1000) * 1000000),
+			HRTIMER_MODE_REL);
+}
+
+/* No two otg timers run in parallel. So one hrtimer is sufficient */
+static void msm_otg_init_timer(struct msm_otg *dev)
+{
+	hrtimer_init(&dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	dev->timer.function = msm_otg_timer_func;
+}
+
+static const char *event_string(enum usb_otg_event event)
+{
+	switch (event) {
+	case OTG_EVENT_DEV_CONN_TMOUT:
+		return "DEV_CONN_TMOUT";
+	case OTG_EVENT_NO_RESP_FOR_HNP_ENABLE:
+		return "NO_RESP_FOR_HNP_ENABLE";
+	case OTG_EVENT_HUB_NOT_SUPPORTED:
+		return "HUB_NOT_SUPPORTED";
+	case OTG_EVENT_DEV_NOT_SUPPORTED:
+		return "DEV_NOT_SUPPORTED,";
+	case OTG_EVENT_HNP_FAILED:
+		return "HNP_FAILED";
+	case OTG_EVENT_NO_RESP_FOR_SRP:
+		return "NO_RESP_FOR_SRP";
+	default:
+		return "UNDEFINED";
+	}
+}
+
+static int msm_otg_send_event(struct usb_otg *otg,
+				enum usb_otg_event event)
+{
+	char module_name[16];
+	char udev_event[128];
+	char *envp[] = { module_name, udev_event, NULL };
+	int ret;
+
+	pr_debug("sending %s event\n", event_string(event));
+
+	snprintf(module_name, 16, "MODULE=%s", DRIVER_NAME);
+	snprintf(udev_event, 128, "EVENT=%s", event_string(event));
+	ret = kobject_uevent_env(&otg->phy->dev->kobj, KOBJ_CHANGE, envp);
+	if (ret < 0)
+		pr_info("uevent sending failed with ret = %d\n", ret);
+	return ret;
+}
+
+static int msm_otg_start_hnp(struct usb_otg *otg)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state != OTG_STATE_A_HOST) {
+		pr_err("HNP can not be initiated in %s state\n",
+				state_string(state));
+		return -EINVAL;
+	}
+
+	pr_debug("A-Host: HNP initiated\n");
+	clear_bit(A_BUS_REQ, &dev->inputs);
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+	return 0;
+}
+
+static int msm_otg_start_srp(struct usb_otg *otg)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	u32	val;
+	int ret = 0;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state != OTG_STATE_B_IDLE) {
+		pr_err("SRP can not be initiated in %s state\n",
+				state_string(state));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if ((jiffies - dev->b_last_se0_sess) < msecs_to_jiffies(TB_SRP_INIT)) {
+		pr_debug("initial conditions of SRP are not met. Try again"
+				"after some time\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	/* Harware auto assist data pulsing: Data pulse is given
+	 * for 7msec; wait for vbus
+	 */
+	val = readl(USB_OTGSC);
+	writel((val & ~OTGSC_INTR_STS_MASK) | OTGSC_HADP, USB_OTGSC);
+
+	/* VBUS plusing is obsoleted in OTG 2.0 supplement */
+out:
+	return ret;
+}
+
+static int msm_otg_set_power(struct usb_phy *xceiv, unsigned mA)
+{
+	static enum chg_type 	curr_chg = USB_CHG_TYPE__INVALID;
+	struct msm_otg		*dev = container_of(xceiv, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = dev->pdata;
+	enum chg_type 		new_chg = atomic_read(&dev->chg_type);
+	unsigned 		charge = mA;
+
+	/* Call chg_connected only if the charger has changed */
+	if (new_chg != curr_chg && pdata->chg_connected) {
+		curr_chg = new_chg;
+		pdata->chg_connected(new_chg);
+	}
+
+	/* Always use USB_IDCHG_MAX for charging in ID_B and ID_C */
+	if (test_bit(ID_C, &dev->inputs) ||
+				test_bit(ID_B, &dev->inputs))
+		charge = USB_IDCHG_MAX;
+
+	pr_debug("Charging with %dmA current\n", charge);
+	/* Call vbus_draw only if the charger is of known type and also
+	 * ignore request to stop charging as a result of suspend interrupt
+	 * when wall-charger is used.
+	 */
+	if (pdata->chg_vbus_draw && new_chg != USB_CHG_TYPE__INVALID &&
+		(charge || new_chg != USB_CHG_TYPE__WALLCHARGER))
+			pdata->chg_vbus_draw(charge);
+
+	if (new_chg == USB_CHG_TYPE__WALLCHARGER) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return 0;
+}
+
+static int msm_otg_set_clk(struct usb_phy *xceiv, int on)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	if (on)
+		/* enable clocks */
+		clk_prepare_enable(dev->alt_core_clk);
+	else
+		clk_disable_unprepare(dev->alt_core_clk);
+
+	return 0;
+}
+static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = dev->pdata;
+
+	if (!otg->gadget)
+		return;
+
+	if (on) {
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_PERIPHERAL);
+		/* vote for minimum dma_latency to prevent idle
+		 * power collapse(pc) while running in peripheral mode.
+		 */
+		otg_pm_qos_update_latency(dev, 1);
+
+		/* increment the clk reference count so that
+		 * it would be still on when disabled from
+		 * low power mode routine
+		 */
+		if (dev->pdata->pclk_required_during_lpm)
+			clk_prepare_enable(dev->iface_clk);
+
+		usb_gadget_vbus_connect(otg->gadget);
+	} else {
+		atomic_set(&dev->chg_type, USB_CHG_TYPE__INVALID);
+		usb_gadget_vbus_disconnect(otg->gadget);
+
+		/* decrement the clk reference count so that
+		 * it would be off when disabled from
+		 * low power mode routine
+		 */
+		if (dev->pdata->pclk_required_during_lpm)
+			clk_disable_unprepare(dev->iface_clk);
+
+		otg_pm_qos_update_latency(dev, 0);
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_DISABLE);
+	}
+}
+
+static void msm_otg_start_host(struct usb_otg *otg, int on)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = dev->pdata;
+
+	if (!otg->host)
+		return;
+
+	if (dev->start_host) {
+		/* Some targets, e.g. ST1.5, use GPIO to choose b/w connector */
+		if (on && pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_HOST);
+
+		/* increment or decrement the clk reference count
+		 * to avoid usb h/w lockup issues when low power
+		 * mode is initiated and vbus is on.
+		 */
+		if (dev->pdata->pclk_required_during_lpm) {
+			if (on)
+				clk_prepare_enable(dev->iface_clk);
+			else
+				clk_disable_unprepare(dev->iface_clk);
+		}
+
+		dev->start_host(otg->host, on);
+
+		if (!on && pdata->setup_gpio)
+			pdata->setup_gpio(USB_SWITCH_DISABLE);
+	}
+}
+
+static int msm_otg_suspend(struct msm_otg *dev)
+{
+	unsigned long timeout;
+	bool host_bus_suspend;
+	unsigned ret;
+	enum chg_type chg_type = atomic_read(&dev->chg_type);
+	unsigned long flags;
+
+	disable_irq(dev->irq);
+	if (atomic_read(&dev->in_lpm))
+		goto out;
+#ifdef CONFIG_USB_MSM_ACA
+	/*
+	 * ACA interrupts are disabled before entering into LPM.
+	 * If LPM is allowed in host mode with accessory charger
+	 * connected or only accessory charger is connected,
+	 * there is a chance that charger is removed and we will
+	 * not know about it.
+	 *
+	 * REVISIT
+	 *
+	 * Allowing LPM in case of gadget bus suspend is tricky.
+	 * Bus suspend can happen in two states.
+	 * 1. ID_float:  Allowing LPM has pros and cons. If LPM is allowed
+	 * and accessory charger is connected, we miss ID_float --> ID_C
+	 * transition where we could draw large amount of current
+	 * compared to the suspend current.
+	 * 2. ID_C: We can not allow LPM. If accessory charger is removed
+	 * we should not draw more than what host could supply which will
+	 * be less compared to accessory charger.
+	 *
+	 * For simplicity, LPM is not allowed in bus suspend.
+	 */
+#ifndef CONFIG_USB_MSM_STANDARD_ACA
+	/*
+	 * RID_A and IdGnd states are only possible with standard ACA.  We can
+	 * exit from low power mode with !BSV or IdGnd interrupt.  Hence LPM
+	 * is allowed.
+	 */
+	if ((test_bit(ID, &dev->inputs) && test_bit(B_SESS_VLD, &dev->inputs) &&
+			chg_type != USB_CHG_TYPE__WALLCHARGER) ||
+			test_bit(ID_A, &dev->inputs))
+		goto out;
+#endif
+	/* Disable ID_abc interrupts else it causes spurious interrupt */
+	disable_idabc(dev);
+#endif
+	ulpi_read(dev, 0x14);/* clear PHY interrupt latch register */
+
+	/*
+	 * Turn on PHY comparators if,
+	 * 1. USB wall charger is connected (bus suspend is not supported)
+	 * 2. Host bus suspend
+	 * 3. host is supported, but, id is not routed to pmic
+	 * 4. peripheral is supported, but, vbus is not routed to pmic
+	 */
+	host_bus_suspend = dev->phy.otg->host && is_host();
+
+	/*
+	 *  Configure the PMIC ID only in case of cable disconnect.
+	 *  PMIC doesn't generate interrupt for ID_GND to ID_A
+	 *  transistion. hence use the PHY ID cricuit.
+	 */
+	if (dev->pdata->pmic_id_notif_init && !host_bus_suspend &&
+		!test_bit(ID_A, &dev->inputs)) {
+		disable_idgnd(dev);
+		ret = dev->pdata->pmic_id_notif_init(
+			&msm_otg_set_id_state, 1);
+		if (!ret) {
+			dev->pmic_id_notif_supp = 1;
+			if (dev->pdata->pmic_id_irq)
+				dev->id_irq = dev->pdata->pmic_id_irq;
+		} else if (ret == -ENOTSUPP) {
+			pr_debug("%s:USB ID is not routed to pmic",
+			__func__);
+			enable_idgnd(dev);
+		} else {
+			pr_err("%s: pmic_id_ notif_init failed err:%d",
+				__func__, ret);
+		}
+	}
+
+	if ((dev->phy.otg->gadget && chg_type == USB_CHG_TYPE__WALLCHARGER) ||
+		host_bus_suspend ||
+		(dev->phy.otg->host && !dev->pmic_id_notif_supp) ||
+		(dev->phy.otg->gadget && !dev->pmic_vbus_notif_supp)) {
+		ulpi_write(dev, 0x01, 0x30);
+	}
+
+	ulpi_write(dev, 0x08, 0x09);/* turn off PLL on integrated phy */
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	disable_phy_clk();
+	while (!is_phy_clk_disabled()) {
+		if (time_after(jiffies, timeout)) {
+			pr_err("%s: Unable to suspend phy\n", __func__);
+			/*
+			 * Start otg state machine in default state upon
+			 * phy suspend failure*/
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_UNDEFINED;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			queue_work(dev->wq, &dev->sm_work);
+			goto out;
+		}
+		msleep(1);
+		/* check if there are any pending interrupts*/
+		if (((readl(USB_OTGSC) & OTGSC_INTR_MASK) >> 8) &
+				readl(USB_OTGSC)) {
+			enable_idabc(dev);
+			goto out;
+		}
+	}
+
+	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+	/* Ensure that above operation is completed before turning off clocks */
+	mb();
+
+	if (dev->iface_clk)
+		clk_disable_unprepare(dev->iface_clk);
+
+	clk_disable_unprepare(dev->core_clk);
+	/* usb phy no more require TCXO clock, hence vote for TCXO disable*/
+	ret = msm_xo_mode_vote(dev->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		pr_err("%s failed to devote for"
+			"TCXO D1 buffer%d\n", __func__, ret);
+
+	if (device_may_wakeup(dev->phy.dev)) {
+		enable_irq_wake(dev->irq);
+		if (dev->vbus_on_irq)
+			enable_irq_wake(dev->vbus_on_irq);
+		if (dev->id_irq)
+			enable_irq_wake(dev->id_irq);
+	}
+
+	atomic_set(&dev->in_lpm, 1);
+
+	/*
+	 * TODO: put regulators in low power mode by assuming that
+	 * regulators are brought back to active state before PHY
+	 * becomes active. But this assumption becomes wrong in case of
+	 * ACA charger where PHY itself will generate the wakeup
+	 * interrupt. This creates a small window where PHY regulators
+	 * are in LPM but PHY is in active state and this patch assumes
+	 * that there is no harm with this. Till hw folks confirms this
+	 * put regulators in lpm.
+	 */
+	 if (!host_bus_suspend && dev->pmic_vbus_notif_supp &&
+		!test_bit(ID_A, &dev->inputs)) {
+		pr_debug("phy can power collapse: (%d)\n",
+			can_phy_power_collapse(dev));
+		if (can_phy_power_collapse(dev) && dev->pdata->ldo_enable) {
+			pr_debug("disabling the regulators\n");
+			dev->pdata->ldo_enable(0);
+		}
+	}
+
+	/* phy can interrupts when vddcx is at 0.75, so irrespective
+	 * of pmic notification support, configure vddcx @0.75
+	 */
+	if (dev->pdata->config_vddcx)
+		dev->pdata->config_vddcx(0);
+	pr_info("%s: usb in low power mode\n", __func__);
+
+out:
+	enable_irq(dev->irq);
+
+	return 0;
+}
+
+static int msm_otg_resume(struct msm_otg *dev)
+{
+	unsigned temp;
+	unsigned ret;
+
+	if (!atomic_read(&dev->in_lpm))
+		return 0;
+	/* vote for vddcx, as PHY cannot tolerate vddcx below 1.0V */
+	if (dev->pdata->config_vddcx) {
+		ret = dev->pdata->config_vddcx(1);
+		if (ret) {
+			pr_err("%s: unable to enable vddcx digital core:%d\n",
+				__func__, ret);
+		}
+	}
+	if (dev->pdata->ldo_set_voltage)
+		dev->pdata->ldo_set_voltage(3400);
+
+	/* Vote for TCXO when waking up the phy */
+	ret = msm_xo_mode_vote(dev->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		pr_err("%s failed to vote for"
+			"TCXO D1 buffer%d\n", __func__, ret);
+
+	clk_prepare_enable(dev->core_clk);
+
+	if (dev->iface_clk)
+		clk_prepare_enable(dev->iface_clk);
+
+	temp = readl(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel(temp, USB_USBCMD);
+
+	if (device_may_wakeup(dev->phy.dev)) {
+		disable_irq_wake(dev->irq);
+		if (dev->vbus_on_irq)
+			disable_irq_wake(dev->vbus_on_irq);
+		if (dev->id_irq)
+			disable_irq_wake(dev->id_irq);
+	}
+
+	atomic_set(&dev->in_lpm, 0);
+
+	pr_info("%s: usb exited from low power mode\n", __func__);
+
+	return 0;
+}
+
+static void msm_otg_get_resume(struct msm_otg *dev)
+{
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_get_noresume(dev->phy.dev);
+	pm_runtime_resume(dev->phy.dev);
+#else
+	msm_otg_resume(dev);
+#endif
+}
+
+static void msm_otg_put_suspend(struct msm_otg *dev)
+{
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_put_sync(dev->phy.dev);
+	if (!atomic_read(&dev->in_lpm))
+		pm_runtime_get_sync(dev->phy.dev);
+#else
+	msm_otg_suspend(dev);
+#endif
+}
+
+static void msm_otg_resume_w(struct work_struct *w)
+{
+	struct msm_otg	*dev = container_of(w, struct msm_otg, otg_resume_work);
+	unsigned long timeout;
+
+	if (can_phy_power_collapse(dev) && dev->pdata->ldo_enable)
+		dev->pdata->ldo_enable(1);
+
+	if (pm_runtime_enabled(dev->phy.dev)) {
+		msm_otg_get_resume(dev);
+	} else {
+		pm_runtime_get_noresume(dev->phy.dev);
+		msm_otg_resume(dev);
+		pm_runtime_set_active(dev->phy.dev);
+	}
+
+	if (!is_phy_clk_disabled())
+		goto phy_resumed;
+
+	timeout = jiffies + usecs_to_jiffies(100);
+	enable_phy_clk();
+	while (is_phy_clk_disabled() || !is_phy_active()) {
+		if (time_after(jiffies, timeout)) {
+			pr_err("%s: Unable to wakeup phy. is_phy_active: %x\n",
+				 __func__, !!is_phy_active());
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			break;
+		}
+		udelay(10);
+	}
+
+phy_resumed:
+	/*
+	 * It is observed that BSVIS may get set immediatly
+	 * after PHY becomes active upon micro-B cable connect.
+	 * But BSVIS might get cleared by below enable_idgnd
+	 * function which causes hw to not generate the BSV interrupt.
+	 * Hence check for BSV interrupt explictly and schedule the
+	 * work.
+	 */
+	if (readl_relaxed(USB_OTGSC) & OTGSC_BSVIS) {
+		set_bit(B_SESS_VLD, &dev->inputs);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+	if (dev->pmic_id_notif_supp) {
+		dev->pdata->pmic_id_notif_init(&msm_otg_set_id_state, 0);
+		dev->pmic_id_notif_supp = 0;
+		enable_idgnd(dev);
+	}
+
+	/* Enable Idabc interrupts as these were disabled before entering LPM */
+	enable_idabc(dev);
+
+	/*
+	 * There is corner case where host won't be resumed
+	 * while transitioning from ID_GND to ID_A. In that
+	 * IDGND might have cleared and ID_A might not have updated
+	 * yet. Hence update the ACA states explicitly.
+	 */
+	set_aca_id_inputs(dev);
+
+	/* If resume signalling finishes before lpm exit, PCD is not set in
+	 * USBSTS register. Drive resume signal to the downstream device now
+	 * so that host driver can process the upcoming port change interrupt.*/
+	if (is_host() || test_bit(ID_A, &dev->inputs)) {
+		writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+		msm_otg_start_host(dev->phy.otg, REQUEST_RESUME);
+	}
+
+	/* Enable irq which was disabled before scheduling this work.
+	 * But don't release wake_lock, as we got async interrupt and
+	 * there will be some work pending for OTG state machine.
+	 */
+	enable_irq(dev->irq);
+}
+
+static int msm_otg_set_suspend(struct usb_phy *xceiv, int suspend)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_debug("suspend request in state: %s\n",
+			state_string(state));
+
+	if (suspend) {
+		switch (state) {
+#ifndef CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT
+		case OTG_STATE_A_WAIT_BCON:
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(xceiv, USB_IDCHG_MIN - 100);
+			msm_otg_put_suspend(dev);
+			break;
+#endif
+		case OTG_STATE_A_HOST:
+			clear_bit(A_BUS_REQ, &dev->inputs);
+			wake_lock(&dev->wlock);
+			queue_work(dev->wq, &dev->sm_work);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			if (xceiv->otg->gadget->b_hnp_enable) {
+				set_bit(A_BUS_SUSPEND, &dev->inputs);
+				set_bit(B_BUS_REQ, &dev->inputs);
+				wake_lock(&dev->wlock);
+				queue_work(dev->wq, &dev->sm_work);
+			}
+			break;
+		case OTG_STATE_A_PERIPHERAL:
+			msm_otg_start_timer(dev, TA_BIDL_ADIS,
+					A_BIDL_ADIS);
+			break;
+		default:
+			break;
+		}
+	} else {
+		unsigned long timeout;
+
+		switch (state) {
+		case OTG_STATE_A_PERIPHERAL:
+			/* A-peripheral observed activity on bus.
+			 * clear A_BIDL_ADIS timer.
+			 */
+			msm_otg_del_timer(dev);
+			break;
+		case OTG_STATE_A_SUSPEND:
+			/* Remote wakeup or resume */
+			set_bit(A_BUS_REQ, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_HOST;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (test_bit(ID_A, &dev->inputs) &&
+				(get_aca_bmaxpower(dev) < USB_IDCHG_MIN))
+				msm_otg_set_power(xceiv,
+					USB_IDCHG_MIN - get_aca_bmaxpower(dev));
+			break;
+		default:
+			break;
+		}
+
+		if (suspend == atomic_read(&dev->in_lpm))
+			return 0;
+
+		disable_irq(dev->irq);
+		if (dev->pmic_vbus_notif_supp)
+			if (can_phy_power_collapse(dev) &&
+					dev->pdata->ldo_enable)
+				dev->pdata->ldo_enable(1);
+
+		msm_otg_get_resume(dev);
+
+		if (!is_phy_clk_disabled())
+			goto out;
+
+		timeout = jiffies + usecs_to_jiffies(100);
+		enable_phy_clk();
+		while (is_phy_clk_disabled() || !is_phy_active()) {
+			if (time_after(jiffies, timeout)) {
+				pr_err("%s: Unable to wakeup phy. "
+					"is_phy_active: %x\n",
+					__func__, !!is_phy_active());
+				/* Reset both phy and link */
+				otg_reset(&dev->phy, 1);
+				break;
+			}
+			udelay(10);
+		}
+		if (dev->pmic_id_notif_supp) {
+			dev->pdata->pmic_id_notif_init(
+				&msm_otg_set_id_state, 0);
+			dev->pmic_id_notif_supp = 0;
+			enable_idgnd(dev);
+		}
+out:
+		enable_idabc(dev);
+		enable_irq(dev->irq);
+
+	}
+
+	return 0;
+}
+
+static int msm_otg_set_peripheral(struct usb_otg *otg,
+			struct usb_gadget *gadget)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	if (!gadget) {
+		msm_otg_start_peripheral(otg, 0);
+		otg->gadget = 0;
+		disable_sess_valid(dev);
+		if (!otg->host)
+			disable_idabc(dev);
+		return 0;
+	}
+	otg->gadget = gadget;
+	pr_info("peripheral driver registered w/ tranceiver\n");
+
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+	return 0;
+}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+static int usbdev_notify(struct notifier_block *self,
+			unsigned long action, void *device)
+{
+	enum usb_otg_state state;
+	struct msm_otg *dev = container_of(self, struct msm_otg, usbdev_nb);
+	struct usb_device *udev = device;
+	int work = 1;
+	unsigned long flags;
+
+	/* Interested in only devices directly connected
+	 * to root hub directly.
+	 */
+	if (!udev->parent || udev->parent->parent)
+		goto out;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	switch (state) {
+	case OTG_STATE_A_WAIT_BCON:
+		if (action == USB_DEVICE_ADD) {
+			pr_debug("B_CONN set\n");
+			set_bit(B_CONN, &dev->inputs);
+			if (udev->actconfig) {
+				set_aca_bmaxpower(dev,
+					udev->actconfig->desc.bMaxPower * 2);
+				goto do_work;
+			}
+			if (udev->portnum == udev->bus->otg_port)
+				set_aca_bmaxpower(dev, USB_IB_UNCFG);
+			else
+				set_aca_bmaxpower(dev, 100);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if (action == USB_DEVICE_REMOVE) {
+			pr_debug("B_CONN clear\n");
+			clear_bit(B_CONN, &dev->inputs);
+			set_aca_bmaxpower(dev, 0);
+		}
+		break;
+	default:
+		work = 0;
+		break;
+	}
+do_work:
+	if (work) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+out:
+	return NOTIFY_OK;
+}
+
+static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct msm_otg *dev = container_of(otg->phy, struct msm_otg, phy);
+
+	if (!dev || (dev != the_msm_otg))
+		return -ENODEV;
+
+	if (!dev->start_host)
+		return -ENODEV;
+
+	if (!host) {
+		msm_otg_start_host(otg, REQUEST_STOP);
+		usb_unregister_notify(&dev->usbdev_nb);
+		otg->host = 0;
+		dev->start_host = 0;
+		disable_idgnd(dev);
+		if (!otg->gadget)
+			disable_idabc(dev);
+		return 0;
+	}
+#ifdef CONFIG_USB_OTG
+	host->otg_port = 1;
+#endif
+	dev->usbdev_nb.notifier_call = usbdev_notify;
+	usb_register_notify(&dev->usbdev_nb);
+	otg->host = host;
+	pr_info("host driver registered w/ tranceiver\n");
+
+#ifndef CONFIG_USB_MSM_72K
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+#endif
+	return 0;
+}
+
+static void msm_otg_set_id_state(int id)
+{
+	struct msm_otg *dev = the_msm_otg;
+	unsigned long flags;
+
+	if (!atomic_read(&dev->in_lpm))
+		return;
+
+	if (id) {
+		set_bit(ID, &dev->inputs);
+	} else {
+		clear_bit(ID, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->phy.state != OTG_STATE_UNDEFINED) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+#endif
+
+void msm_otg_set_vbus_state(int online)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	/*
+	 * Process disconnect only for wallcharger
+	 * during fast plug-out plug-in at the
+	 * AC source side.
+	 */
+	if (online)
+		set_bit(B_SESS_VLD, &dev->inputs);
+	else
+		clear_bit(B_SESS_VLD, &dev->inputs);
+
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+}
+
+static irqreturn_t msm_otg_irq(int irq, void *data)
+{
+	struct msm_otg *dev = data;
+	u32 otgsc, sts, pc, sts_mask;
+	irqreturn_t ret = IRQ_HANDLED;
+	int work = 0;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	if (atomic_read(&dev->in_lpm)) {
+		disable_irq_nosync(dev->irq);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->otg_resume_work);
+		goto out;
+	}
+
+	/* Return immediately if instead of ID pin, USER controls mode switch */
+	if (dev->pdata->otg_mode == OTG_USER_CONTROL)
+		return IRQ_NONE;
+
+
+	otgsc = readl(USB_OTGSC);
+	sts = readl(USB_USBSTS);
+
+	sts_mask = (otgsc & OTGSC_INTR_MASK) >> 8;
+
+	if (!((otgsc & sts_mask) || (sts & STS_PCI))) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_debug("IRQ state: %s\n", state_string(state));
+	pr_debug("otgsc = %x\n", otgsc);
+
+	if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+		if (otgsc & OTGSC_ID) {
+			pr_debug("Id set\n");
+			set_bit(ID, &dev->inputs);
+		} else {
+			pr_debug("Id clear\n");
+			/* Assert a_bus_req to supply power on
+			 * VBUS when Micro/Mini-A cable is connected
+			 * with out user intervention.
+			 */
+			set_bit(A_BUS_REQ, &dev->inputs);
+			clear_bit(ID, &dev->inputs);
+		}
+		writel(otgsc, USB_OTGSC);
+		work = 1;
+	} else if (otgsc & OTGSC_BSVIS) {
+		writel(otgsc, USB_OTGSC);
+		/* BSV interrupt comes when operating as an A-device
+		 * (VBUS on/off).
+		 * But, handle BSV when charger is removed from ACA in ID_A
+		 */
+		if ((state >= OTG_STATE_A_IDLE) &&
+			!test_bit(ID_A, &dev->inputs))
+			goto out;
+		if (otgsc & OTGSC_BSV) {
+			pr_debug("BSV set\n");
+			set_bit(B_SESS_VLD, &dev->inputs);
+		} else {
+			pr_debug("BSV clear\n");
+			clear_bit(B_SESS_VLD, &dev->inputs);
+		}
+		work = 1;
+	} else if (otgsc & OTGSC_DPIS) {
+		pr_debug("DPIS detected\n");
+		writel(otgsc, USB_OTGSC);
+		set_bit(A_SRP_DET, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+		work = 1;
+	} else if (sts & STS_PCI) {
+		pc = readl(USB_PORTSC);
+		pr_debug("portsc = %x\n", pc);
+		ret = IRQ_NONE;
+		/* HCD Acks PCI interrupt. We use this to switch
+		 * between different OTG states.
+		 */
+		work = 1;
+		switch (state) {
+		case OTG_STATE_A_SUSPEND:
+			if (dev->phy.otg->host->b_hnp_enable &&
+					(pc & PORTSC_CSC) &&
+					!(pc & PORTSC_CCS)) {
+				pr_debug("B_CONN clear\n");
+				clear_bit(B_CONN, &dev->inputs);
+			}
+			break;
+		case OTG_STATE_B_WAIT_ACON:
+			if ((pc & PORTSC_CSC) && (pc & PORTSC_CCS)) {
+				pr_debug("A_CONN set\n");
+				set_bit(A_CONN, &dev->inputs);
+				/* Clear ASE0_BRST timer */
+				msm_otg_del_timer(dev);
+			}
+			break;
+		case OTG_STATE_B_HOST:
+			if ((pc & PORTSC_CSC) && !(pc & PORTSC_CCS)) {
+				pr_debug("A_CONN clear\n");
+				clear_bit(A_CONN, &dev->inputs);
+			}
+			break;
+		default:
+			work = 0;
+			break;
+		}
+	}
+	if (work) {
+#ifdef CONFIG_USB_MSM_ACA
+		/* With ACA, ID can change bcoz of BSVIS as well, so update */
+		if ((otgsc & OTGSC_IDIS) || (otgsc & OTGSC_BSVIS))
+			set_aca_id_inputs(dev);
+#endif
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+out:
+	return ret;
+}
+
+#define ULPI_VERIFY_MAX_LOOP_COUNT  5
+#define PHY_CALIB_RETRY_COUNT 10
+static void phy_clk_reset(struct msm_otg *dev)
+{
+	unsigned rc;
+	enum clk_reset_action assert = CLK_RESET_ASSERT;
+
+	if (dev->pdata->phy_reset_sig_inverted)
+		assert = CLK_RESET_DEASSERT;
+
+	rc = clk_reset(dev->phy_reset_clk, assert);
+	if (rc) {
+		pr_err("%s: phy clk assert failed\n", __func__);
+		return;
+	}
+
+	msleep(1);
+
+	rc = clk_reset(dev->phy_reset_clk, !assert);
+	if (rc) {
+		pr_err("%s: phy clk deassert failed\n", __func__);
+		return;
+	}
+
+	msleep(1);
+}
+
+static unsigned ulpi_read_with_reset(struct msm_otg *dev, unsigned reg)
+{
+	int temp;
+	unsigned res;
+
+	for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) {
+		res = ulpi_read(dev, reg);
+		if (res != 0xffffffff)
+			return res;
+
+		phy_clk_reset(dev);
+	}
+
+	pr_err("%s: ulpi read failed for %d times\n",
+			__func__, ULPI_VERIFY_MAX_LOOP_COUNT);
+
+	return -1;
+}
+
+static int ulpi_write_with_reset(struct msm_otg *dev,
+unsigned val, unsigned reg)
+{
+	int temp, res;
+
+	for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) {
+		res = ulpi_write(dev, val, reg);
+		if (!res)
+			return 0;
+		phy_clk_reset(dev);
+	}
+	pr_err("%s: ulpi write failed for %d times\n",
+		__func__, ULPI_VERIFY_MAX_LOOP_COUNT);
+
+	return -1;
+}
+
+/* some of the older targets does not turn off the PLL
+ * if onclock bit is set and clocksuspendM bit is on,
+ * hence clear them too and initiate the suspend mode
+ * by clearing SupendM bit.
+ */
+static inline int turn_off_phy_pll(struct msm_otg *dev)
+{
+	unsigned res;
+
+	res = ulpi_read_with_reset(dev, ULPI_CONFIG_REG1);
+	if (res == 0xffffffff)
+		return -ETIMEDOUT;
+
+	res = ulpi_write_with_reset(dev,
+		res & ~(ULPI_ONCLOCK), ULPI_CONFIG_REG1);
+	if (res)
+		return -ETIMEDOUT;
+
+	res = ulpi_write_with_reset(dev,
+		ULPI_CLOCK_SUSPENDM, ULPI_IFC_CTRL_CLR);
+	if (res)
+		return -ETIMEDOUT;
+
+	/*Clear SuspendM bit to initiate suspend mode */
+	res = ulpi_write_with_reset(dev,
+		ULPI_SUSPENDM, ULPI_FUNC_CTRL_CLR);
+	if (res)
+		return -ETIMEDOUT;
+
+	return res;
+}
+
+static inline int check_phy_caliberation(struct msm_otg *dev)
+{
+	unsigned res;
+
+	res = ulpi_read_with_reset(dev, ULPI_DEBUG);
+
+	if (res == 0xffffffff)
+		return -ETIMEDOUT;
+
+	if (!(res & ULPI_CALIB_STS) && ULPI_CALIB_VAL(res))
+		return 0;
+
+	return -1;
+}
+
+static int msm_otg_phy_caliberate(struct msm_otg *dev)
+{
+	int i = 0;
+	unsigned long res;
+
+	do {
+		res = turn_off_phy_pll(dev);
+		if (res)
+			return -ETIMEDOUT;
+
+		/* bring phy out of suspend */
+		phy_clk_reset(dev);
+
+		res = check_phy_caliberation(dev);
+		if (!res)
+			return res;
+		i++;
+
+	} while (i < PHY_CALIB_RETRY_COUNT);
+
+	return res;
+}
+
+static int msm_otg_phy_reset(struct msm_otg *dev)
+{
+	unsigned rc;
+	unsigned temp;
+	unsigned long timeout;
+
+	rc = clk_reset(dev->alt_core_clk, CLK_RESET_ASSERT);
+	if (rc) {
+		pr_err("%s: usb hs clk assert failed\n", __func__);
+		return -1;
+	}
+
+	phy_clk_reset(dev);
+
+	rc = clk_reset(dev->alt_core_clk, CLK_RESET_DEASSERT);
+	if (rc) {
+		pr_err("%s: usb hs clk deassert failed\n", __func__);
+		return -1;
+	}
+	/* Observing ulpi timeouts as part of PHY calibration. On resetting
+	 * the HW link explicity by setting the RESET bit in the USBCMD
+	 * register before PHY calibration fixes the ulpi timeout issue.
+	 * This workaround is required for unicorn target
+	 */
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	do {
+		if (time_after(jiffies, timeout)) {
+			pr_err("msm_otg: usb link reset timeout\n");
+			break;
+		}
+		usleep_range(1000, 1200);
+	} while (readl_relaxed(USB_USBCMD) & USBCMD_RESET);
+
+	/* select ULPI phy */
+	temp = (readl(USB_PORTSC) & ~PORTSC_PTS);
+	writel(temp | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	if (atomic_read(&dev->chg_type) !=
+				USB_CHG_TYPE__WALLCHARGER) {
+		rc = msm_otg_phy_caliberate(dev);
+		if (rc)
+			return rc;
+	}
+
+	/* TBD: There are two link resets. One is below and other one
+	 * is done immediately after this function. See if we can
+	 * eliminate one of these.
+	 */
+	writel(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	do {
+		if (time_after(jiffies, timeout)) {
+			pr_err("msm_otg: usb link reset timeout\n");
+			break;
+		}
+		msleep(1);
+	} while (readl(USB_USBCMD) & USBCMD_RESET);
+
+	if (readl(USB_USBCMD) & USBCMD_RESET) {
+		pr_err("%s: usb core reset failed\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void otg_reset(struct usb_phy *xceiv, int phy_reset)
+{
+	struct msm_otg *dev = container_of(xceiv, struct msm_otg, phy);
+	unsigned long timeout;
+	u32 mode, work = 0;
+
+	clk_prepare_enable(dev->alt_core_clk);
+
+	if (!phy_reset)
+		goto reset_link;
+
+	if (dev->pdata->phy_reset)
+		dev->pdata->phy_reset(dev->regs);
+	else
+		msm_otg_phy_reset(dev);
+
+	/*disable all phy interrupts*/
+	ulpi_write(dev, 0xFF, 0x0F);
+	ulpi_write(dev, 0xFF, 0x12);
+	msleep(100);
+
+reset_link:
+	writel(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	do {
+		if (time_after(jiffies, timeout)) {
+			pr_err("msm_otg: usb link reset timeout\n");
+			break;
+		}
+		msleep(1);
+	} while (readl(USB_USBCMD) & USBCMD_RESET);
+
+	/* select ULPI phy */
+	writel(0x80000000, USB_PORTSC);
+
+	set_pre_emphasis_level(dev);
+	set_hsdrv_slope(dev);
+	set_cdr_auto_reset(dev);
+	set_driver_amplitude(dev);
+	set_se1_gating(dev);
+
+	writel(0x0, USB_AHB_BURST);
+	writel(0x00, USB_AHB_MODE);
+	if (dev->pdata->bam_disable) {
+		writel_relaxed((readl_relaxed(USB_GEN_CONFIG) |
+					USB_BAM_DISABLE), USB_GEN_CONFIG);
+		pr_debug("%s(): USB_GEN_CONFIG = %x\n",
+				__func__, readl_relaxed(USB_GEN_CONFIG));
+	}
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+
+	clk_disable_unprepare(dev->alt_core_clk);
+
+	if ((xceiv->otg->gadget && xceiv->otg->gadget->is_a_peripheral) ||
+			test_bit(ID, &dev->inputs))
+		mode = USBMODE_SDIS | USBMODE_DEVICE;
+	else
+		mode = USBMODE_SDIS | USBMODE_HOST;
+	writel(mode, USB_USBMODE);
+
+	writel_relaxed((readl_relaxed(USB_OTGSC) | OTGSC_IDPU), USB_OTGSC);
+	if (dev->phy.otg->gadget) {
+		enable_sess_valid(dev);
+		/* Due to the above 100ms delay, interrupts from PHY are
+		 * sometimes missed during fast plug-in/plug-out of cable.
+		 * Check for such cases here.
+		 */
+		if (is_b_sess_vld() && !test_bit(B_SESS_VLD, &dev->inputs)) {
+			pr_debug("%s: handle missing BSV event\n", __func__);
+			set_bit(B_SESS_VLD, &dev->inputs);
+			work = 1;
+		} else if (!is_b_sess_vld() && test_bit(B_SESS_VLD,
+				&dev->inputs)) {
+			pr_debug("%s: handle missing !BSV event\n", __func__);
+			clear_bit(B_SESS_VLD, &dev->inputs);
+			work = 1;
+		}
+	}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+	if (dev->phy.otg->host && !dev->pmic_id_notif_supp) {
+		enable_idgnd(dev);
+		/* Handle missing ID_GND interrupts during fast PIPO */
+		if (is_host() && test_bit(ID, &dev->inputs)) {
+			pr_debug("%s: handle missing ID_GND event\n", __func__);
+			clear_bit(ID, &dev->inputs);
+			work = 1;
+		} else if (!is_host() && !test_bit(ID, &dev->inputs)) {
+			pr_debug("%s: handle missing !ID_GND event\n",
+						__func__);
+			set_bit(ID, &dev->inputs);
+			work = 1;
+		}
+	} else {
+		disable_idgnd(dev);
+	}
+#endif
+
+	enable_idabc(dev);
+
+	if (work) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+}
+
+static void msm_otg_sm_work(struct work_struct *w)
+{
+	struct msm_otg	*dev = container_of(w, struct msm_otg, sm_work);
+	enum chg_type	chg_type = atomic_read(&dev->chg_type);
+	int ret;
+	int work = 0;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	if (atomic_read(&dev->in_lpm))
+		msm_otg_set_suspend(&dev->phy, 0);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	switch (state) {
+	case OTG_STATE_UNDEFINED:
+
+		/*
+		 * We can come here when LPM fails with wall charger
+		 * connected. Change the state to B_PERIPHERAL and
+		 * schedule the work which takes care of resetting the
+		 * PHY and putting the hardware in low power mode.
+		 */
+		if (atomic_read(&dev->chg_type) ==
+				USB_CHG_TYPE__WALLCHARGER) {
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			work = 1;
+			break;
+		}
+
+		/* Reset both phy and link */
+		otg_reset(&dev->phy, 1);
+
+#ifdef CONFIG_USB_MSM_ACA
+		set_aca_id_inputs(dev);
+#endif
+		if (dev->pdata->otg_mode == OTG_USER_CONTROL) {
+			if ((dev->pdata->usb_mode == USB_PERIPHERAL_MODE) ||
+					!dev->phy.otg->host) {
+				set_bit(ID, &dev->inputs);
+				set_bit(B_SESS_VLD, &dev->inputs);
+			}
+		} else {
+			if (!dev->phy.otg->host || !is_host())
+				set_bit(ID, &dev->inputs);
+
+			if (dev->phy.otg->gadget && is_b_sess_vld())
+				set_bit(B_SESS_VLD, &dev->inputs);
+		}
+		spin_lock_irqsave(&dev->lock, flags);
+		if ((test_bit(ID, &dev->inputs)) &&
+				!test_bit(ID_A, &dev->inputs)) {
+			dev->phy.state = OTG_STATE_B_IDLE;
+		} else {
+			set_bit(A_BUS_REQ, &dev->inputs);
+			dev->phy.state = OTG_STATE_A_IDLE;
+		}
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		work = 1;
+		break;
+	case OTG_STATE_B_IDLE:
+		dev->phy.otg->default_a = 0;
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs)) {
+			pr_debug("!id || id_A\n");
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			otg_reset(&dev->phy, 0);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_set_power(&dev->phy, 0);
+			work = 1;
+		} else if (test_bit(B_SESS_VLD, &dev->inputs) &&
+				!test_bit(ID_B, &dev->inputs)) {
+			pr_debug("b_sess_vld\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_set_power(&dev->phy, 0);
+			msm_otg_start_peripheral(dev->phy.otg, 1);
+		} else if (test_bit(B_BUS_REQ, &dev->inputs)) {
+			pr_debug("b_sess_end && b_bus_req\n");
+			ret = msm_otg_start_srp(dev->phy.otg);
+			if (ret < 0) {
+				/* notify user space */
+				clear_bit(B_BUS_REQ, &dev->inputs);
+				work = 1;
+				break;
+			}
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_SRP_INIT;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_timer(dev, TB_SRP_FAIL, B_SRP_FAIL);
+			break;
+		} else if (test_bit(ID_B, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		} else {
+			msm_otg_set_power(&dev->phy, 0);
+			pr_debug("entering into lpm\n");
+			msm_otg_put_suspend(dev);
+
+			if (dev->pdata->ldo_set_voltage)
+				dev->pdata->ldo_set_voltage(3075);
+		}
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs) ||
+				test_bit(ID_C, &dev->inputs) ||
+				(test_bit(B_SESS_VLD, &dev->inputs) &&
+				!test_bit(ID_B, &dev->inputs))) {
+			pr_debug("!id || id_a/c || b_sess_vld+!id_b\n");
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			work = 1;
+		} else if (test_bit(B_SRP_FAIL, &dev->tmouts)) {
+			pr_debug("b_srp_fail\n");
+			/* notify user space */
+			msm_otg_send_event(dev->phy.otg,
+				OTG_EVENT_NO_RESP_FOR_SRP);
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			clear_bit(B_SRP_FAIL, &dev->tmouts);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			dev->b_last_se0_sess = jiffies;
+			work = 1;
+		}
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs) ||
+				test_bit(ID_B, &dev->inputs) ||
+				!test_bit(B_SESS_VLD, &dev->inputs)) {
+			pr_debug("!id  || id_a/b || !b_sess_vld\n");
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->b_last_se0_sess = jiffies;
+
+			/* Workaround: Reset phy after session */
+			otg_reset(&dev->phy, 1);
+			work = 1;
+		} else if (test_bit(B_BUS_REQ, &dev->inputs) &&
+				dev->phy.otg->gadget->b_hnp_enable &&
+				test_bit(A_BUS_SUSPEND, &dev->inputs)) {
+			pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n");
+			msm_otg_start_timer(dev, TB_ASE0_BRST, B_ASE0_BRST);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_WAIT_ACON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* start HCD even before A-device enable
+			 * pull-up to meet HNP timings.
+			 */
+			dev->phy.otg->host->is_b_host = 1;
+			msm_otg_start_host(dev->phy.otg, REQUEST_START);
+
+		} else if (test_bit(ID_C, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		} else if (chg_type == USB_CHG_TYPE__WALLCHARGER) {
+#ifdef CONFIG_USB_MSM_ACA
+			del_timer_sync(&dev->id_timer);
+#endif
+			/* Workaround: Reset PHY in SE1 state */
+			otg_reset(&dev->phy, 1);
+			pr_debug("entering into lpm with wall-charger\n");
+			msm_otg_put_suspend(dev);
+			/* Allow idle power collapse */
+			otg_pm_qos_update_latency(dev, 0);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if (!test_bit(ID, &dev->inputs) ||
+				test_bit(ID_A, &dev->inputs) ||
+				test_bit(ID_B, &dev->inputs) ||
+				!test_bit(B_SESS_VLD, &dev->inputs)) {
+			pr_debug("!id || id_a/b || !b_sess_vld\n");
+			msm_otg_del_timer(dev);
+			/* A-device is physically disconnected during
+			 * HNP. Remove HCD.
+			 */
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->phy.otg->host->is_b_host = 0;
+
+			clear_bit(B_BUS_REQ, &dev->inputs);
+			clear_bit(A_BUS_SUSPEND, &dev->inputs);
+			dev->b_last_se0_sess = jiffies;
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+
+			/* Workaround: Reset phy after session */
+			otg_reset(&dev->phy, 1);
+			work = 1;
+		} else if (test_bit(A_CONN, &dev->inputs)) {
+			pr_debug("a_conn\n");
+			clear_bit(A_BUS_SUSPEND, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_HOST;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (test_bit(ID_C, &dev->inputs)) {
+				atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+				msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+			}
+		} else if (test_bit(B_ASE0_BRST, &dev->tmouts)) {
+			/* TODO: A-device may send reset after
+			 * enabling HNP; a_bus_resume case is
+			 * not handled for now.
+			 */
+			pr_debug("b_ase0_brst_tmout\n");
+			msm_otg_send_event(dev->phy.otg,
+				OTG_EVENT_HNP_FAILED);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->phy.otg->host->is_b_host = 0;
+			clear_bit(B_ASE0_BRST, &dev->tmouts);
+			clear_bit(A_BUS_SUSPEND, &dev->inputs);
+			clear_bit(B_BUS_REQ, &dev->inputs);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 1);
+		} else if (test_bit(ID_C, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		/* B_BUS_REQ is not exposed to user space. So
+		 * it must be A_CONN for now.
+		 */
+		if (!test_bit(B_BUS_REQ, &dev->inputs) ||
+				!test_bit(A_CONN, &dev->inputs)) {
+			pr_debug("!b_bus_req || !a_conn\n");
+			clear_bit(A_CONN, &dev->inputs);
+			clear_bit(B_BUS_REQ, &dev->inputs);
+
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->phy.otg->host->is_b_host = 0;
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* Workaround: Reset phy after session */
+			otg_reset(&dev->phy, 1);
+			work = 1;
+		} else if (test_bit(ID_C, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+		}
+		break;
+	case OTG_STATE_A_IDLE:
+		dev->phy.otg->default_a = 1;
+		if (test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) {
+			pr_debug("id && !id_a\n");
+			dev->phy.otg->default_a = 0;
+			otg_reset(&dev->phy, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_B_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_set_power(&dev->phy, 0);
+			work = 1;
+		} else if (!test_bit(A_BUS_DROP, &dev->inputs) &&
+				(test_bit(A_SRP_DET, &dev->inputs) ||
+				 test_bit(A_BUS_REQ, &dev->inputs))) {
+			pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n");
+
+			clear_bit(A_SRP_DET, &dev->inputs);
+			/* Disable SRP detection */
+			writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) &
+					~OTGSC_DPIE, USB_OTGSC);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VRISE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* ACA: ID_A: Stop charging untill enumeration */
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(&dev->phy, 0);
+			else
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+			msm_otg_start_timer(dev, TA_WAIT_VRISE, A_WAIT_VRISE);
+			/* no need to schedule work now */
+		} else {
+			pr_debug("No session requested\n");
+
+			/* A-device is not providing power on VBUS.
+			 * Enable SRP detection.
+			 */
+			writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) |
+					OTGSC_DPIE, USB_OTGSC);
+			msm_otg_put_suspend(dev);
+
+		}
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_WAIT_VRISE, &dev->tmouts)) {
+			pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n");
+			clear_bit(A_BUS_REQ, &dev->inputs);
+			msm_otg_del_timer(dev);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("a_vbus_vld\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			/* Start HCD to detect peripherals. */
+			msm_otg_start_host(dev->phy.otg, REQUEST_START);
+		}
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_WAIT_BCON, &dev->tmouts)) {
+			pr_debug("id_f/b/c || a_bus_drop ||"
+					"a_wait_bcon_tmout\n");
+			if (test_bit(A_WAIT_BCON, &dev->tmouts))
+				msm_otg_send_event(dev->phy.otg,
+					OTG_EVENT_DEV_CONN_TMOUT);
+			msm_otg_del_timer(dev);
+			clear_bit(A_BUS_REQ, &dev->inputs);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			/* ACA: ID_A with NO accessory, just the A plug is
+			 * attached to ACA: Use IDCHG_MAX for charging
+			 */
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(&dev->phy, USB_IDCHG_MAX);
+			else
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (test_bit(B_CONN, &dev->inputs)) {
+			pr_debug("b_conn\n");
+			msm_otg_del_timer(dev);
+			/* HCD is added already. just move to
+			 * A_HOST state.
+			 */
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_HOST;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (test_bit(ID_A, &dev->inputs)) {
+				atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+				msm_otg_set_power(&dev->phy,
+					USB_IDCHG_MIN - get_aca_bmaxpower(dev));
+			}
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			msm_otg_del_timer(dev);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+		} else if (!test_bit(ID, &dev->inputs)) {
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs)) {
+			pr_debug("id_f/b/c || a_bus_drop\n");
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			/* no work */
+		} else if (!test_bit(A_BUS_REQ, &dev->inputs)) {
+			/* a_bus_req is de-asserted when root hub is
+			 * suspended or HNP is in progress.
+			 */
+			pr_debug("!a_bus_req\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_SUSPEND;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (dev->phy.otg->host->b_hnp_enable) {
+				msm_otg_start_timer(dev, TA_AIDL_BDIS,
+						A_AIDL_BDIS);
+			} else {
+				/* No HNP. Root hub suspended */
+				msm_otg_put_suspend(dev);
+			}
+			if (test_bit(ID_A, &dev->inputs))
+				msm_otg_set_power(&dev->phy,
+						USB_IDCHG_MIN - USB_IB_UNCFG);
+		} else if (!test_bit(B_CONN, &dev->inputs)) {
+			pr_debug("!b_conn\n");
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_set_power(&dev->phy,
+					USB_IDCHG_MIN - get_aca_bmaxpower(dev));
+		} else if (!test_bit(ID, &dev->inputs)) {
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__INVALID);
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_AIDL_BDIS, &dev->tmouts)) {
+			pr_debug("id_f/b/c || a_bus_drop ||"
+					"a_aidl_bdis_tmout\n");
+			if (test_bit(A_AIDL_BDIS, &dev->tmouts))
+				msm_otg_send_event(dev->phy.otg,
+					OTG_EVENT_HNP_FAILED);
+			msm_otg_del_timer(dev);
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			msm_otg_del_timer(dev);
+			clear_bit(B_CONN, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+		} else if (!test_bit(B_CONN, &dev->inputs) &&
+				dev->phy.otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && b_hnp_enable");
+			/* Clear AIDL_BDIS timer */
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_PERIPHERAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+
+			msm_otg_start_host(dev->phy.otg, REQUEST_HNP_SUSPEND);
+
+			/* We may come here even when B-dev is physically
+			 * disconnected during HNP. We go back to host
+			 * role if bus is idle for BIDL_ADIS time.
+			 */
+			dev->phy.otg->gadget->is_a_peripheral = 1;
+			msm_otg_start_peripheral(dev->phy.otg, 1);
+			/* If ID_A: we can charge in a_peripheral as well */
+			if (test_bit(ID_A, &dev->inputs)) {
+				atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+				msm_otg_set_power(&dev->phy,
+					 USB_IDCHG_MIN - USB_IB_UNCFG);
+			}
+		} else if (!test_bit(B_CONN, &dev->inputs) &&
+				!dev->phy.otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && !b_hnp_enable");
+			/* bus request is dropped during suspend.
+			 * acquire again for next device.
+			 */
+			set_bit(A_BUS_REQ, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy,
+					 USB_IDCHG_MIN - USB_IB_UNCFG);
+		} else if (!test_bit(ID, &dev->inputs)) {
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs)) {
+			pr_debug("id _f/b/c || a_bus_drop\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->phy.otg->gadget->is_a_peripheral = 0;
+			/* HCD was suspended before. Stop it now */
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+
+			/* Reset both phy and link */
+			otg_reset(&dev->phy, 1);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (!test_bit(A_VBUS_VLD, &dev->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_VBUS_ERR;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->phy.otg->gadget->is_a_peripheral = 0;
+			/* HCD was suspended before. Stop it now */
+			msm_otg_start_host(dev->phy.otg, REQUEST_STOP);
+		} else if (test_bit(A_BIDL_ADIS, &dev->tmouts)) {
+			pr_debug("a_bidl_adis_tmout\n");
+			msm_otg_start_peripheral(dev->phy.otg, 0);
+			dev->phy.otg->gadget->is_a_peripheral = 0;
+
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_BCON;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			set_bit(A_BUS_REQ, &dev->inputs);
+			msm_otg_start_host(dev->phy.otg, REQUEST_HNP_RESUME);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(dev, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			msm_otg_set_power(&dev->phy, 0);
+		} else if (test_bit(ID_A, &dev->inputs)) {
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			atomic_set(&dev->chg_type, USB_CHG_TYPE__SDP);
+			msm_otg_set_power(&dev->phy,
+					 USB_IDCHG_MIN - USB_IB_UNCFG);
+		} else if (!test_bit(ID, &dev->inputs)) {
+			msm_otg_set_power(&dev->phy, 0);
+			dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1);
+		}
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (test_bit(A_WAIT_VFALL, &dev->tmouts)) {
+			clear_bit(A_VBUS_VLD, &dev->inputs);
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_IDLE;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			work = 1;
+		}
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if ((test_bit(ID, &dev->inputs) &&
+				!test_bit(ID_A, &dev->inputs)) ||
+				test_bit(A_BUS_DROP, &dev->inputs) ||
+				test_bit(A_CLR_ERR, &dev->inputs)) {
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->phy.state = OTG_STATE_A_WAIT_VFALL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (!test_bit(ID_A, &dev->inputs))
+				dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+			msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL);
+			msm_otg_set_power(&dev->phy, 0);
+		}
+		break;
+	default:
+		pr_err("invalid OTG state\n");
+	}
+
+	if (work)
+		queue_work(dev->wq, &dev->sm_work);
+
+#ifdef CONFIG_USB_MSM_ACA
+	/* Start id_polling if (ID_FLOAT&BSV) || ID_A/B/C */
+	if ((test_bit(ID, &dev->inputs) &&
+			test_bit(B_SESS_VLD, &dev->inputs) &&
+			chg_type != USB_CHG_TYPE__WALLCHARGER) ||
+			test_bit(ID_A, &dev->inputs)) {
+		mod_timer(&dev->id_timer, jiffies +
+				 msecs_to_jiffies(OTG_ID_POLL_MS));
+		return;
+	}
+	del_timer(&dev->id_timer);
+#endif
+	/* IRQ/sysfs may queue work. Check work_pending. otherwise
+	 * we might endup releasing wakelock after it is acquired
+	 * in IRQ/sysfs.
+	 */
+	if (!work_pending(&dev->sm_work) && !hrtimer_active(&dev->timer) &&
+			!work_pending(&dev->otg_resume_work))
+		wake_unlock(&dev->wlock);
+}
+
+#ifdef CONFIG_USB_MSM_ACA
+static void msm_otg_id_func(unsigned long _dev)
+{
+	struct msm_otg	*dev = (struct msm_otg *) _dev;
+	u8		phy_ints;
+
+#ifdef CONFIG_USB_MSM_STANDARD_ACA
+	/*
+	 * When standard ACA is attached RID_A and RID_GND states are only
+	 * possible.  RID_A-->RID_GND transition generates IdGnd interrupt
+	 * from PHY.  Hence polling is disabled.
+	 */
+	if (test_bit(ID_A, &dev->inputs))
+		goto out;
+#endif
+
+	if (atomic_read(&dev->in_lpm))
+		msm_otg_set_suspend(&dev->phy, 0);
+
+	phy_ints = ulpi_read(dev, 0x13);
+
+	/*
+	 * ACA timer will be kicked again after the PHY
+	 * state is recovered.
+	 */
+	if (phy_ints == -ETIMEDOUT)
+		return;
+
+
+	/* If id_gnd happened then stop and let isr take care of this */
+	if (phy_id_state_gnd(phy_ints))
+		goto out;
+
+	if ((test_bit(ID_A, &dev->inputs) == phy_id_state_a(phy_ints)) &&
+	    (test_bit(ID_B, &dev->inputs) == phy_id_state_b(phy_ints)) &&
+	    (test_bit(ID_C, &dev->inputs) == phy_id_state_c(phy_ints))) {
+		mod_timer(&dev->id_timer,
+				jiffies + msecs_to_jiffies(OTG_ID_POLL_MS));
+		goto out;
+	} else {
+		set_aca_id_inputs(dev);
+	}
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+out:
+	/* OOPS: runing while !BSV, schedule work to initiate LPM */
+	if (!is_b_sess_vld()) {
+		clear_bit(B_SESS_VLD, &dev->inputs);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+	return;
+}
+#endif
+#ifdef CONFIG_USB_OTG
+static ssize_t
+set_pwr_down(struct device *_dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct msm_otg *dev = the_msm_otg;
+	int value;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* Applicable for only A-Device */
+	if (state <= OTG_STATE_A_IDLE)
+		return -EINVAL;
+
+	sscanf(buf, "%d", &value);
+
+	if (test_bit(A_BUS_DROP, &dev->inputs) != !!value) {
+		change_bit(A_BUS_DROP, &dev->inputs);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(pwr_down, S_IRUGO | S_IWUSR, NULL, set_pwr_down);
+
+static ssize_t
+set_srp_req(struct device *_dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct msm_otg *dev = the_msm_otg;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state != OTG_STATE_B_IDLE)
+		return -EINVAL;
+
+	set_bit(B_BUS_REQ, &dev->inputs);
+	wake_lock(&dev->wlock);
+	queue_work(dev->wq, &dev->sm_work);
+
+	return count;
+}
+static DEVICE_ATTR(srp_req, S_IRUGO | S_IWUSR, NULL, set_srp_req);
+
+static ssize_t
+set_clr_err(struct device *_dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct msm_otg *dev = the_msm_otg;
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	state = dev->phy.state;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (state == OTG_STATE_A_VBUS_ERR) {
+		set_bit(A_CLR_ERR, &dev->inputs);
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(clr_err, S_IRUGO | S_IWUSR, NULL, set_clr_err);
+
+static struct attribute *msm_otg_attrs[] = {
+	&dev_attr_pwr_down.attr,
+	&dev_attr_srp_req.attr,
+	&dev_attr_clr_err.attr,
+	NULL,
+};
+
+static struct attribute_group msm_otg_attr_grp = {
+	.attrs = msm_otg_attrs,
+};
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static int otg_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+static ssize_t otg_mode_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct msm_otg *dev = file->private_data;
+	int ret = count;
+	int work = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->pdata->otg_mode = OTG_USER_CONTROL;
+	if (!memcmp(buf, "none", 4)) {
+		clear_bit(B_SESS_VLD, &dev->inputs);
+		set_bit(ID, &dev->inputs);
+		work = 1;
+	} else if (!memcmp(buf, "peripheral", 10)) {
+		set_bit(B_SESS_VLD, &dev->inputs);
+		set_bit(ID, &dev->inputs);
+		work = 1;
+	} else if (!memcmp(buf, "host", 4)) {
+		clear_bit(B_SESS_VLD, &dev->inputs);
+		clear_bit(ID, &dev->inputs);
+		set_bit(A_BUS_REQ, &dev->inputs);
+		work = 1;
+	} else {
+		pr_info("%s: unknown mode specified\n", __func__);
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (work) {
+		wake_lock(&dev->wlock);
+		queue_work(dev->wq, &dev->sm_work);
+	}
+
+	return ret;
+}
+const struct file_operations otgfs_fops = {
+	.open	= otg_open,
+	.write	= otg_mode_write,
+};
+
+#define OTG_INFO_SIZE 512
+static ssize_t otg_info_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char *buf;
+	int temp = 0;
+	int ret;
+	struct msm_otg *dev = file->private_data;
+
+	buf = kzalloc(sizeof(char) * OTG_INFO_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	temp += scnprintf(buf + temp, OTG_INFO_SIZE - temp,
+			"OTG State:             %s\n"
+			"OTG Mode:              %d\n"
+			"OTG Inputs:            0x%lx\n"
+			"Charger Type:          %d\n"
+			"PMIC VBUS Support:     %u\n"
+			"PMIC ID Support:       %u\n"
+			"USB In SPS:            %d\n"
+			"pre_emphasis_level:    0x%x\n"
+			"cdr_auto_reset:        0x%x\n"
+			"hs_drv_amplitude:      0x%x\n"
+			"se1_gate_state:        0x%x\n"
+			"swfi_latency:          0x%x\n"
+			"PHY Powercollapse:     0x%x\n",
+			state_string(dev->phy.state),
+			dev->pdata->otg_mode,
+			dev->inputs,
+			atomic_read(&dev->chg_type),
+			dev->pmic_vbus_notif_supp,
+			dev->pmic_id_notif_supp,
+			dev->pdata->usb_in_sps,
+			dev->pdata->pemp_level,
+			dev->pdata->cdr_autoreset,
+			dev->pdata->drv_ampl,
+			dev->pdata->se1_gating,
+			dev->pdata->swfi_latency,
+			dev->pdata->phy_can_powercollapse);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+const struct file_operations otgfs_info_fops = {
+	.open	= otg_open,
+	.read	= otg_info_read,
+};
+
+struct dentry *otg_debug_root;
+struct dentry *otg_debug_mode;
+struct dentry *otg_debug_info;
+#endif
+
+static int otg_debugfs_init(struct msm_otg *dev)
+{
+#ifdef CONFIG_DEBUG_FS
+	otg_debug_root = debugfs_create_dir("otg", NULL);
+	if (!otg_debug_root)
+		return -ENOENT;
+
+	otg_debug_mode = debugfs_create_file("mode", 0222,
+						otg_debug_root, dev,
+						&otgfs_fops);
+	if (!otg_debug_mode)
+		goto free_root;
+
+	otg_debug_info = debugfs_create_file("info", 0444,
+						otg_debug_root, dev,
+						&otgfs_info_fops);
+	if (!otg_debug_info)
+		goto free_mode;
+
+	return 0;
+
+free_mode:
+	debugfs_remove(otg_debug_mode);
+	otg_debug_mode = NULL;
+
+free_root:
+	debugfs_remove(otg_debug_root);
+	otg_debug_root = NULL;
+	return -ENOENT;
+#endif
+	return 0;
+}
+
+static void otg_debugfs_cleanup(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(otg_debug_info);
+	debugfs_remove(otg_debug_mode);
+	debugfs_remove(otg_debug_root);
+#endif
+}
+
+struct usb_phy_io_ops msm_otg_io_ops = {
+	.read = usb_ulpi_read,
+	.write = usb_ulpi_write,
+};
+
+static int __init msm_otg_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+	struct msm_otg *dev;
+
+	dev = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+	if (!dev->phy.otg) {
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	the_msm_otg = dev;
+	dev->phy.dev = &pdev->dev;
+	dev->phy.otg->phy = &dev->phy;
+	dev->pdata = pdev->dev.platform_data;
+
+	if (!dev->pdata) {
+		ret = -ENODEV;
+		goto free_dev;
+	}
+
+#ifdef CONFIG_USB_EHCI_MSM_72K
+	if (!dev->pdata->vbus_power) {
+		ret = -ENODEV;
+		goto free_dev;
+	} else
+		dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0);
+
+#endif
+
+	if (dev->pdata->rpc_connect) {
+		ret = dev->pdata->rpc_connect(1);
+		pr_debug("%s: rpc_connect(%d)\n", __func__, ret);
+		if (ret) {
+			pr_err("%s: rpc connect failed\n", __func__);
+			ret = -ENODEV;
+			goto free_dev;
+		}
+	}
+
+	dev->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk");
+	if (IS_ERR(dev->alt_core_clk)) {
+		pr_err("%s: failed to get alt_core_clk\n", __func__);
+		ret = PTR_ERR(dev->alt_core_clk);
+		goto rpc_fail;
+	}
+	clk_set_rate(dev->alt_core_clk, 60000000);
+
+	/* pm qos request to prevent apps idle power collapse */
+	pm_qos_add_request(&dev->pdata->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY,
+			   PM_QOS_DEFAULT_VALUE);
+
+	dev->core_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(dev->core_clk)) {
+		pr_err("%s: failed to get core_clk\n", __func__);
+		ret = PTR_ERR(dev->core_clk);
+		goto put_alt_core_clk;
+	}
+	/* CORE clk must be running at >60Mhz for correct HSUSB operation
+	 * and USB core cannot tolerate frequency changes on CORE CLK.
+	 * Vote for maximum clk frequency for CORE clock.
+	 */
+	clk_set_rate(dev->core_clk, INT_MAX);
+
+	clk_prepare_enable(dev->core_clk);
+
+	if (!dev->pdata->pclk_is_hw_gated) {
+		dev->iface_clk = clk_get(&pdev->dev, "iface_clk");
+		if (IS_ERR(dev->iface_clk)) {
+			pr_err("%s: failed to get abh_clk\n", __func__);
+			ret = PTR_ERR(dev->iface_clk);
+			goto put_core_clk;
+		}
+		clk_prepare_enable(dev->iface_clk);
+	}
+
+	if (!dev->pdata->phy_reset) {
+		dev->phy_reset_clk = clk_get(&pdev->dev, "phy_clk");
+		if (IS_ERR(dev->phy_reset_clk)) {
+			pr_err("%s: failed to get phy_clk\n", __func__);
+			ret = PTR_ERR(dev->phy_reset_clk);
+			goto put_iface_clk;
+		}
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_err("%s: failed to get platform resource mem\n", __func__);
+		ret = -ENODEV;
+		goto put_phy_clk;
+	}
+
+	dev->regs = ioremap(res->start, resource_size(res));
+	if (!dev->regs) {
+		pr_err("%s: ioremap failed\n", __func__);
+		ret = -ENOMEM;
+		goto put_phy_clk;
+	}
+	dev->irq = platform_get_irq(pdev, 0);
+	if (!dev->irq) {
+		pr_err("%s: platform_get_irq failed\n", __func__);
+		ret = -ENODEV;
+		goto free_regs;
+	}
+	dev->xo_handle = msm_xo_get(MSM_XO_TCXO_D1, "usb");
+	if (IS_ERR(dev->xo_handle)) {
+		pr_err(" %s not able to get the handle"
+			"to vote for TCXO D1 buffer\n", __func__);
+		ret = PTR_ERR(dev->xo_handle);
+		goto free_regs;
+	}
+
+	ret = msm_xo_mode_vote(dev->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		pr_err("%s failed to vote for TCXO"
+			"D1 buffer%d\n", __func__, ret);
+		goto free_xo_handle;
+	}
+
+
+	msm_otg_init_timer(dev);
+	INIT_WORK(&dev->sm_work, msm_otg_sm_work);
+	INIT_WORK(&dev->otg_resume_work, msm_otg_resume_w);
+	spin_lock_init(&dev->lock);
+	wake_lock_init(&dev->wlock, WAKE_LOCK_SUSPEND, "msm_otg");
+
+	dev->wq = alloc_workqueue("k_otg", WQ_NON_REENTRANT, 0);
+	if (!dev->wq) {
+		ret = -ENOMEM;
+		goto free_wlock;
+	}
+
+	if (dev->pdata->init_gpio) {
+		ret = dev->pdata->init_gpio(1);
+		if (ret) {
+			pr_err("%s: gpio init failed with err:%d\n",
+					__func__, ret);
+			goto free_wq;
+		}
+	}
+	/* To reduce phy power consumption and to avoid external LDO
+	 * on the board, PMIC comparators can be used to detect VBUS
+	 * session change.
+	 */
+	if (dev->pdata->pmic_vbus_notif_init) {
+		ret = dev->pdata->pmic_vbus_notif_init
+			(&msm_otg_set_vbus_state, 1);
+		if (!ret) {
+			dev->pmic_vbus_notif_supp = 1;
+		} else if (ret != -ENOTSUPP) {
+			pr_err("%s: pmic_vbus_notif_init() failed, err:%d\n",
+					__func__, ret);
+			goto free_gpio;
+		}
+	}
+
+	if (dev->pdata->phy_id_setup_init) {
+		ret = dev->pdata->phy_id_setup_init(1);
+		if (ret) {
+			pr_err("%s: phy_id_setup_init failed err:%d",
+					__func__, ret);
+			goto free_pmic_vbus_notif;
+		}
+	}
+
+	if (dev->pdata->pmic_vbus_irq)
+		dev->vbus_on_irq = dev->pdata->pmic_vbus_irq;
+
+	/* vote for vddcx, as PHY cannot tolerate vddcx below 1.0V */
+	if (dev->pdata->init_vddcx) {
+		ret = dev->pdata->init_vddcx(1);
+		if (ret) {
+			pr_err("%s: unable to enable vddcx digital core:%d\n",
+				__func__, ret);
+			goto free_phy_id_setup;
+		}
+	}
+
+	if (dev->pdata->ldo_init) {
+		ret = dev->pdata->ldo_init(1);
+		if (ret) {
+			pr_err("%s: ldo_init failed with err:%d\n",
+					__func__, ret);
+			goto free_config_vddcx;
+		}
+	}
+
+	if (dev->pdata->ldo_enable) {
+		ret = dev->pdata->ldo_enable(1);
+		if (ret) {
+			pr_err("%s: ldo_enable failed with err:%d\n",
+					__func__, ret);
+			goto free_ldo_init;
+		}
+	}
+
+
+	/* ACk all pending interrupts and clear interrupt enable registers */
+	writel((readl(USB_OTGSC) & ~OTGSC_INTR_MASK), USB_OTGSC);
+	writel(readl(USB_USBSTS), USB_USBSTS);
+	writel(0, USB_USBINTR);
+	/* Ensure that above STOREs are completed before enabling interrupts */
+	mb();
+
+	ret = request_irq(dev->irq, msm_otg_irq, IRQF_SHARED,
+					"msm_otg", dev);
+	if (ret) {
+		pr_err("%s: request irq failed\n", __func__);
+		goto free_ldo_enable;
+	}
+
+	dev->phy.set_suspend = msm_otg_set_suspend;
+	dev->phy.set_power = msm_otg_set_power;
+
+	dev->phy.otg->set_peripheral = msm_otg_set_peripheral;
+#ifdef CONFIG_USB_EHCI_MSM_72K
+	dev->phy.otg->set_host = msm_otg_set_host;
+#endif
+	dev->phy.otg->start_hnp = msm_otg_start_hnp;
+	dev->phy.otg->send_event = msm_otg_send_event;
+	dev->set_clk = msm_otg_set_clk;
+	dev->reset = otg_reset;
+	dev->phy.io_ops = &msm_otg_io_ops;
+	if (usb_set_transceiver(&dev->phy)) {
+		WARN_ON(1);
+		goto free_otg_irq;
+	}
+#ifdef CONFIG_USB_MSM_ACA
+	/* Link doesnt support id_a/b/c interrupts, hence polling
+	 * needs to be done to support ACA charger
+	 */
+	init_timer(&dev->id_timer);
+	dev->id_timer.function = msm_otg_id_func;
+	dev->id_timer.data = (unsigned long) dev;
+#endif
+
+	atomic_set(&dev->chg_type, USB_CHG_TYPE__INVALID);
+	if (dev->pdata->chg_init && dev->pdata->chg_init(1))
+		pr_err("%s: chg_init failed\n", __func__);
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	ret = pm_runtime_set_active(&pdev->dev);
+	if (ret < 0)
+		pr_err("%s: pm_runtime: Fail to set active\n", __func__);
+
+	ret = 0;
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get(&pdev->dev);
+
+
+	ret = otg_debugfs_init(dev);
+	if (ret) {
+		pr_err("%s: otg_debugfs_init failed\n", __func__);
+		goto chg_deinit;
+	}
+
+#ifdef CONFIG_USB_OTG
+	ret = sysfs_create_group(&pdev->dev.kobj, &msm_otg_attr_grp);
+	if (ret < 0) {
+		pr_err("%s: Failed to create the sysfs entry\n", __func__);
+		otg_debugfs_cleanup();
+		goto chg_deinit;
+	}
+#endif
+
+
+	return 0;
+
+chg_deinit:
+	if (dev->pdata->chg_init)
+		dev->pdata->chg_init(0);
+free_otg_irq:
+	free_irq(dev->irq, dev);
+free_ldo_enable:
+	if (dev->pdata->ldo_enable)
+		dev->pdata->ldo_enable(0);
+	if (dev->pdata->setup_gpio)
+		dev->pdata->setup_gpio(USB_SWITCH_DISABLE);
+free_ldo_init:
+	if (dev->pdata->ldo_init)
+		dev->pdata->ldo_init(0);
+free_config_vddcx:
+	if (dev->pdata->init_vddcx)
+		dev->pdata->init_vddcx(0);
+free_phy_id_setup:
+	if (dev->pdata->phy_id_setup_init)
+		dev->pdata->phy_id_setup_init(0);
+free_pmic_vbus_notif:
+	if (dev->pdata->pmic_vbus_notif_init && dev->pmic_vbus_notif_supp)
+		dev->pdata->pmic_vbus_notif_init(&msm_otg_set_vbus_state, 0);
+free_gpio:
+	if (dev->pdata->init_gpio)
+		dev->pdata->init_gpio(0);
+free_wq:
+	destroy_workqueue(dev->wq);
+free_wlock:
+	wake_lock_destroy(&dev->wlock);
+free_xo_handle:
+	msm_xo_put(dev->xo_handle);
+free_regs:
+	iounmap(dev->regs);
+put_phy_clk:
+	if (dev->phy_reset_clk)
+		clk_put(dev->phy_reset_clk);
+put_iface_clk:
+	if (dev->iface_clk) {
+		clk_disable_unprepare(dev->iface_clk);
+		clk_put(dev->iface_clk);
+	}
+put_core_clk:
+	clk_disable_unprepare(dev->core_clk);
+	clk_put(dev->core_clk);
+put_alt_core_clk:
+	clk_put(dev->alt_core_clk);
+rpc_fail:
+	if (dev->pdata->rpc_connect)
+		dev->pdata->rpc_connect(0);
+free_dev:
+	kfree(dev->phy.otg);
+	kfree(dev);
+	return ret;
+}
+
+static int __exit msm_otg_remove(struct platform_device *pdev)
+{
+	struct msm_otg *dev = the_msm_otg;
+
+	otg_debugfs_cleanup();
+#ifdef CONFIG_USB_OTG
+	sysfs_remove_group(&pdev->dev.kobj, &msm_otg_attr_grp);
+#endif
+	destroy_workqueue(dev->wq);
+	wake_lock_destroy(&dev->wlock);
+
+	if (dev->pdata->setup_gpio)
+		dev->pdata->setup_gpio(USB_SWITCH_DISABLE);
+
+	if (dev->pdata->init_vddcx)
+		dev->pdata->init_vddcx(0);
+	if (dev->pdata->ldo_enable)
+		dev->pdata->ldo_enable(0);
+
+	if (dev->pdata->ldo_init)
+		dev->pdata->ldo_init(0);
+
+	if (dev->pmic_vbus_notif_supp)
+		dev->pdata->pmic_vbus_notif_init(&msm_otg_set_vbus_state, 0);
+
+	if (dev->pdata->phy_id_setup_init)
+		dev->pdata->phy_id_setup_init(0);
+
+	if (dev->pmic_id_notif_supp)
+		dev->pdata->pmic_id_notif_init(&msm_otg_set_id_state, 0);
+
+#ifdef CONFIG_USB_MSM_ACA
+	del_timer_sync(&dev->id_timer);
+#endif
+	if (dev->pdata->chg_init)
+		dev->pdata->chg_init(0);
+	free_irq(dev->irq, pdev);
+	iounmap(dev->regs);
+	clk_disable_unprepare(dev->core_clk);
+	clk_put(dev->core_clk);
+	if (dev->iface_clk) {
+		clk_disable_unprepare(dev->iface_clk);
+		clk_put(dev->iface_clk);
+	}
+	if (dev->alt_core_clk)
+		clk_put(dev->alt_core_clk);
+	if (dev->phy_reset_clk)
+		clk_put(dev->phy_reset_clk);
+	if (dev->pdata->rpc_connect)
+		dev->pdata->rpc_connect(0);
+	msm_xo_put(dev->xo_handle);
+	pm_qos_remove_request(&dev->pdata->pm_qos_req_dma);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	kfree(dev->phy.otg);
+	kfree(dev);
+	return 0;
+}
+
+static int msm_otg_runtime_suspend(struct device *dev)
+{
+	struct msm_otg *otg = the_msm_otg;
+
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	msm_otg_suspend(otg);
+	return  0;
+}
+
+static int msm_otg_runtime_resume(struct device *dev)
+{
+	struct msm_otg *otg = the_msm_otg;
+
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	msm_otg_resume(otg);
+	return  0;
+}
+
+static int msm_otg_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: idling...\n");
+	return  0;
+}
+
+static struct dev_pm_ops msm_otg_dev_pm_ops = {
+	.runtime_suspend = msm_otg_runtime_suspend,
+	.runtime_resume = msm_otg_runtime_resume,
+	.runtime_idle = msm_otg_runtime_idle,
+};
+
+static struct platform_driver msm_otg_driver = {
+	.remove = __exit_p(msm_otg_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = &msm_otg_dev_pm_ops,
+	},
+};
+
+static int __init msm_otg_init(void)
+{
+	return platform_driver_probe(&msm_otg_driver, msm_otg_probe);
+}
+
+static void __exit msm_otg_exit(void)
+{
+	platform_driver_unregister(&msm_otg_driver);
+}
+
+module_init(msm_otg_init);
+module_exit(msm_otg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM usb transceiver driver");
+MODULE_VERSION("1.00");
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 1d0347c..59f01f6 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,11 +9,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
  */
 
 #include <linux/module.h>
@@ -30,23 +25,32 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
 
 #include <linux/usb.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/ulpi.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
 #include <linux/usb/msm_hsusb.h>
 #include <linux/usb/msm_hsusb_hw.h>
 #include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8921-charger.h>
+#include <linux/mfd/pm8xxx/misc.h>
+#include <linux/power_supply.h>
 
 #include <mach/clk.h>
+#include <mach/msm_xo.h>
+#include <mach/msm_bus.h>
+#include <mach/rpm-regulator.h>
 
 #define MSM_USB_BASE	(motg->regs)
 #define DRIVER_NAME	"msm_otg"
 
+#define ID_TIMER_FREQ		(jiffies + msecs_to_jiffies(500))
 #define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
-
 #define USB_PHY_3P3_VOL_MIN	3050000 /* uV */
 #define USB_PHY_3P3_VOL_MAX	3300000 /* uV */
 #define USB_PHY_3P3_HPM_LOAD	50000	/* uA */
@@ -57,61 +61,58 @@
 #define USB_PHY_1P8_HPM_LOAD	50000	/* uA */
 #define USB_PHY_1P8_LPM_LOAD	4000	/* uA */
 
-#define USB_PHY_VDD_DIG_VOL_MIN	1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_NONE	0 /*uV */
+#define USB_PHY_VDD_DIG_VOL_MIN	1045000 /* uV */
 #define USB_PHY_VDD_DIG_VOL_MAX	1320000 /* uV */
 
+static DECLARE_COMPLETION(pmic_vbus_init);
+static struct msm_otg *the_msm_otg;
+static bool debug_aca_enabled;
+static bool debug_bus_voting_enabled;
+
 static struct regulator *hsusb_3p3;
 static struct regulator *hsusb_1p8;
 static struct regulator *hsusb_vddcx;
+static struct regulator *vbus_otg;
+static struct regulator *mhl_analog_switch;
+static struct power_supply *psy;
 
-static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init)
+static bool aca_id_turned_on;
+static inline bool aca_enabled(void)
 {
-	int ret = 0;
-
-	if (init) {
-		hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX");
-		if (IS_ERR(hsusb_vddcx)) {
-			dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
-			return PTR_ERR(hsusb_vddcx);
-		}
-
-		ret = regulator_set_voltage(hsusb_vddcx,
-				USB_PHY_VDD_DIG_VOL_MIN,
-				USB_PHY_VDD_DIG_VOL_MAX);
-		if (ret) {
-			dev_err(motg->phy.dev, "unable to set the voltage "
-					"for hsusb vddcx\n");
-			regulator_put(hsusb_vddcx);
-			return ret;
-		}
-
-		ret = regulator_enable(hsusb_vddcx);
-		if (ret) {
-			dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n");
-			regulator_put(hsusb_vddcx);
-		}
-	} else {
-		ret = regulator_set_voltage(hsusb_vddcx, 0,
-			USB_PHY_VDD_DIG_VOL_MAX);
-		if (ret)
-			dev_err(motg->phy.dev, "unable to set the voltage "
-					"for hsusb vddcx\n");
-		ret = regulator_disable(hsusb_vddcx);
-		if (ret)
-			dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n");
-
-		regulator_put(hsusb_vddcx);
-	}
-
-	return ret;
+#ifdef CONFIG_USB_MSM_ACA
+	return true;
+#else
+	return debug_aca_enabled;
+#endif
 }
 
+enum usb_vdd_value {
+	VDD_NONE = 0,
+	VDD_MIN,
+	VDD_MAX,
+	VDD_VAL_MAX,
+};
+
+static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
+		{  /* VDD_CX CORNER Voting */
+			[VDD_NONE]	= RPM_VREG_CORNER_NONE,
+			[VDD_MIN]	= RPM_VREG_CORNER_NOMINAL,
+			[VDD_MAX]	= RPM_VREG_CORNER_HIGH,
+		},
+		{ /* VDD_CX Voltage Voting */
+			[VDD_NONE]	= USB_PHY_VDD_DIG_VOL_NONE,
+			[VDD_MIN]	= USB_PHY_VDD_DIG_VOL_MIN,
+			[VDD_MAX]	= USB_PHY_VDD_DIG_VOL_MAX,
+		},
+};
+
 static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
 {
 	int rc = 0;
 
 	if (init) {
-		hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3");
+		hsusb_3p3 = devm_regulator_get(motg->phy.dev, "HSUSB_3p3");
 		if (IS_ERR(hsusb_3p3)) {
 			dev_err(motg->phy.dev, "unable to get hsusb 3p3\n");
 			return PTR_ERR(hsusb_3p3);
@@ -120,60 +121,43 @@
 		rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN,
 				USB_PHY_3P3_VOL_MAX);
 		if (rc) {
-			dev_err(motg->phy.dev, "unable to set voltage level "
-					"for hsusb 3p3\n");
-			goto put_3p3;
+			dev_err(motg->phy.dev, "unable to set voltage level for"
+					"hsusb 3p3\n");
+			return rc;
 		}
-		rc = regulator_enable(hsusb_3p3);
-		if (rc) {
-			dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n");
-			goto put_3p3;
-		}
-		hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8");
+		hsusb_1p8 = devm_regulator_get(motg->phy.dev, "HSUSB_1p8");
 		if (IS_ERR(hsusb_1p8)) {
 			dev_err(motg->phy.dev, "unable to get hsusb 1p8\n");
 			rc = PTR_ERR(hsusb_1p8);
-			goto disable_3p3;
+			goto put_3p3_lpm;
 		}
 		rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN,
 				USB_PHY_1P8_VOL_MAX);
 		if (rc) {
-			dev_err(motg->phy.dev, "unable to set voltage level "
-					"for hsusb 1p8\n");
-			goto put_1p8;
-		}
-		rc = regulator_enable(hsusb_1p8);
-		if (rc) {
-			dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n");
+			dev_err(motg->phy.dev, "unable to set voltage level for"
+					"hsusb 1p8\n");
 			goto put_1p8;
 		}
 
 		return 0;
 	}
 
-	regulator_disable(hsusb_1p8);
 put_1p8:
-	regulator_put(hsusb_1p8);
-disable_3p3:
-	regulator_disable(hsusb_3p3);
-put_3p3:
-	regulator_put(hsusb_3p3);
+	regulator_set_voltage(hsusb_1p8, 0, USB_PHY_1P8_VOL_MAX);
+put_3p3_lpm:
+	regulator_set_voltage(hsusb_3p3, 0, USB_PHY_3P3_VOL_MAX);
 	return rc;
 }
 
-#ifdef CONFIG_PM_SLEEP
-#define USB_PHY_SUSP_DIG_VOL  500000
 static int msm_hsusb_config_vddcx(int high)
 {
-	int max_vol = USB_PHY_VDD_DIG_VOL_MAX;
+	struct msm_otg *motg = the_msm_otg;
+	enum usb_vdd_type vdd_type = motg->vdd_type;
+	int max_vol = vdd_val[vdd_type][VDD_MAX];
 	int min_vol;
 	int ret;
 
-	if (high)
-		min_vol = USB_PHY_VDD_DIG_VOL_MIN;
-	else
-		min_vol = USB_PHY_SUSP_DIG_VOL;
-
+	min_vol = vdd_val[vdd_type][!!high];
 	ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol);
 	if (ret) {
 		pr_err("%s: unable to set the voltage for regulator "
@@ -185,18 +169,17 @@
 
 	return ret;
 }
-#endif
 
-static int msm_hsusb_ldo_set_mode(int on)
+static int msm_hsusb_ldo_enable(struct msm_otg *motg, int on)
 {
 	int ret = 0;
 
-	if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) {
+	if (IS_ERR(hsusb_1p8)) {
 		pr_err("%s: HSUSB_1p8 is not initialized\n", __func__);
 		return -ENODEV;
 	}
 
-	if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) {
+	if (IS_ERR(hsusb_3p3)) {
 		pr_err("%s: HSUSB_3p3 is not initialized\n", __func__);
 		return -ENODEV;
 	}
@@ -205,29 +188,61 @@
 		ret = regulator_set_optimum_mode(hsusb_1p8,
 				USB_PHY_1P8_HPM_LOAD);
 		if (ret < 0) {
-			pr_err("%s: Unable to set HPM of the regulator "
+			pr_err("%s: Unable to set HPM of the regulator:"
 				"HSUSB_1p8\n", __func__);
 			return ret;
 		}
+
+		ret = regulator_enable(hsusb_1p8);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to enable the hsusb 1p8\n",
+				__func__);
+			regulator_set_optimum_mode(hsusb_1p8, 0);
+			return ret;
+		}
+
 		ret = regulator_set_optimum_mode(hsusb_3p3,
 				USB_PHY_3P3_HPM_LOAD);
 		if (ret < 0) {
-			pr_err("%s: Unable to set HPM of the regulator "
+			pr_err("%s: Unable to set HPM of the regulator:"
 				"HSUSB_3p3\n", __func__);
-			regulator_set_optimum_mode(hsusb_1p8,
-				USB_PHY_1P8_LPM_LOAD);
+			regulator_set_optimum_mode(hsusb_1p8, 0);
+			regulator_disable(hsusb_1p8);
 			return ret;
 		}
+
+		ret = regulator_enable(hsusb_3p3);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to enable the hsusb 3p3\n",
+				__func__);
+			regulator_set_optimum_mode(hsusb_3p3, 0);
+			regulator_set_optimum_mode(hsusb_1p8, 0);
+			regulator_disable(hsusb_1p8);
+			return ret;
+		}
+
 	} else {
-		ret = regulator_set_optimum_mode(hsusb_1p8,
-				USB_PHY_1P8_LPM_LOAD);
+		ret = regulator_disable(hsusb_1p8);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to disable the hsusb 1p8\n",
+				__func__);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(hsusb_1p8, 0);
 		if (ret < 0)
-			pr_err("%s: Unable to set LPM of the regulator "
+			pr_err("%s: Unable to set LPM of the regulator:"
 				"HSUSB_1p8\n", __func__);
-		ret = regulator_set_optimum_mode(hsusb_3p3,
-				USB_PHY_3P3_LPM_LOAD);
+
+		ret = regulator_disable(hsusb_3p3);
+		if (ret) {
+			dev_err(motg->phy.dev, "%s: unable to disable the hsusb 3p3\n",
+				 __func__);
+			return ret;
+		}
+		ret = regulator_set_optimum_mode(hsusb_3p3, 0);
 		if (ret < 0)
-			pr_err("%s: Unable to set LPM of the regulator "
+			pr_err("%s: Unable to set LPM of the regulator:"
 				"HSUSB_3p3\n", __func__);
 	}
 
@@ -235,6 +250,26 @@
 	return ret < 0 ? ret : 0;
 }
 
+static void msm_hsusb_mhl_switch_enable(struct msm_otg *motg, bool on)
+{
+	struct msm_otg_platform_data *pdata = motg->pdata;
+
+	if (!pdata->mhl_enable)
+		return;
+
+	if (!mhl_analog_switch) {
+		pr_err("%s: mhl_analog_switch is NULL.\n", __func__);
+		return;
+	}
+
+	if (on) {
+		if (regulator_enable(mhl_analog_switch))
+			pr_err("unable to enable mhl_analog_switch\n");
+	} else {
+		regulator_disable(mhl_analog_switch);
+	}
+}
+
 static int ulpi_read(struct usb_phy *phy, u32 reg)
 {
 	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
@@ -310,6 +345,9 @@
 {
 	int ret;
 
+	if (IS_ERR(motg->clk))
+		return 0;
+
 	if (assert) {
 		ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
 		if (ret)
@@ -326,6 +364,9 @@
 {
 	int ret;
 
+	if (IS_ERR(motg->phy_reset_clk))
+		return 0;
+
 	ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT);
 	if (ret) {
 		dev_err(motg->phy.dev, "usb phy clk assert failed\n");
@@ -390,26 +431,13 @@
 }
 
 #define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
-static int msm_otg_reset(struct usb_phy *phy)
+static int msm_otg_link_reset(struct msm_otg *motg)
 {
-	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
-	struct msm_otg_platform_data *pdata = motg->pdata;
 	int cnt = 0;
-	int ret;
-	u32 val = 0;
-	u32 ulpi_val = 0;
 
-	ret = msm_otg_phy_reset(motg);
-	if (ret) {
-		dev_err(phy->dev, "phy_reset failed\n");
-		return ret;
-	}
-
-	ulpi_init(motg);
-
-	writel(USBCMD_RESET, USB_USBCMD);
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
 	while (cnt < LINK_RESET_TIMEOUT_USEC) {
-		if (!(readl(USB_USBCMD) & USBCMD_RESET))
+		if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
 			break;
 		udelay(1);
 		cnt++;
@@ -418,15 +446,59 @@
 		return -ETIMEDOUT;
 
 	/* select ULPI phy */
-	writel(0x80000000, USB_PORTSC);
+	writel_relaxed(0x80000000, USB_PORTSC);
+	writel_relaxed(0x0, USB_AHBBURST);
+	writel_relaxed(0x08, USB_AHBMODE);
 
+	return 0;
+}
+
+static int msm_otg_reset(struct usb_phy *phy)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int ret;
+	u32 val = 0;
+	u32 ulpi_val = 0;
+
+	/*
+	 * USB PHY and Link reset also reset the USB BAM.
+	 * Thus perform reset operation only once to avoid
+	 * USB BAM reset on other cases e.g. USB cable disconnections.
+	 */
+	if (pdata->disable_reset_on_disconnect) {
+		if (motg->reset_counter)
+			return 0;
+		else
+			motg->reset_counter++;
+	}
+
+	if (!IS_ERR(motg->clk))
+		clk_prepare_enable(motg->clk);
+	ret = msm_otg_phy_reset(motg);
+	if (ret) {
+		dev_err(phy->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	aca_id_turned_on = false;
+	ret = msm_otg_link_reset(motg);
+	if (ret) {
+		dev_err(phy->dev, "link reset failed\n");
+		return ret;
+	}
 	msleep(100);
 
-	writel(0x0, USB_AHBBURST);
-	writel(0x00, USB_AHBMODE);
+	ulpi_init(motg);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+
+	if (!IS_ERR(motg->clk))
+		clk_disable_unprepare(motg->clk);
 
 	if (pdata->otg_control == OTG_PHY_CONTROL) {
-		val = readl(USB_OTGSC);
+		val = readl_relaxed(USB_OTGSC);
 		if (pdata->mode == USB_OTG) {
 			ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID;
 			val |= OTGSC_IDIE | OTGSC_BSVIE;
@@ -434,14 +506,223 @@
 			ulpi_val = ULPI_INT_SESS_VALID;
 			val |= OTGSC_BSVIE;
 		}
-		writel(val, USB_OTGSC);
+		writel_relaxed(val, USB_OTGSC);
 		ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE);
 		ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
+	} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+		ulpi_write(phy, OTG_COMP_DISABLE,
+			ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+		/* Enable PMIC pull-up */
+		pm8xxx_usb_id_pullup(1);
 	}
 
 	return 0;
 }
 
+static const char *timer_string(int bit)
+{
+	switch (bit) {
+	case A_WAIT_VRISE:		return "a_wait_vrise";
+	case A_WAIT_VFALL:		return "a_wait_vfall";
+	case B_SRP_FAIL:		return "b_srp_fail";
+	case A_WAIT_BCON:		return "a_wait_bcon";
+	case A_AIDL_BDIS:		return "a_aidl_bdis";
+	case A_BIDL_ADIS:		return "a_bidl_adis";
+	case B_ASE0_BRST:		return "b_ase0_brst";
+	case A_TST_MAINT:		return "a_tst_maint";
+	case B_TST_SRP:			return "b_tst_srp";
+	case B_TST_CONFIG:		return "b_tst_config";
+	default:			return "UNDEFINED";
+	}
+}
+
+static enum hrtimer_restart msm_otg_timer_func(struct hrtimer *hrtimer)
+{
+	struct msm_otg *motg = container_of(hrtimer, struct msm_otg, timer);
+
+	switch (motg->active_tmout) {
+	case A_WAIT_VRISE:
+		/* TODO: use vbus_vld interrupt */
+		set_bit(A_VBUS_VLD, &motg->inputs);
+		break;
+	case A_TST_MAINT:
+		/* OTG PET: End session after TA_TST_MAINT */
+		set_bit(A_BUS_DROP, &motg->inputs);
+		break;
+	case B_TST_SRP:
+		/*
+		 * OTG PET: Initiate SRP after TB_TST_SRP of
+		 * previous session end.
+		 */
+		set_bit(B_BUS_REQ, &motg->inputs);
+		break;
+	case B_TST_CONFIG:
+		clear_bit(A_CONN, &motg->inputs);
+		break;
+	default:
+		set_bit(motg->active_tmout, &motg->tmouts);
+	}
+
+	pr_debug("expired %s timer\n", timer_string(motg->active_tmout));
+	queue_work(system_nrt_wq, &motg->sm_work);
+	return HRTIMER_NORESTART;
+}
+
+static void msm_otg_del_timer(struct msm_otg *motg)
+{
+	int bit = motg->active_tmout;
+
+	pr_debug("deleting %s timer. remaining %lld msec\n", timer_string(bit),
+			div_s64(ktime_to_us(hrtimer_get_remaining(
+					&motg->timer)), 1000));
+	hrtimer_cancel(&motg->timer);
+	clear_bit(bit, &motg->tmouts);
+}
+
+static void msm_otg_start_timer(struct msm_otg *motg, int time, int bit)
+{
+	clear_bit(bit, &motg->tmouts);
+	motg->active_tmout = bit;
+	pr_debug("starting %s timer\n", timer_string(bit));
+	hrtimer_start(&motg->timer,
+			ktime_set(time / 1000, (time % 1000) * 1000000),
+			HRTIMER_MODE_REL);
+}
+
+static void msm_otg_init_timer(struct msm_otg *motg)
+{
+	hrtimer_init(&motg->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	motg->timer.function = msm_otg_timer_func;
+}
+
+static int msm_otg_start_hnp(struct usb_otg *otg)
+{
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+
+	if (otg->phy->state != OTG_STATE_A_HOST) {
+		pr_err("HNP can not be initiated in %s state\n",
+				otg_state_string(otg->phy->state));
+		return -EINVAL;
+	}
+
+	pr_debug("A-Host: HNP initiated\n");
+	clear_bit(A_BUS_REQ, &motg->inputs);
+	queue_work(system_nrt_wq, &motg->sm_work);
+	return 0;
+}
+
+static int msm_otg_start_srp(struct usb_otg *otg)
+{
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+	u32 val;
+	int ret = 0;
+
+	if (otg->phy->state != OTG_STATE_B_IDLE) {
+		pr_err("SRP can not be initiated in %s state\n",
+				otg_state_string(otg->phy->state));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if ((jiffies - motg->b_last_se0_sess) < msecs_to_jiffies(TB_SRP_INIT)) {
+		pr_debug("initial conditions of SRP are not met. Try again"
+				"after some time\n");
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	pr_debug("B-Device SRP started\n");
+
+	/*
+	 * PHY won't pull D+ high unless it detects Vbus valid.
+	 * Since by definition, SRP is only done when Vbus is not valid,
+	 * software work-around needs to be used to spoof the PHY into
+	 * thinking it is valid. This can be done using the VBUSVLDEXTSEL and
+	 * VBUSVLDEXT register bits.
+	 */
+	ulpi_write(otg->phy, 0x03, 0x97);
+	/*
+	 * Harware auto assist data pulsing: Data pulse is given
+	 * for 7msec; wait for vbus
+	 */
+	val = readl_relaxed(USB_OTGSC);
+	writel_relaxed((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, USB_OTGSC);
+
+	/* VBUS plusing is obsoleted in OTG 2.0 supplement */
+out:
+	return ret;
+}
+
+static void msm_otg_host_hnp_enable(struct usb_otg *otg, bool enable)
+{
+	struct usb_hcd *hcd = bus_to_hcd(otg->host);
+	struct usb_device *rhub = otg->host->root_hub;
+
+	if (enable) {
+		pm_runtime_disable(&rhub->dev);
+		rhub->state = USB_STATE_NOTATTACHED;
+		hcd->driver->bus_suspend(hcd);
+		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+	} else {
+		usb_remove_hcd(hcd);
+		msm_otg_reset(otg->phy);
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	}
+}
+
+static int msm_otg_set_suspend(struct usb_phy *phy, int suspend)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+
+	if (aca_enabled())
+		return 0;
+
+	if (suspend) {
+		switch (phy->state) {
+		case OTG_STATE_A_WAIT_BCON:
+			if (TA_WAIT_BCON > 0)
+				break;
+			/* fall through */
+		case OTG_STATE_A_HOST:
+			pr_debug("host bus suspend\n");
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			queue_work(system_nrt_wq, &motg->sm_work);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			pr_debug("peripheral bus suspend\n");
+			if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+				break;
+			set_bit(A_BUS_SUSPEND, &motg->inputs);
+			queue_work(system_nrt_wq, &motg->sm_work);
+			break;
+
+		default:
+			break;
+		}
+	} else {
+		switch (phy->state) {
+		case OTG_STATE_A_SUSPEND:
+			/* Remote wakeup or resume */
+			set_bit(A_BUS_REQ, &motg->inputs);
+			phy->state = OTG_STATE_A_HOST;
+
+			/* ensure hardware is not in low power mode */
+			pm_runtime_resume(phy->dev);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			pr_debug("peripheral bus resume\n");
+			if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+				break;
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			queue_work(system_nrt_wq, &motg->sm_work);
+			break;
+		default:
+			break;
+		}
+	}
+	return 0;
+}
+
 #define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
 #define PHY_RESUME_TIMEOUT_USEC	(100 * 1000)
 
@@ -452,11 +733,20 @@
 	struct usb_bus *bus = phy->otg->host;
 	struct msm_otg_platform_data *pdata = motg->pdata;
 	int cnt = 0;
+	bool host_bus_suspend, device_bus_suspend, dcp;
+	u32 phy_ctrl_val = 0, cmd_val;
+	unsigned ret;
+	u32 portsc;
 
 	if (atomic_read(&motg->in_lpm))
 		return 0;
 
 	disable_irq(motg->irq);
+	host_bus_suspend = phy->otg->host && !test_bit(ID, &motg->inputs);
+	device_bus_suspend = phy->otg->gadget && test_bit(ID, &motg->inputs) &&
+		test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+		motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
+	dcp = motg->chg_type == USB_DCP_CHARGER;
 	/*
 	 * Chipidea 45-nm PHY suspend sequence:
 	 *
@@ -481,17 +771,22 @@
 		ulpi_write(phy, 0x08, 0x09);
 	}
 
-	/*
+
+	/* Set the PHCD bit, only if it is not set by the controller.
 	 * PHY may take some time or even fail to enter into low power
 	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
 	 * in failure case.
 	 */
-	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
-	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
-		if (readl(USB_PORTSC) & PORTSC_PHCD)
-			break;
-		udelay(1);
-		cnt++;
+	portsc = readl_relaxed(USB_PORTSC);
+	if (!(portsc & PORTSC_PHCD)) {
+		writel_relaxed(portsc | PORTSC_PHCD,
+				USB_PORTSC);
+		while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+			if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+				break;
+			udelay(1);
+			cnt++;
+		}
 	}
 
 	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
@@ -507,34 +802,69 @@
 	 * line must be disabled till async interrupt enable bit is cleared
 	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
 	 * block data communication from PHY.
+	 *
+	 * PHY retention mode is disallowed while entering to LPM with wall
+	 * charger connected.  But PHY is put into suspend mode. Hence
+	 * enable asynchronous interrupt to detect charger disconnection when
+	 * PMIC notifications are unavailable.
 	 */
-	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+	cmd_val = readl_relaxed(USB_USBCMD);
+	if (host_bus_suspend || device_bus_suspend ||
+		(motg->pdata->otg_control == OTG_PHY_CONTROL && dcp))
+		cmd_val |= ASYNC_INTR_CTRL | ULPI_STP_CTRL;
+	else
+		cmd_val |= ULPI_STP_CTRL;
+	writel_relaxed(cmd_val, USB_USBCMD);
 
-	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
-			motg->pdata->otg_control == OTG_PMIC_CONTROL)
-		writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL);
+	/*
+	 * BC1.2 spec mandates PD to enable VDP_SRC when charging from DCP.
+	 * PHY retention and collapse can not happen with VDP_SRC enabled.
+	 */
+	if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
+		!device_bus_suspend && !dcp) {
+		phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+			/* Enable PHY HV interrupts to wake MPM/Link */
+			phy_ctrl_val |=
+				(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
 
-	clk_disable(motg->pclk);
-	clk_disable(motg->clk);
-	if (motg->core_clk)
-		clk_disable(motg->core_clk);
-
-	if (!IS_ERR(motg->pclk_src))
-		clk_disable(motg->pclk_src);
-
-	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
-			motg->pdata->otg_control == OTG_PMIC_CONTROL) {
-		msm_hsusb_ldo_set_mode(0);
-		msm_hsusb_config_vddcx(0);
+		writel_relaxed(phy_ctrl_val & ~PHY_RETEN, USB_PHY_CTRL);
+		motg->lpm_flags |= PHY_RETENTIONED;
 	}
 
-	if (device_may_wakeup(phy->dev))
+	/* Ensure that above operation is completed before turning off clocks */
+	mb();
+	clk_disable_unprepare(motg->pclk);
+	clk_disable_unprepare(motg->core_clk);
+
+	/* usb phy no more require TCXO clock, hence vote for TCXO disable */
+	ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(phy->dev, "%s failed to devote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
+
+	if (motg->caps & ALLOW_PHY_POWER_COLLAPSE &&
+			!host_bus_suspend && !dcp) {
+		msm_hsusb_ldo_enable(motg, 0);
+		motg->lpm_flags |= PHY_PWR_COLLAPSED;
+	}
+
+	if (motg->lpm_flags & PHY_RETENTIONED) {
+		msm_hsusb_config_vddcx(0);
+		msm_hsusb_mhl_switch_enable(motg, 0);
+	}
+
+	if (device_may_wakeup(phy->dev)) {
 		enable_irq_wake(motg->irq);
+		if (motg->pdata->pmic_id_irq)
+			enable_irq_wake(motg->pdata->pmic_id_irq);
+	}
 	if (bus)
 		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
 
 	atomic_set(&motg->in_lpm, 1);
 	enable_irq(motg->irq);
+	wake_unlock(&motg->wlock);
 
 	dev_info(phy->dev, "USB in low power mode\n");
 
@@ -547,23 +877,40 @@
 	struct usb_bus *bus = phy->otg->host;
 	int cnt = 0;
 	unsigned temp;
+	u32 phy_ctrl_val = 0;
+	unsigned ret;
 
 	if (!atomic_read(&motg->in_lpm))
 		return 0;
 
-	if (!IS_ERR(motg->pclk_src))
-		clk_enable(motg->pclk_src);
+	wake_lock(&motg->wlock);
 
-	clk_enable(motg->pclk);
-	clk_enable(motg->clk);
-	if (motg->core_clk)
-		clk_enable(motg->core_clk);
+	/* Vote for TCXO when waking up the phy */
+	ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(phy->dev, "%s failed to vote for "
+			"TCXO D0 buffer%d\n", __func__, ret);
 
-	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
-			motg->pdata->otg_control == OTG_PMIC_CONTROL) {
-		msm_hsusb_ldo_set_mode(1);
+	clk_prepare_enable(motg->core_clk);
+
+	clk_prepare_enable(motg->pclk);
+
+	if (motg->lpm_flags & PHY_PWR_COLLAPSED) {
+		msm_hsusb_ldo_enable(motg, 1);
+		motg->lpm_flags &= ~PHY_PWR_COLLAPSED;
+	}
+
+	if (motg->lpm_flags & PHY_RETENTIONED) {
+		msm_hsusb_mhl_switch_enable(motg, 1);
 		msm_hsusb_config_vddcx(1);
-		writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL);
+		phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
+		phy_ctrl_val |= PHY_RETEN;
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+			/* Disable PHY HV interrupts */
+			phy_ctrl_val &=
+				~(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+		writel_relaxed(phy_ctrl_val, USB_PHY_CTRL);
+		motg->lpm_flags &= ~PHY_RETENTIONED;
 	}
 
 	temp = readl(USB_USBCMD);
@@ -598,8 +945,11 @@
 	}
 
 skip_phy_resume:
-	if (device_may_wakeup(phy->dev))
+	if (device_may_wakeup(phy->dev)) {
 		disable_irq_wake(motg->irq);
+		if (motg->pdata->pmic_id_irq)
+			disable_irq_wake(motg->pdata->pmic_id_irq);
+	}
 	if (bus)
 		set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
 
@@ -607,7 +957,6 @@
 
 	if (motg->async_int) {
 		motg->async_int = 0;
-		pm_runtime_put(phy->dev);
 		enable_irq(motg->irq);
 	}
 
@@ -617,13 +966,91 @@
 }
 #endif
 
+static int msm_otg_notify_chg_type(struct msm_otg *motg)
+{
+	static int charger_type;
+	/*
+	 * TODO
+	 * Unify OTG driver charger types and power supply charger types
+	 */
+	if (charger_type == motg->chg_type)
+		return 0;
+
+	if (motg->chg_type == USB_SDP_CHARGER)
+		charger_type = POWER_SUPPLY_TYPE_USB;
+	else if (motg->chg_type == USB_CDP_CHARGER)
+		charger_type = POWER_SUPPLY_TYPE_USB_CDP;
+	else if (motg->chg_type == USB_DCP_CHARGER)
+		charger_type = POWER_SUPPLY_TYPE_USB_DCP;
+	else if ((motg->chg_type == USB_ACA_DOCK_CHARGER ||
+		motg->chg_type == USB_ACA_A_CHARGER ||
+		motg->chg_type == USB_ACA_B_CHARGER ||
+		motg->chg_type == USB_ACA_C_CHARGER))
+		charger_type = POWER_SUPPLY_TYPE_USB_ACA;
+	else
+		charger_type = POWER_SUPPLY_TYPE_BATTERY;
+
+	return pm8921_set_usb_power_supply_type(charger_type);
+}
+
+static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
+{
+
+	if (!psy)
+		goto psy_not_supported;
+
+	if (motg->cur_power == 0 && mA > 0) {
+		/* Enable charging */
+		if (power_supply_set_online(psy, true))
+			goto psy_not_supported;
+	} else if (motg->cur_power > 0 && mA == 0) {
+		/* Disable charging */
+		if (power_supply_set_online(psy, false))
+			goto psy_not_supported;
+		return 0;
+	}
+	/* Set max current limit */
+	if (power_supply_set_current_limit(psy, 1000*mA))
+		goto psy_not_supported;
+
+	return 0;
+
+psy_not_supported:
+	dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
+	return -ENXIO;
+}
+
 static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
 {
+	struct usb_gadget *g = motg->phy.otg->gadget;
+
+	if (g && g->is_a_peripheral)
+		return;
+
+	if ((motg->chg_type == USB_ACA_DOCK_CHARGER ||
+		motg->chg_type == USB_ACA_A_CHARGER ||
+		motg->chg_type == USB_ACA_B_CHARGER ||
+		motg->chg_type == USB_ACA_C_CHARGER) &&
+			mA > IDEV_ACA_CHG_LIMIT)
+		mA = IDEV_ACA_CHG_LIMIT;
+
+	if (msm_otg_notify_chg_type(motg))
+		dev_err(motg->phy.dev,
+			"Failed notifying %d charger type to PMIC\n",
+							motg->chg_type);
+
 	if (motg->cur_power == mA)
 		return;
 
-	/* TODO: Notify PMIC about available current */
 	dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
+
+	/*
+	 *  Use Power Supply API if supported, otherwise fallback
+	 *  to legacy pm8921 API.
+	 */
+	if (msm_otg_notify_power_supply(motg, mA))
+		pm8921_charger_vbus_draw(mA);
+
 	motg->cur_power = mA;
 }
 
@@ -644,22 +1071,24 @@
 	return 0;
 }
 
-static void msm_otg_start_host(struct usb_phy *phy, int on)
+static void msm_otg_start_host(struct usb_otg *otg, int on)
 {
-	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
 	struct msm_otg_platform_data *pdata = motg->pdata;
 	struct usb_hcd *hcd;
 
-	if (!phy->otg->host)
+	if (!otg->host)
 		return;
 
-	hcd = bus_to_hcd(phy->otg->host);
+	hcd = bus_to_hcd(otg->host);
 
 	if (on) {
-		dev_dbg(phy->dev, "host on\n");
+		dev_dbg(otg->phy->dev, "host on\n");
 
-		if (pdata->vbus_power)
-			pdata->vbus_power(1);
+		if (pdata->otg_control == OTG_PHY_CONTROL)
+			ulpi_write(otg->phy, OTG_COMP_DISABLE,
+				ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+
 		/*
 		 * Some boards have a switch cotrolled by gpio
 		 * to enable/disable internal HUB. Enable internal
@@ -667,19 +1096,139 @@
 		 */
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_A_HOST);
-#ifdef CONFIG_USB
 		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
-#endif
 	} else {
-		dev_dbg(phy->dev, "host off\n");
+		dev_dbg(otg->phy->dev, "host off\n");
 
-#ifdef CONFIG_USB
 		usb_remove_hcd(hcd);
-#endif
+		/* HCD core reset all bits of PORTSC. select ULPI phy */
+		writel_relaxed(0x80000000, USB_PORTSC);
+
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_UNDEFINED);
-		if (pdata->vbus_power)
-			pdata->vbus_power(0);
+
+		if (pdata->otg_control == OTG_PHY_CONTROL)
+			ulpi_write(otg->phy, OTG_COMP_DISABLE,
+				ULPI_CLR(ULPI_PWR_CLK_MNG_REG));
+	}
+}
+
+static int msm_otg_usbdev_notify(struct notifier_block *self,
+			unsigned long action, void *priv)
+{
+	struct msm_otg *motg = container_of(self, struct msm_otg, usbdev_nb);
+	struct usb_otg *otg = motg->phy.otg;
+	struct usb_device *udev = priv;
+
+	if (action == USB_BUS_ADD || action == USB_BUS_REMOVE)
+		goto out;
+
+	if (udev->bus != otg->host)
+		goto out;
+	/*
+	 * Interested in devices connected directly to the root hub.
+	 * ACA dock can supply IDEV_CHG irrespective devices connected
+	 * on the accessory port.
+	 */
+	if (!udev->parent || udev->parent->parent ||
+			motg->chg_type == USB_ACA_DOCK_CHARGER)
+		goto out;
+
+	switch (action) {
+	case USB_DEVICE_ADD:
+		if (aca_enabled())
+			usb_disable_autosuspend(udev);
+		if (otg->phy->state == OTG_STATE_A_WAIT_BCON) {
+			pr_debug("B_CONN set\n");
+			set_bit(B_CONN, &motg->inputs);
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_HOST;
+			/*
+			 * OTG PET: A-device must end session within
+			 * 10 sec after PET enumeration.
+			 */
+			if (udev->quirks & USB_QUIRK_OTG_PET)
+				msm_otg_start_timer(motg, TA_TST_MAINT,
+						A_TST_MAINT);
+		}
+		/* fall through */
+	case USB_DEVICE_CONFIG:
+		if (udev->actconfig)
+			motg->mA_port = udev->actconfig->desc.bMaxPower * 2;
+		else
+			motg->mA_port = IUNIT;
+		if (otg->phy->state == OTG_STATE_B_HOST)
+			msm_otg_del_timer(motg);
+		break;
+	case USB_DEVICE_REMOVE:
+		if ((otg->phy->state == OTG_STATE_A_HOST) ||
+			(otg->phy->state == OTG_STATE_A_SUSPEND)) {
+			pr_debug("B_CONN clear\n");
+			clear_bit(B_CONN, &motg->inputs);
+			/*
+			 * OTG PET: A-device must end session after
+			 * PET disconnection if it is enumerated
+			 * with bcdDevice[0] = 1. USB core sets
+			 * bus->otg_vbus_off for us. clear it here.
+			 */
+			if (udev->bus->otg_vbus_off) {
+				udev->bus->otg_vbus_off = 0;
+				set_bit(A_BUS_DROP, &motg->inputs);
+			}
+			queue_work(system_nrt_wq, &motg->sm_work);
+		}
+	default:
+		break;
+	}
+	if (test_bit(ID_A, &motg->inputs))
+		msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX -
+				motg->mA_port);
+out:
+	return NOTIFY_OK;
+}
+
+static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on)
+{
+	int ret;
+	static bool vbus_is_on;
+
+	if (vbus_is_on == on)
+		return;
+
+	if (motg->pdata->vbus_power) {
+		ret = motg->pdata->vbus_power(on);
+		if (!ret)
+			vbus_is_on = on;
+		return;
+	}
+
+	if (!vbus_otg) {
+		pr_err("vbus_otg is NULL.");
+		return;
+	}
+
+	/*
+	 * if entering host mode tell the charger to not draw any current
+	 * from usb before turning on the boost.
+	 * if exiting host mode disable the boost before enabling to draw
+	 * current from the source.
+	 */
+	if (on) {
+		pm8921_disable_source_current(on);
+		ret = regulator_enable(vbus_otg);
+		if (ret) {
+			pr_err("unable to enable vbus_otg\n");
+			return;
+		}
+		vbus_is_on = true;
+	} else {
+		ret = regulator_disable(vbus_otg);
+		if (ret) {
+			pr_err("unable to disable vbus_otg\n");
+			return;
+		}
+		pm8921_disable_source_current(on);
+		vbus_is_on = false;
 	}
 }
 
@@ -697,13 +1246,23 @@
 		return -ENODEV;
 	}
 
+	if (!motg->pdata->vbus_power && host) {
+		vbus_otg = devm_regulator_get(motg->phy.dev, "vbus_otg");
+		if (IS_ERR(vbus_otg)) {
+			pr_err("Unable to get vbus_otg\n");
+			return -ENODEV;
+		}
+	}
+
 	if (!host) {
 		if (otg->phy->state == OTG_STATE_A_HOST) {
 			pm_runtime_get_sync(otg->phy->dev);
-			msm_otg_start_host(otg->phy, 0);
+			usb_unregister_notify(&motg->usbdev_nb);
+			msm_otg_start_host(otg, 0);
+			msm_hsusb_vbus_power(motg, 0);
 			otg->host = NULL;
 			otg->phy->state = OTG_STATE_UNDEFINED;
-			schedule_work(&motg->sm_work);
+			queue_work(system_nrt_wq, &motg->sm_work);
 		} else {
 			otg->host = NULL;
 		}
@@ -714,6 +1273,11 @@
 	hcd = bus_to_hcd(host);
 	hcd->power_budget = motg->pdata->power_budget;
 
+#ifdef CONFIG_USB_OTG
+	host->otg_port = 1;
+#endif
+	motg->usbdev_nb.notifier_call = msm_otg_usbdev_notify;
+	usb_register_notify(&motg->usbdev_nb);
 	otg->host = host;
 	dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n");
 
@@ -723,22 +1287,23 @@
 	 */
 	if (motg->pdata->mode == USB_HOST || otg->gadget) {
 		pm_runtime_get_sync(otg->phy->dev);
-		schedule_work(&motg->sm_work);
+		queue_work(system_nrt_wq, &motg->sm_work);
 	}
 
 	return 0;
 }
 
-static void msm_otg_start_peripheral(struct usb_phy *phy, int on)
+static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
 {
-	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	int ret;
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
 	struct msm_otg_platform_data *pdata = motg->pdata;
 
-	if (!phy->otg->gadget)
+	if (!otg->gadget)
 		return;
 
 	if (on) {
-		dev_dbg(phy->dev, "gadget on\n");
+		dev_dbg(otg->phy->dev, "gadget on\n");
 		/*
 		 * Some boards have a switch cotrolled by gpio
 		 * to enable/disable internal HUB. Disable internal
@@ -746,10 +1311,27 @@
 		 */
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_B_PERIPHERAL);
-		usb_gadget_vbus_connect(phy->otg->gadget);
+
+		/* Configure BUS performance parameters for MAX bandwidth */
+		if (motg->bus_perf_client && debug_bus_voting_enabled) {
+			ret = msm_bus_scale_client_update_request(
+					motg->bus_perf_client, 1);
+			if (ret)
+				dev_err(motg->phy.dev, "%s: Failed to vote for "
+					   "bus bandwidth %d\n", __func__, ret);
+		}
+		usb_gadget_vbus_connect(otg->gadget);
 	} else {
-		dev_dbg(phy->dev, "gadget off\n");
-		usb_gadget_vbus_disconnect(phy->otg->gadget);
+		dev_dbg(otg->phy->dev, "gadget off\n");
+		usb_gadget_vbus_disconnect(otg->gadget);
+		/* Configure BUS performance parameters to default */
+		if (motg->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					motg->bus_perf_client, 0);
+			if (ret)
+				dev_err(motg->phy.dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
 		if (pdata->setup_gpio)
 			pdata->setup_gpio(OTG_STATE_UNDEFINED);
 	}
@@ -757,7 +1339,7 @@
 }
 
 static int msm_otg_set_peripheral(struct usb_otg *otg,
-					struct usb_gadget *gadget)
+			struct usb_gadget *gadget)
 {
 	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
 
@@ -773,10 +1355,10 @@
 	if (!gadget) {
 		if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
 			pm_runtime_get_sync(otg->phy->dev);
-			msm_otg_start_peripheral(otg->phy, 0);
+			msm_otg_start_peripheral(otg, 0);
 			otg->gadget = NULL;
 			otg->phy->state = OTG_STATE_UNDEFINED;
-			schedule_work(&motg->sm_work);
+			queue_work(system_nrt_wq, &motg->sm_work);
 		} else {
 			otg->gadget = NULL;
 		}
@@ -792,12 +1374,190 @@
 	 */
 	if (motg->pdata->mode == USB_PERIPHERAL || otg->host) {
 		pm_runtime_get_sync(otg->phy->dev);
-		schedule_work(&motg->sm_work);
+		queue_work(system_nrt_wq, &motg->sm_work);
 	}
 
 	return 0;
 }
 
+static bool msm_chg_aca_detect(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 int_sts;
+	bool ret = false;
+
+	if (!aca_enabled())
+		goto out;
+
+	if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY)
+		goto out;
+
+	int_sts = ulpi_read(phy, 0x87);
+	switch (int_sts & 0x1C) {
+	case 0x08:
+		if (!test_and_set_bit(ID_A, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_A\n");
+			motg->chg_type = USB_ACA_A_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			clear_bit(ID_B, &motg->inputs);
+			clear_bit(ID_C, &motg->inputs);
+			set_bit(ID, &motg->inputs);
+			ret = true;
+		}
+		break;
+	case 0x0C:
+		if (!test_and_set_bit(ID_B, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_B\n");
+			motg->chg_type = USB_ACA_B_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			clear_bit(ID_A, &motg->inputs);
+			clear_bit(ID_C, &motg->inputs);
+			set_bit(ID, &motg->inputs);
+			ret = true;
+		}
+		break;
+	case 0x10:
+		if (!test_and_set_bit(ID_C, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_C\n");
+			motg->chg_type = USB_ACA_C_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			clear_bit(ID_A, &motg->inputs);
+			clear_bit(ID_B, &motg->inputs);
+			set_bit(ID, &motg->inputs);
+			ret = true;
+		}
+		break;
+	case 0x04:
+		if (test_and_clear_bit(ID, &motg->inputs)) {
+			dev_dbg(phy->dev, "ID_GND\n");
+			motg->chg_type = USB_INVALID_CHARGER;
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			clear_bit(ID_A, &motg->inputs);
+			clear_bit(ID_B, &motg->inputs);
+			clear_bit(ID_C, &motg->inputs);
+			ret = true;
+		}
+		break;
+	default:
+		ret = test_and_clear_bit(ID_A, &motg->inputs) |
+			test_and_clear_bit(ID_B, &motg->inputs) |
+			test_and_clear_bit(ID_C, &motg->inputs) |
+			!test_and_set_bit(ID, &motg->inputs);
+		if (ret) {
+			dev_dbg(phy->dev, "ID A/B/C/GND is no more\n");
+			motg->chg_type = USB_INVALID_CHARGER;
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+		}
+	}
+out:
+	return ret;
+}
+
+static void msm_chg_enable_aca_det(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+
+	if (!aca_enabled())
+		return;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Disable ID_GND in link and PHY */
+		writel_relaxed(readl_relaxed(USB_OTGSC) & ~(OTGSC_IDPU |
+				OTGSC_IDIE), USB_OTGSC);
+		ulpi_write(phy, 0x01, 0x0C);
+		ulpi_write(phy, 0x10, 0x0F);
+		ulpi_write(phy, 0x10, 0x12);
+		/* Disable PMIC ID pull-up */
+		pm8xxx_usb_id_pullup(0);
+		/* Enable ACA ID detection */
+		ulpi_write(phy, 0x20, 0x85);
+		aca_id_turned_on = true;
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_enable_aca_intr(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+
+	if (!aca_enabled())
+		return;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Enable ACA Detection interrupt (on any RID change) */
+		ulpi_write(phy, 0x01, 0x94);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_disable_aca_intr(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+
+	if (!aca_enabled())
+		return;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		ulpi_write(phy, 0x01, 0x95);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool msm_chg_check_aca_intr(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	bool ret = false;
+
+	if (!aca_enabled())
+		return ret;
+
+	switch (motg->pdata->phy_type) {
+	case SNPS_28NM_INTEGRATED_PHY:
+		if (ulpi_read(phy, 0x91) & 1) {
+			dev_dbg(phy->dev, "RID change\n");
+			ulpi_write(phy, 0x01, 0x92);
+			ret = msm_chg_aca_detect(motg);
+		}
+	default:
+		break;
+	}
+	return ret;
+}
+
+static void msm_otg_id_timer_func(unsigned long data)
+{
+	struct msm_otg *motg = (struct msm_otg *) data;
+
+	if (!aca_enabled())
+		return;
+
+	if (atomic_read(&motg->in_lpm)) {
+		dev_dbg(motg->phy.dev, "timer: in lpm\n");
+		return;
+	}
+
+	if (motg->phy.state == OTG_STATE_A_SUSPEND)
+		goto out;
+
+	if (msm_chg_check_aca_intr(motg)) {
+		dev_dbg(motg->phy.dev, "timer: aca work\n");
+		queue_work(system_nrt_wq, &motg->sm_work);
+	}
+
+out:
+	if (!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs))
+		mod_timer(&motg->id_timer, ID_TIMER_FREQ);
+}
+
 static bool msm_chg_check_secondary_det(struct msm_otg *motg)
 {
 	struct usb_phy *phy = &motg->phy;
@@ -846,6 +1606,9 @@
 		ulpi_write(phy, chg_det, 0x34);
 		break;
 	case SNPS_28NM_INTEGRATED_PHY:
+		/* Turn off VDP_SRC */
+		ulpi_write(phy, 0x3, 0x86);
+		msleep(20);
 		/*
 		 * Configure DM as current source, DP as current sink
 		 * and enable battery charging comparators.
@@ -990,7 +1753,7 @@
 		break;
 	case SNPS_28NM_INTEGRATED_PHY:
 		/* Clear charger detecting control bits */
-		ulpi_write(phy, 0x3F, 0x86);
+		ulpi_write(phy, 0x1F, 0x86);
 		/* Clear alt interrupt latch and enable bits */
 		ulpi_write(phy, 0x1F, 0x92);
 		ulpi_write(phy, 0x1F, 0x95);
@@ -1031,32 +1794,62 @@
 	ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
 }
 
+static const char *chg_to_string(enum usb_chg_type chg_type)
+{
+	switch (chg_type) {
+	case USB_SDP_CHARGER:		return "USB_SDP_CHARGER";
+	case USB_DCP_CHARGER:		return "USB_DCP_CHARGER";
+	case USB_CDP_CHARGER:		return "USB_CDP_CHARGER";
+	case USB_ACA_A_CHARGER:		return "USB_ACA_A_CHARGER";
+	case USB_ACA_B_CHARGER:		return "USB_ACA_B_CHARGER";
+	case USB_ACA_C_CHARGER:		return "USB_ACA_C_CHARGER";
+	case USB_ACA_DOCK_CHARGER:	return "USB_ACA_DOCK_CHARGER";
+	default:			return "INVALID_CHARGER";
+	}
+}
+
 #define MSM_CHG_DCD_POLL_TIME		(100 * HZ/1000) /* 100 msec */
 #define MSM_CHG_DCD_MAX_RETRIES		6 /* Tdcd_tmout = 6 * 100 msec */
-#define MSM_CHG_PRIMARY_DET_TIME	(40 * HZ/1000) /* TVDPSRC_ON */
-#define MSM_CHG_SECONDARY_DET_TIME	(40 * HZ/1000) /* TVDMSRC_ON */
+#define MSM_CHG_PRIMARY_DET_TIME	(50 * HZ/1000) /* TVDPSRC_ON */
+#define MSM_CHG_SECONDARY_DET_TIME	(50 * HZ/1000) /* TVDMSRC_ON */
 static void msm_chg_detect_work(struct work_struct *w)
 {
 	struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
 	struct usb_phy *phy = &motg->phy;
-	bool is_dcd, tmout, vout;
+	bool is_dcd = false, tmout, vout, is_aca;
 	unsigned long delay;
 
 	dev_dbg(phy->dev, "chg detection work\n");
 	switch (motg->chg_state) {
 	case USB_CHG_STATE_UNDEFINED:
-		pm_runtime_get_sync(phy->dev);
 		msm_chg_block_on(motg);
-		msm_chg_enable_dcd(motg);
+		if (motg->pdata->enable_dcd)
+			msm_chg_enable_dcd(motg);
+		msm_chg_enable_aca_det(motg);
 		motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
 		motg->dcd_retries = 0;
 		delay = MSM_CHG_DCD_POLL_TIME;
 		break;
 	case USB_CHG_STATE_WAIT_FOR_DCD:
-		is_dcd = msm_chg_check_dcd(motg);
+		is_aca = msm_chg_aca_detect(motg);
+		if (is_aca) {
+			/*
+			 * ID_A can be ACA dock too. continue
+			 * primary detection after DCD.
+			 */
+			if (test_bit(ID_A, &motg->inputs)) {
+				motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+			} else {
+				delay = 0;
+				break;
+			}
+		}
+		if (motg->pdata->enable_dcd)
+			is_dcd = msm_chg_check_dcd(motg);
 		tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
 		if (is_dcd || tmout) {
-			msm_chg_disable_dcd(motg);
+			if (motg->pdata->enable_dcd)
+				msm_chg_disable_dcd(motg);
 			msm_chg_enable_primary_det(motg);
 			delay = MSM_CHG_PRIMARY_DET_TIME;
 			motg->chg_state = USB_CHG_STATE_DCD_DONE;
@@ -1067,10 +1860,22 @@
 	case USB_CHG_STATE_DCD_DONE:
 		vout = msm_chg_check_primary_det(motg);
 		if (vout) {
+			if (test_bit(ID_A, &motg->inputs)) {
+				motg->chg_type = USB_ACA_DOCK_CHARGER;
+				motg->chg_state = USB_CHG_STATE_DETECTED;
+				delay = 0;
+				break;
+			}
 			msm_chg_enable_secondary_det(motg);
 			delay = MSM_CHG_SECONDARY_DET_TIME;
 			motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
 		} else {
+			if (test_bit(ID_A, &motg->inputs)) {
+				motg->chg_type = USB_ACA_A_CHARGER;
+				motg->chg_state = USB_CHG_STATE_DETECTED;
+				delay = 0;
+				break;
+			}
 			motg->chg_type = USB_SDP_CHARGER;
 			motg->chg_state = USB_CHG_STATE_DETECTED;
 			delay = 0;
@@ -1088,14 +1893,23 @@
 		motg->chg_state = USB_CHG_STATE_DETECTED;
 	case USB_CHG_STATE_DETECTED:
 		msm_chg_block_off(motg);
-		dev_dbg(phy->dev, "charger = %d\n", motg->chg_type);
-		schedule_work(&motg->sm_work);
+		msm_chg_enable_aca_det(motg);
+		/*
+		 * Spurious interrupt is seen after enabling ACA detection
+		 * due to which charger detection fails in case of PET.
+		 * Add delay of 100 microsec to avoid that.
+		 */
+		udelay(100);
+		msm_chg_enable_aca_intr(motg);
+		dev_dbg(phy->dev, "chg_type = %s\n",
+			chg_to_string(motg->chg_type));
+		queue_work(system_nrt_wq, &motg->sm_work);
 		return;
 	default:
 		return;
 	}
 
-	schedule_delayed_work(&motg->chg_work, delay);
+	queue_delayed_work(system_nrt_wq, &motg->chg_work, delay);
 }
 
 /*
@@ -1112,17 +1926,7 @@
 
 	switch (pdata->mode) {
 	case USB_OTG:
-		if (pdata->otg_control == OTG_PHY_CONTROL) {
-			if (otgsc & OTGSC_ID)
-				set_bit(ID, &motg->inputs);
-			else
-				clear_bit(ID, &motg->inputs);
-
-			if (otgsc & OTGSC_BSV)
-				set_bit(B_SESS_VLD, &motg->inputs);
-			else
-				clear_bit(B_SESS_VLD, &motg->inputs);
-		} else if (pdata->otg_control == OTG_USER_CONTROL) {
+		if (pdata->otg_control == OTG_USER_CONTROL) {
 			if (pdata->default_mode == USB_HOST) {
 				clear_bit(ID, &motg->inputs);
 			} else if (pdata->default_mode == USB_PERIPHERAL) {
@@ -1132,6 +1936,32 @@
 				set_bit(ID, &motg->inputs);
 				clear_bit(B_SESS_VLD, &motg->inputs);
 			}
+		} else if (pdata->otg_control == OTG_PHY_CONTROL) {
+			if (otgsc & OTGSC_ID) {
+				set_bit(ID, &motg->inputs);
+			} else {
+				clear_bit(ID, &motg->inputs);
+				set_bit(A_BUS_REQ, &motg->inputs);
+			}
+			if (otgsc & OTGSC_BSV)
+				set_bit(B_SESS_VLD, &motg->inputs);
+			else
+				clear_bit(B_SESS_VLD, &motg->inputs);
+		} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+			if (pdata->pmic_id_irq) {
+				unsigned long flags;
+				local_irq_save(flags);
+				if (irq_read_line(pdata->pmic_id_irq))
+					set_bit(ID, &motg->inputs);
+				else
+					clear_bit(ID, &motg->inputs);
+				local_irq_restore(flags);
+			}
+			/*
+			 * VBUS initial state is reported after PMIC
+			 * driver initialization. Wait for it.
+			 */
+			wait_for_completion(&pmic_vbus_init);
 		}
 		break;
 	case USB_HOST:
@@ -1139,10 +1969,18 @@
 		break;
 	case USB_PERIPHERAL:
 		set_bit(ID, &motg->inputs);
-		if (otgsc & OTGSC_BSV)
-			set_bit(B_SESS_VLD, &motg->inputs);
-		else
-			clear_bit(B_SESS_VLD, &motg->inputs);
+		if (pdata->otg_control == OTG_PHY_CONTROL) {
+			if (otgsc & OTGSC_BSV)
+				set_bit(B_SESS_VLD, &motg->inputs);
+			else
+				clear_bit(B_SESS_VLD, &motg->inputs);
+		} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+			/*
+			 * VBUS initial state is reported after PMIC
+			 * driver initialization. Wait for it.
+			 */
+			wait_for_completion(&pmic_vbus_init);
+		}
 		break;
 	default:
 		break;
@@ -1153,22 +1991,35 @@
 {
 	struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
 	struct usb_otg *otg = motg->phy.otg;
+	bool work = 0, srp_reqd;
 
+	pm_runtime_resume(otg->phy->dev);
+	pr_debug("%s work\n", otg_state_string(otg->phy->state));
 	switch (otg->phy->state) {
 	case OTG_STATE_UNDEFINED:
-		dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n");
 		msm_otg_reset(otg->phy);
 		msm_otg_init_sm(motg);
+		psy = power_supply_get_by_name("usb");
+		if (!psy)
+			pr_err("couldn't get usb power supply\n");
 		otg->phy->state = OTG_STATE_B_IDLE;
+		if (!test_bit(B_SESS_VLD, &motg->inputs) &&
+				test_bit(ID, &motg->inputs)) {
+			pm_runtime_put_noidle(otg->phy->dev);
+			pm_runtime_suspend(otg->phy->dev);
+			break;
+		}
 		/* FALL THROUGH */
 	case OTG_STATE_B_IDLE:
-		dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n");
-		if (!test_bit(ID, &motg->inputs) && otg->host) {
-			/* disable BSV bit */
-			writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
-			msm_otg_start_host(otg->phy, 1);
-			otg->phy->state = OTG_STATE_A_HOST;
+		if ((!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs)) && otg->host) {
+			pr_debug("!id || id_A\n");
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			set_bit(A_BUS_REQ, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_IDLE;
+			work = 1;
 		} else if (test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("b_sess_vld\n");
 			switch (motg->chg_state) {
 			case USB_CHG_STATE_UNDEFINED:
 				msm_chg_detect_work(&motg->chg_work.work);
@@ -1176,21 +2027,39 @@
 			case USB_CHG_STATE_DETECTED:
 				switch (motg->chg_type) {
 				case USB_DCP_CHARGER:
+					/* Enable VDP_SRC */
+					ulpi_write(otg->phy, 0x2, 0x85);
 					msm_otg_notify_charger(motg,
 							IDEV_CHG_MAX);
+					pm_runtime_put_noidle(otg->phy->dev);
+					pm_runtime_suspend(otg->phy->dev);
+					break;
+				case USB_ACA_B_CHARGER:
+					msm_otg_notify_charger(motg,
+							IDEV_ACA_CHG_MAX);
+					/*
+					 * (ID_B --> ID_C) PHY_ALT interrupt can
+					 * not be detected in LPM.
+					 */
 					break;
 				case USB_CDP_CHARGER:
 					msm_otg_notify_charger(motg,
 							IDEV_CHG_MAX);
-					msm_otg_start_peripheral(otg->phy, 1);
-					otg->phy->state
-						= OTG_STATE_B_PERIPHERAL;
+					msm_otg_start_peripheral(otg, 1);
+					otg->phy->state =
+						OTG_STATE_B_PERIPHERAL;
+					break;
+				case USB_ACA_C_CHARGER:
+					msm_otg_notify_charger(motg,
+							IDEV_ACA_CHG_MAX);
+					msm_otg_start_peripheral(otg, 1);
+					otg->phy->state =
+						OTG_STATE_B_PERIPHERAL;
 					break;
 				case USB_SDP_CHARGER:
-					msm_otg_notify_charger(motg, IUNIT);
-					msm_otg_start_peripheral(otg->phy, 1);
-					otg->phy->state
-						= OTG_STATE_B_PERIPHERAL;
+					msm_otg_start_peripheral(otg, 1);
+					otg->phy->state =
+						OTG_STATE_B_PERIPHERAL;
 					break;
 				default:
 					break;
@@ -1199,93 +2068,692 @@
 			default:
 				break;
 			}
-		} else {
-			/*
-			 * If charger detection work is pending, decrement
-			 * the pm usage counter to balance with the one that
-			 * is incremented in charger detection work.
-			 */
-			if (cancel_delayed_work_sync(&motg->chg_work)) {
-				pm_runtime_put_sync(otg->phy->dev);
-				msm_otg_reset(otg->phy);
+		} else if (test_bit(B_BUS_REQ, &motg->inputs)) {
+			pr_debug("b_sess_end && b_bus_req\n");
+			if (msm_otg_start_srp(otg) < 0) {
+				clear_bit(B_BUS_REQ, &motg->inputs);
+				work = 1;
+				break;
 			}
-			msm_otg_notify_charger(motg, 0);
+			otg->phy->state = OTG_STATE_B_SRP_INIT;
+			msm_otg_start_timer(motg, TB_SRP_FAIL, B_SRP_FAIL);
+			break;
+		} else {
+			pr_debug("chg_work cancel");
+			cancel_delayed_work_sync(&motg->chg_work);
 			motg->chg_state = USB_CHG_STATE_UNDEFINED;
 			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
+			msm_otg_reset(otg->phy);
+			pm_runtime_put_noidle(otg->phy->dev);
+			pm_runtime_suspend(otg->phy->dev);
 		}
-		pm_runtime_put_sync(otg->phy->dev);
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		if (!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs) ||
+				test_bit(ID_C, &motg->inputs) ||
+				(test_bit(B_SESS_VLD, &motg->inputs) &&
+				!test_bit(ID_B, &motg->inputs))) {
+			pr_debug("!id || id_a/c || b_sess_vld+!id_b\n");
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			/*
+			 * clear VBUSVLDEXTSEL and VBUSVLDEXT register
+			 * bits after SRP initiation.
+			 */
+			ulpi_write(otg->phy, 0x0, 0x98);
+			work = 1;
+		} else if (test_bit(B_SRP_FAIL, &motg->tmouts)) {
+			pr_debug("b_srp_fail\n");
+			pr_info("A-device did not respond to SRP\n");
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			clear_bit(B_SRP_FAIL, &motg->tmouts);
+			otg_send_event(OTG_EVENT_NO_RESP_FOR_SRP);
+			ulpi_write(otg->phy, 0x0, 0x98);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			motg->b_last_se0_sess = jiffies;
+			work = 1;
+		}
 		break;
 	case OTG_STATE_B_PERIPHERAL:
-		dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n");
-		if (!test_bit(B_SESS_VLD, &motg->inputs) ||
-				!test_bit(ID, &motg->inputs)) {
-			msm_otg_notify_charger(motg, 0);
-			msm_otg_start_peripheral(otg->phy, 0);
+		if (!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs) ||
+				test_bit(ID_B, &motg->inputs) ||
+				!test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("!id  || id_a/b || !b_sess_vld\n");
 			motg->chg_state = USB_CHG_STATE_UNDEFINED;
 			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
+			srp_reqd = otg->gadget->otg_srp_reqd;
+			msm_otg_start_peripheral(otg, 0);
+			if (test_bit(ID_B, &motg->inputs))
+				clear_bit(ID_B, &motg->inputs);
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			motg->b_last_se0_sess = jiffies;
+			if (srp_reqd)
+				msm_otg_start_timer(motg,
+					TB_TST_SRP, B_TST_SRP);
+			else
+				work = 1;
+		} else if (test_bit(B_BUS_REQ, &motg->inputs) &&
+				otg->gadget->b_hnp_enable &&
+				test_bit(A_BUS_SUSPEND, &motg->inputs)) {
+			pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n");
+			msm_otg_start_timer(motg, TB_ASE0_BRST, B_ASE0_BRST);
+			/* D+ pullup should not be disconnected within 4msec
+			 * after A device suspends the bus. Otherwise PET will
+			 * fail the compliance test.
+			 */
+			udelay(1000);
+			msm_otg_start_peripheral(otg, 0);
+			otg->phy->state = OTG_STATE_B_WAIT_ACON;
+			/*
+			 * start HCD even before A-device enable
+			 * pull-up to meet HNP timings.
+			 */
+			otg->host->is_b_host = 1;
+			msm_otg_start_host(otg, 1);
+		} else if (test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+				   test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("a_bus_suspend && b_sess_vld\n");
+			if (motg->caps & ALLOW_LPM_ON_DEV_SUSPEND) {
+				pm_runtime_put_noidle(otg->phy->dev);
+				pm_runtime_suspend(otg->phy->dev);
+			}
+		} else if (test_bit(ID_C, &motg->inputs)) {
+			msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if (!test_bit(ID, &motg->inputs) ||
+				test_bit(ID_A, &motg->inputs) ||
+				test_bit(ID_B, &motg->inputs) ||
+				!test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("!id || id_a/b || !b_sess_vld\n");
+			msm_otg_del_timer(motg);
+			/*
+			 * A-device is physically disconnected during
+			 * HNP. Remove HCD.
+			 */
+			msm_otg_start_host(otg, 0);
+			otg->host->is_b_host = 0;
+
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			motg->b_last_se0_sess = jiffies;
 			otg->phy->state = OTG_STATE_B_IDLE;
 			msm_otg_reset(otg->phy);
-			schedule_work(w);
+			work = 1;
+		} else if (test_bit(A_CONN, &motg->inputs)) {
+			pr_debug("a_conn\n");
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			otg->phy->state = OTG_STATE_B_HOST;
+			/*
+			 * PET disconnects D+ pullup after reset is generated
+			 * by B device in B_HOST role which is not detected by
+			 * B device. As workaorund , start timer of 300msec
+			 * and stop timer if A device is enumerated else clear
+			 * A_CONN.
+			 */
+			msm_otg_start_timer(motg, TB_TST_CONFIG,
+						B_TST_CONFIG);
+		} else if (test_bit(B_ASE0_BRST, &motg->tmouts)) {
+			pr_debug("b_ase0_brst_tmout\n");
+			pr_info("B HNP fail:No response from A device\n");
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+			otg->host->is_b_host = 0;
+			clear_bit(B_ASE0_BRST, &motg->tmouts);
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			otg_send_event(OTG_EVENT_HNP_FAILED);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			work = 1;
+		} else if (test_bit(ID_C, &motg->inputs)) {
+			msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		if (!test_bit(B_BUS_REQ, &motg->inputs) ||
+				!test_bit(A_CONN, &motg->inputs) ||
+				!test_bit(B_SESS_VLD, &motg->inputs)) {
+			pr_debug("!b_bus_req || !a_conn || !b_sess_vld\n");
+			clear_bit(A_CONN, &motg->inputs);
+			clear_bit(B_BUS_REQ, &motg->inputs);
+			msm_otg_start_host(otg, 0);
+			otg->host->is_b_host = 0;
+			otg->phy->state = OTG_STATE_B_IDLE;
+			msm_otg_reset(otg->phy);
+			work = 1;
+		} else if (test_bit(ID_C, &motg->inputs)) {
+			msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
+		}
+		break;
+	case OTG_STATE_A_IDLE:
+		otg->default_a = 1;
+		if (test_bit(ID, &motg->inputs) &&
+			!test_bit(ID_A, &motg->inputs)) {
+			pr_debug("id && !id_a\n");
+			otg->default_a = 0;
+			clear_bit(A_BUS_DROP, &motg->inputs);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			del_timer_sync(&motg->id_timer);
+			msm_otg_link_reset(motg);
+			msm_chg_enable_aca_intr(motg);
+			msm_otg_notify_charger(motg, 0);
+			work = 1;
+		} else if (!test_bit(A_BUS_DROP, &motg->inputs) &&
+				(test_bit(A_SRP_DET, &motg->inputs) ||
+				 test_bit(A_BUS_REQ, &motg->inputs))) {
+			pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n");
+
+			clear_bit(A_SRP_DET, &motg->inputs);
+			/* Disable SRP detection */
+			writel_relaxed((readl_relaxed(USB_OTGSC) &
+					~OTGSC_INTSTS_MASK) &
+					~OTGSC_DPIE, USB_OTGSC);
+
+			otg->phy->state = OTG_STATE_A_WAIT_VRISE;
+			/* VBUS should not be supplied before end of SRP pulse
+			 * generated by PET, if not complaince test fail.
+			 */
+			usleep_range(10000, 12000);
+			/* ACA: ID_A: Stop charging untill enumeration */
+			if (test_bit(ID_A, &motg->inputs))
+				msm_otg_notify_charger(motg, 0);
+			else
+				msm_hsusb_vbus_power(motg, 1);
+			msm_otg_start_timer(motg, TA_WAIT_VRISE, A_WAIT_VRISE);
+		} else {
+			pr_debug("No session requested\n");
+			clear_bit(A_BUS_DROP, &motg->inputs);
+			if (test_bit(ID_A, &motg->inputs)) {
+					msm_otg_notify_charger(motg,
+							IDEV_ACA_CHG_MAX);
+			} else if (!test_bit(ID, &motg->inputs)) {
+				msm_otg_notify_charger(motg, 0);
+				/*
+				 * A-device is not providing power on VBUS.
+				 * Enable SRP detection.
+				 */
+				writel_relaxed(0x13, USB_USBMODE);
+				writel_relaxed((readl_relaxed(USB_OTGSC) &
+						~OTGSC_INTSTS_MASK) |
+						OTGSC_DPIE, USB_OTGSC);
+				mb();
+			}
+		}
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_WAIT_VRISE, &motg->tmouts)) {
+			pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n");
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			msm_otg_del_timer(motg);
+			msm_hsusb_vbus_power(motg, 0);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("a_vbus_vld\n");
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			msm_otg_start_host(otg, 1);
+			msm_chg_enable_aca_det(motg);
+			msm_chg_disable_aca_intr(motg);
+			mod_timer(&motg->id_timer, ID_TIMER_FREQ);
+			if (msm_chg_check_aca_intr(motg))
+				work = 1;
+		}
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_WAIT_BCON, &motg->tmouts)) {
+			pr_debug("(id && id_a/b/c) || a_bus_drop ||"
+					"a_wait_bcon_tmout\n");
+			if (test_bit(A_WAIT_BCON, &motg->tmouts)) {
+				pr_info("Device No Response\n");
+				otg_send_event(OTG_EVENT_DEV_CONN_TMOUT);
+			}
+			msm_otg_del_timer(motg);
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			clear_bit(B_CONN, &motg->inputs);
+			msm_otg_start_host(otg, 0);
+			/*
+			 * ACA: ID_A with NO accessory, just the A plug is
+			 * attached to ACA: Use IDCHG_MAX for charging
+			 */
+			if (test_bit(ID_A, &motg->inputs))
+				msm_otg_notify_charger(motg, IDEV_CHG_MIN);
+			else
+				msm_hsusb_vbus_power(motg, 0);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			clear_bit(B_CONN, &motg->inputs);
+			msm_otg_del_timer(motg);
+			msm_otg_start_host(otg, 0);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_reset(otg->phy);
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 0);
+		} else if (!test_bit(A_BUS_REQ, &motg->inputs)) {
+			/*
+			 * If TA_WAIT_BCON is infinite, we don;t
+			 * turn off VBUS. Enter low power mode.
+			 */
+			if (TA_WAIT_BCON < 0)
+				pm_runtime_put_sync(otg->phy->dev);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 1);
 		}
 		break;
 	case OTG_STATE_A_HOST:
-		dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n");
-		if (test_bit(ID, &motg->inputs)) {
-			msm_otg_start_host(otg->phy, 0);
-			otg->phy->state = OTG_STATE_B_IDLE;
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs)) {
+			pr_debug("id_a/b/c || a_bus_drop\n");
+			clear_bit(B_CONN, &motg->inputs);
+			clear_bit(A_BUS_REQ, &motg->inputs);
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_host(otg, 0);
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			clear_bit(B_CONN, &motg->inputs);
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_start_host(otg, 0);
 			msm_otg_reset(otg->phy);
-			schedule_work(w);
+		} else if (!test_bit(A_BUS_REQ, &motg->inputs)) {
+			/*
+			 * a_bus_req is de-asserted when root hub is
+			 * suspended or HNP is in progress.
+			 */
+			pr_debug("!a_bus_req\n");
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_SUSPEND;
+			if (otg->host->b_hnp_enable)
+				msm_otg_start_timer(motg, TA_AIDL_BDIS,
+						A_AIDL_BDIS);
+			else
+				pm_runtime_put_sync(otg->phy->dev);
+		} else if (!test_bit(B_CONN, &motg->inputs)) {
+			pr_debug("!b_conn\n");
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+			if (msm_chg_check_aca_intr(motg))
+				work = 1;
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_otg_del_timer(motg);
+			msm_hsusb_vbus_power(motg, 0);
+			if (motg->chg_type == USB_ACA_DOCK_CHARGER)
+				msm_otg_notify_charger(motg,
+						IDEV_ACA_CHG_MAX);
+			else
+				msm_otg_notify_charger(motg,
+						IDEV_CHG_MIN - motg->mA_port);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
+			msm_hsusb_vbus_power(motg, 1);
+		}
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_AIDL_BDIS, &motg->tmouts)) {
+			pr_debug("id_a/b/c || a_bus_drop ||"
+					"a_aidl_bdis_tmout\n");
+			msm_otg_del_timer(motg);
+			clear_bit(B_CONN, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			msm_otg_del_timer(motg);
+			clear_bit(B_CONN, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+		} else if (!test_bit(B_CONN, &motg->inputs) &&
+				otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && b_hnp_enable");
+			otg->phy->state = OTG_STATE_A_PERIPHERAL;
+			msm_otg_host_hnp_enable(otg, 1);
+			otg->gadget->is_a_peripheral = 1;
+			msm_otg_start_peripheral(otg, 1);
+		} else if (!test_bit(B_CONN, &motg->inputs) &&
+				!otg->host->b_hnp_enable) {
+			pr_debug("!b_conn && !b_hnp_enable");
+			/*
+			 * bus request is dropped during suspend.
+			 * acquire again for next device.
+			 */
+			set_bit(A_BUS_REQ, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 0);
+			msm_otg_notify_charger(motg,
+					IDEV_CHG_MIN - motg->mA_port);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			msm_otg_notify_charger(motg, 0);
+			msm_hsusb_vbus_power(motg, 1);
+		}
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs)) {
+			pr_debug("id _f/b/c || a_bus_drop\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget->is_a_peripheral = 0;
+			msm_otg_start_host(otg, 0);
+			msm_otg_reset(otg->phy);
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+		} else if (!test_bit(A_VBUS_VLD, &motg->inputs)) {
+			pr_debug("!a_vbus_vld\n");
+			/* Clear BIDL_ADIS timer */
+			msm_otg_del_timer(motg);
+			otg->phy->state = OTG_STATE_A_VBUS_ERR;
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget->is_a_peripheral = 0;
+			msm_otg_start_host(otg, 0);
+		} else if (test_bit(A_BIDL_ADIS, &motg->tmouts)) {
+			pr_debug("a_bidl_adis_tmout\n");
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget->is_a_peripheral = 0;
+			otg->phy->state = OTG_STATE_A_WAIT_BCON;
+			set_bit(A_BUS_REQ, &motg->inputs);
+			msm_otg_host_hnp_enable(otg, 0);
+			if (TA_WAIT_BCON > 0)
+				msm_otg_start_timer(motg, TA_WAIT_BCON,
+					A_WAIT_BCON);
+		} else if (test_bit(ID_A, &motg->inputs)) {
+			msm_hsusb_vbus_power(motg, 0);
+			msm_otg_notify_charger(motg,
+					IDEV_CHG_MIN - motg->mA_port);
+		} else if (!test_bit(ID, &motg->inputs)) {
+			msm_otg_notify_charger(motg, 0);
+			msm_hsusb_vbus_power(motg, 1);
+		}
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (test_bit(A_WAIT_VFALL, &motg->tmouts)) {
+			clear_bit(A_VBUS_VLD, &motg->inputs);
+			otg->phy->state = OTG_STATE_A_IDLE;
+			work = 1;
+		}
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if ((test_bit(ID, &motg->inputs) &&
+				!test_bit(ID_A, &motg->inputs)) ||
+				test_bit(A_BUS_DROP, &motg->inputs) ||
+				test_bit(A_CLR_ERR, &motg->inputs)) {
+			otg->phy->state = OTG_STATE_A_WAIT_VFALL;
+			if (!test_bit(ID_A, &motg->inputs))
+				msm_hsusb_vbus_power(motg, 0);
+			msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL);
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			motg->chg_type = USB_INVALID_CHARGER;
+			msm_otg_notify_charger(motg, 0);
 		}
 		break;
 	default:
 		break;
 	}
+	if (work)
+		queue_work(system_nrt_wq, &motg->sm_work);
 }
 
 static irqreturn_t msm_otg_irq(int irq, void *data)
 {
 	struct msm_otg *motg = data;
-	struct usb_phy *phy = &motg->phy;
-	u32 otgsc = 0;
+	struct usb_otg *otg = motg->phy.otg;
+	u32 otgsc = 0, usbsts, pc;
+	bool work = 0;
+	irqreturn_t ret = IRQ_HANDLED;
 
 	if (atomic_read(&motg->in_lpm)) {
+		pr_debug("OTG IRQ: in LPM\n");
 		disable_irq_nosync(irq);
 		motg->async_int = 1;
-		pm_runtime_get(phy->dev);
+		if (atomic_read(&motg->pm_suspended))
+			motg->sm_work_pending = true;
+		else
+			pm_request_resume(otg->phy->dev);
 		return IRQ_HANDLED;
 	}
 
+	usbsts = readl(USB_USBSTS);
 	otgsc = readl(USB_OTGSC);
-	if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS)))
+
+	if (!(otgsc & OTG_OTGSTS_MASK) && !(usbsts & OTG_USBSTS_MASK))
 		return IRQ_NONE;
 
 	if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) {
-		if (otgsc & OTGSC_ID)
+		if (otgsc & OTGSC_ID) {
+			pr_debug("Id set\n");
 			set_bit(ID, &motg->inputs);
-		else
+		} else {
+			pr_debug("Id clear\n");
+			/*
+			 * Assert a_bus_req to supply power on
+			 * VBUS when Micro/Mini-A cable is connected
+			 * with out user intervention.
+			 */
+			set_bit(A_BUS_REQ, &motg->inputs);
 			clear_bit(ID, &motg->inputs);
-		dev_dbg(phy->dev, "ID set/clear\n");
-		pm_runtime_get_noresume(phy->dev);
-	} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
-		if (otgsc & OTGSC_BSV)
+			msm_chg_enable_aca_det(motg);
+		}
+		writel_relaxed(otgsc, USB_OTGSC);
+		work = 1;
+	} else if (otgsc & OTGSC_DPIS) {
+		pr_debug("DPIS detected\n");
+		writel_relaxed(otgsc, USB_OTGSC);
+		set_bit(A_SRP_DET, &motg->inputs);
+		set_bit(A_BUS_REQ, &motg->inputs);
+		work = 1;
+	} else if (otgsc & OTGSC_BSVIS) {
+		writel_relaxed(otgsc, USB_OTGSC);
+		/*
+		 * BSV interrupt comes when operating as an A-device
+		 * (VBUS on/off).
+		 * But, handle BSV when charger is removed from ACA in ID_A
+		 */
+		if ((otg->phy->state >= OTG_STATE_A_IDLE) &&
+			!test_bit(ID_A, &motg->inputs))
+			return IRQ_HANDLED;
+		if (otgsc & OTGSC_BSV) {
+			pr_debug("BSV set\n");
 			set_bit(B_SESS_VLD, &motg->inputs);
-		else
+		} else {
+			pr_debug("BSV clear\n");
 			clear_bit(B_SESS_VLD, &motg->inputs);
-		dev_dbg(phy->dev, "BSV set/clear\n");
-		pm_runtime_get_noresume(phy->dev);
+			clear_bit(A_BUS_SUSPEND, &motg->inputs);
+
+			msm_chg_check_aca_intr(motg);
+		}
+		work = 1;
+	} else if (usbsts & STS_PCI) {
+		pc = readl_relaxed(USB_PORTSC);
+		pr_debug("portsc = %x\n", pc);
+		ret = IRQ_NONE;
+		/*
+		 * HCD Acks PCI interrupt. We use this to switch
+		 * between different OTG states.
+		 */
+		work = 1;
+		switch (otg->phy->state) {
+		case OTG_STATE_A_SUSPEND:
+			if (otg->host->b_hnp_enable && (pc & PORTSC_CSC) &&
+					!(pc & PORTSC_CCS)) {
+				pr_debug("B_CONN clear\n");
+				clear_bit(B_CONN, &motg->inputs);
+				msm_otg_del_timer(motg);
+			}
+			break;
+		case OTG_STATE_A_PERIPHERAL:
+			/*
+			 * A-peripheral observed activity on bus.
+			 * clear A_BIDL_ADIS timer.
+			 */
+			msm_otg_del_timer(motg);
+			work = 0;
+			break;
+		case OTG_STATE_B_WAIT_ACON:
+			if ((pc & PORTSC_CSC) && (pc & PORTSC_CCS)) {
+				pr_debug("A_CONN set\n");
+				set_bit(A_CONN, &motg->inputs);
+				/* Clear ASE0_BRST timer */
+				msm_otg_del_timer(motg);
+			}
+			break;
+		case OTG_STATE_B_HOST:
+			if ((pc & PORTSC_CSC) && !(pc & PORTSC_CCS)) {
+				pr_debug("A_CONN clear\n");
+				clear_bit(A_CONN, &motg->inputs);
+				msm_otg_del_timer(motg);
+			}
+			break;
+		case OTG_STATE_A_WAIT_BCON:
+			if (TA_WAIT_BCON < 0)
+				set_bit(A_BUS_REQ, &motg->inputs);
+		default:
+			work = 0;
+			break;
+		}
+	} else if (usbsts & STS_URI) {
+		ret = IRQ_NONE;
+		switch (otg->phy->state) {
+		case OTG_STATE_A_PERIPHERAL:
+			/*
+			 * A-peripheral observed activity on bus.
+			 * clear A_BIDL_ADIS timer.
+			 */
+			msm_otg_del_timer(motg);
+			work = 0;
+			break;
+		default:
+			work = 0;
+			break;
+		}
+	} else if (usbsts & STS_SLI) {
+		ret = IRQ_NONE;
+		work = 0;
+		switch (otg->phy->state) {
+		case OTG_STATE_B_PERIPHERAL:
+			if (otg->gadget->b_hnp_enable) {
+				set_bit(A_BUS_SUSPEND, &motg->inputs);
+				set_bit(B_BUS_REQ, &motg->inputs);
+				work = 1;
+			}
+			break;
+		case OTG_STATE_A_PERIPHERAL:
+			msm_otg_start_timer(motg, TA_BIDL_ADIS,
+					A_BIDL_ADIS);
+			break;
+		default:
+			break;
+		}
+	} else if ((usbsts & PHY_ALT_INT)) {
+		writel_relaxed(PHY_ALT_INT, USB_USBSTS);
+		if (msm_chg_check_aca_intr(motg))
+			work = 1;
+		ret = IRQ_HANDLED;
+	}
+	if (work)
+		queue_work(system_nrt_wq, &motg->sm_work);
+
+	return ret;
+}
+
+static void msm_otg_set_vbus_state(int online)
+{
+	static bool init;
+	struct msm_otg *motg = the_msm_otg;
+
+	if (online) {
+		pr_debug("PMIC: BSV set\n");
+		set_bit(B_SESS_VLD, &motg->inputs);
+	} else {
+		pr_debug("PMIC: BSV clear\n");
+		clear_bit(B_SESS_VLD, &motg->inputs);
 	}
 
-	writel(otgsc, USB_OTGSC);
-	schedule_work(&motg->sm_work);
+	if (!init) {
+		init = true;
+		complete(&pmic_vbus_init);
+		pr_debug("PMIC: BSV init complete\n");
+		return;
+	}
+
+	if (atomic_read(&motg->pm_suspended))
+		motg->sm_work_pending = true;
+	else
+		queue_work(system_nrt_wq, &motg->sm_work);
+}
+
+static irqreturn_t msm_pmic_id_irq(int irq, void *data)
+{
+	struct msm_otg *motg = data;
+
+	if (aca_id_turned_on)
+		return IRQ_HANDLED;
+
+	if (irq_read_line(motg->pdata->pmic_id_irq)) {
+		pr_debug("PMIC: ID set\n");
+		set_bit(ID, &motg->inputs);
+	} else {
+		pr_debug("PMIC: ID clear\n");
+		clear_bit(ID, &motg->inputs);
+		set_bit(A_BUS_REQ, &motg->inputs);
+	}
+
+	if (motg->phy.state != OTG_STATE_UNDEFINED) {
+		if (atomic_read(&motg->pm_suspended))
+			motg->sm_work_pending = true;
+		else
+			queue_work(system_nrt_wq, &motg->sm_work);
+	}
+
 	return IRQ_HANDLED;
 }
 
 static int msm_otg_mode_show(struct seq_file *s, void *unused)
 {
 	struct msm_otg *motg = s->private;
-	struct usb_otg *otg = motg->phy.otg;
+	struct usb_phy *phy = &motg->phy;
 
-	switch (otg->phy->state) {
+	switch (phy->state) {
 	case OTG_STATE_A_HOST:
 		seq_printf(s, "host\n");
 		break;
@@ -1311,7 +2779,7 @@
 	struct seq_file *s = file->private_data;
 	struct msm_otg *motg = s->private;
 	char buf[16];
-	struct usb_otg *otg = motg->phy.otg;
+	struct usb_phy *phy = &motg->phy;
 	int status = count;
 	enum usb_mode_type req_mode;
 
@@ -1335,7 +2803,7 @@
 
 	switch (req_mode) {
 	case USB_NONE:
-		switch (otg->phy->state) {
+		switch (phy->state) {
 		case OTG_STATE_A_HOST:
 		case OTG_STATE_B_PERIPHERAL:
 			set_bit(ID, &motg->inputs);
@@ -1346,7 +2814,7 @@
 		}
 		break;
 	case USB_PERIPHERAL:
-		switch (otg->phy->state) {
+		switch (phy->state) {
 		case OTG_STATE_B_IDLE:
 		case OTG_STATE_A_HOST:
 			set_bit(ID, &motg->inputs);
@@ -1357,7 +2825,7 @@
 		}
 		break;
 	case USB_HOST:
-		switch (otg->phy->state) {
+		switch (phy->state) {
 		case OTG_STATE_B_IDLE:
 		case OTG_STATE_B_PERIPHERAL:
 			clear_bit(ID, &motg->inputs);
@@ -1370,8 +2838,8 @@
 		goto out;
 	}
 
-	pm_runtime_get_sync(otg->phy->dev);
-	schedule_work(&motg->sm_work);
+	pm_runtime_resume(phy->dev);
+	queue_work(system_nrt_wq, &motg->sm_work);
 out:
 	return status;
 }
@@ -1384,31 +2852,320 @@
 	.release = single_release,
 };
 
+static int msm_otg_show_otg_state(struct seq_file *s, void *unused)
+{
+	struct msm_otg *motg = s->private;
+	struct usb_phy *phy = &motg->phy;
+
+	seq_printf(s, "%s\n", otg_state_string(phy->state));
+	return 0;
+}
+
+static int msm_otg_otg_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_show_otg_state, inode->i_private);
+}
+
+const struct file_operations msm_otg_state_fops = {
+	.open = msm_otg_otg_state_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int msm_otg_show_chg_type(struct seq_file *s, void *unused)
+{
+	struct msm_otg *motg = s->private;
+
+	seq_printf(s, "%s\n", chg_to_string(motg->chg_type));
+	return 0;
+}
+
+static int msm_otg_chg_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_show_chg_type, inode->i_private);
+}
+
+const struct file_operations msm_otg_chg_fops = {
+	.open = msm_otg_chg_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int msm_otg_aca_show(struct seq_file *s, void *unused)
+{
+	if (debug_aca_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int msm_otg_aca_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_aca_show, inode->i_private);
+}
+
+static ssize_t msm_otg_aca_write(struct file *file, const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char buf[8];
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6))
+		debug_aca_enabled = true;
+	else
+		debug_aca_enabled = false;
+
+	return count;
+}
+
+const struct file_operations msm_otg_aca_fops = {
+	.open = msm_otg_aca_open,
+	.read = seq_read,
+	.write = msm_otg_aca_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int msm_otg_bus_show(struct seq_file *s, void *unused)
+{
+	if (debug_bus_voting_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int msm_otg_bus_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_bus_show, inode->i_private);
+}
+
+static ssize_t msm_otg_bus_write(struct file *file, const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char buf[8];
+	int ret;
+	struct seq_file *s = file->private_data;
+	struct msm_otg *motg = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6)) {
+		/* Do not vote here. Let OTG statemachine decide when to vote */
+		debug_bus_voting_enabled = true;
+	} else {
+		debug_bus_voting_enabled = false;
+		if (motg->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					motg->bus_perf_client, 0);
+			if (ret)
+				dev_err(motg->phy.dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations msm_otg_bus_fops = {
+	.open = msm_otg_bus_open,
+	.read = seq_read,
+	.write = msm_otg_bus_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
 static struct dentry *msm_otg_dbg_root;
-static struct dentry *msm_otg_dbg_mode;
 
 static int msm_otg_debugfs_init(struct msm_otg *motg)
 {
+	struct dentry *msm_otg_dentry;
+
 	msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL);
 
 	if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root))
 		return -ENODEV;
 
-	msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR,
-				msm_otg_dbg_root, motg, &msm_otg_mode_fops);
-	if (!msm_otg_dbg_mode) {
-		debugfs_remove(msm_otg_dbg_root);
-		msm_otg_dbg_root = NULL;
+	if (motg->pdata->mode == USB_OTG &&
+		motg->pdata->otg_control == OTG_USER_CONTROL) {
+
+		msm_otg_dentry = debugfs_create_file("mode", S_IRUGO |
+			S_IWUSR, msm_otg_dbg_root, motg,
+			&msm_otg_mode_fops);
+
+		if (!msm_otg_dentry) {
+			debugfs_remove(msm_otg_dbg_root);
+			msm_otg_dbg_root = NULL;
+			return -ENODEV;
+		}
+	}
+
+	msm_otg_dentry = debugfs_create_file("chg_type", S_IRUGO,
+		msm_otg_dbg_root, motg,
+		&msm_otg_chg_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
 		return -ENODEV;
 	}
 
+	msm_otg_dentry = debugfs_create_file("aca", S_IRUGO | S_IWUSR,
+		msm_otg_dbg_root, motg,
+		&msm_otg_aca_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
+		return -ENODEV;
+	}
+
+	msm_otg_dentry = debugfs_create_file("bus_voting", S_IRUGO | S_IWUSR,
+		msm_otg_dbg_root, motg,
+		&msm_otg_bus_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
+		return -ENODEV;
+	}
+
+	msm_otg_dentry = debugfs_create_file("otg_state", S_IRUGO,
+				msm_otg_dbg_root, motg, &msm_otg_state_fops);
+
+	if (!msm_otg_dentry) {
+		debugfs_remove_recursive(msm_otg_dbg_root);
+		return -ENODEV;
+	}
 	return 0;
 }
 
 static void msm_otg_debugfs_cleanup(void)
 {
-	debugfs_remove(msm_otg_dbg_mode);
-	debugfs_remove(msm_otg_dbg_root);
+	debugfs_remove_recursive(msm_otg_dbg_root);
+}
+
+static u64 msm_otg_dma_mask = DMA_BIT_MASK(64);
+static struct platform_device *msm_otg_add_pdev(
+		struct platform_device *ofdev, const char *name)
+{
+	struct platform_device *pdev;
+	const struct resource *res = ofdev->resource;
+	unsigned int num = ofdev->num_resources;
+	int retval;
+
+	pdev = platform_device_alloc(name, -1);
+	if (!pdev) {
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	pdev->dev.dma_mask = &msm_otg_dma_mask;
+
+	if (num) {
+		retval = platform_device_add_resources(pdev, res, num);
+		if (retval)
+			goto error;
+	}
+
+	retval = platform_device_add(pdev);
+	if (retval)
+		goto error;
+
+	return pdev;
+
+error:
+	platform_device_put(pdev);
+	return ERR_PTR(retval);
+}
+
+static int msm_otg_setup_devices(struct platform_device *ofdev,
+		enum usb_mode_type mode, bool init)
+{
+	const char *gadget_name = "msm_hsusb";
+	const char *host_name = "msm_hsusb_host";
+	static struct platform_device *gadget_pdev;
+	static struct platform_device *host_pdev;
+	int retval = 0;
+
+	if (!init) {
+		if (gadget_pdev)
+			platform_device_unregister(gadget_pdev);
+		if (host_pdev)
+			platform_device_unregister(host_pdev);
+		return 0;
+	}
+
+	switch (mode) {
+	case USB_OTG:
+		/* fall through */
+	case USB_PERIPHERAL:
+		gadget_pdev = msm_otg_add_pdev(ofdev, gadget_name);
+		if (IS_ERR(gadget_pdev)) {
+			retval = PTR_ERR(gadget_pdev);
+			break;
+		}
+		if (mode == USB_PERIPHERAL)
+			break;
+		/* fall through */
+	case USB_HOST:
+		host_pdev = msm_otg_add_pdev(ofdev, host_name);
+		if (IS_ERR(host_pdev)) {
+			retval = PTR_ERR(host_pdev);
+			if (mode == USB_OTG)
+				platform_device_unregister(gadget_pdev);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return retval;
+}
+
+struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_otg_platform_data *pdata;
+	int len = 0;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		pr_err("unable to allocate platform data\n");
+		return NULL;
+	}
+	of_get_property(node, "qcom,hsusb-otg-phy-init-seq", &len);
+	if (len) {
+		pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+		if (!pdata->phy_init_seq)
+			return NULL;
+		of_property_read_u32_array(node, "qcom,hsusb-otg-phy-init-seq",
+				pdata->phy_init_seq,
+				len/sizeof(*pdata->phy_init_seq));
+	}
+	of_property_read_u32(node, "qcom,hsusb-otg-power-budget",
+				&pdata->power_budget);
+	of_property_read_u32(node, "qcom,hsusb-otg-mode",
+				&pdata->mode);
+	of_property_read_u32(node, "qcom,hsusb-otg-otg-control",
+				&pdata->otg_control);
+	of_property_read_u32(node, "qcom,hsusb-otg-default-mode",
+				&pdata->default_mode);
+	of_property_read_u32(node, "qcom,hsusb-otg-phy-type",
+				&pdata->phy_type);
+	of_property_read_u32(node, "qcom,hsusb-otg-pmic-id-irq",
+				&pdata->pmic_id_irq);
+	return pdata;
 }
 
 static int __init msm_otg_probe(struct platform_device *pdev)
@@ -1417,11 +3174,25 @@
 	struct resource *res;
 	struct msm_otg *motg;
 	struct usb_phy *phy;
+	struct msm_otg_platform_data *pdata;
 
 	dev_info(&pdev->dev, "msm_otg probe\n");
-	if (!pdev->dev.platform_data) {
+
+	if (pdev->dev.of_node) {
+		dev_dbg(&pdev->dev, "device tree enabled\n");
+		pdata = msm_otg_dt_to_pdata(pdev);
+		if (!pdata)
+			return -ENOMEM;
+		ret = msm_otg_setup_devices(pdev, pdata->mode, true);
+		if (ret) {
+			dev_err(&pdev->dev, "devices setup failed\n");
+			return ret;
+		}
+	} else if (!pdev->dev.platform_data) {
 		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
 		return -ENODEV;
+	} else {
+		pdata = pdev->dev.platform_data;
 	}
 
 	motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
@@ -1432,75 +3203,80 @@
 
 	motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
 	if (!motg->phy.otg) {
-		dev_err(&pdev->dev, "unable to allocate msm_otg\n");
-		return -ENOMEM;
-	}
-
-	motg->pdata = pdev->dev.platform_data;
-	phy = &motg->phy;
-	phy->dev = &pdev->dev;
-
-	motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk");
-	if (IS_ERR(motg->phy_reset_clk)) {
-		dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
-		ret = PTR_ERR(motg->phy_reset_clk);
+		dev_err(&pdev->dev, "unable to allocate usb_otg\n");
+		ret = -ENOMEM;
 		goto free_motg;
 	}
 
-	motg->clk = clk_get(&pdev->dev, "usb_hs_clk");
-	if (IS_ERR(motg->clk)) {
-		dev_err(&pdev->dev, "failed to get usb_hs_clk\n");
-		ret = PTR_ERR(motg->clk);
-		goto put_phy_reset_clk;
-	}
-	clk_set_rate(motg->clk, 60000000);
+	the_msm_otg = motg;
+	motg->pdata = pdata;
+	phy = &motg->phy;
+	phy->dev = &pdev->dev;
 
 	/*
-	 * If USB Core is running its protocol engine based on CORE CLK,
+	 * ACA ID_GND threshold range is overlapped with OTG ID_FLOAT.  Hence
+	 * PHY treat ACA ID_GND as float and no interrupt is generated.  But
+	 * PMIC can detect ACA ID_GND and generate an interrupt.
+	 */
+	if (aca_enabled() && motg->pdata->otg_control != OTG_PMIC_CONTROL) {
+		dev_err(&pdev->dev, "ACA can not be enabled without PMIC\n");
+		ret = -EINVAL;
+		goto free_otg;
+	}
+
+	/* initialize reset counter */
+	motg->reset_counter = 0;
+
+	/* Some targets don't support PHY clock. */
+	motg->phy_reset_clk = clk_get(&pdev->dev, "phy_clk");
+	if (IS_ERR(motg->phy_reset_clk))
+		dev_err(&pdev->dev, "failed to get phy_clk\n");
+
+	/*
+	 * Targets on which link uses asynchronous reset methodology,
+	 * free running clock is not required during the reset.
+	 */
+	motg->clk = clk_get(&pdev->dev, "alt_core_clk");
+	if (IS_ERR(motg->clk))
+		dev_dbg(&pdev->dev, "alt_core_clk is not present\n");
+	else
+		clk_set_rate(motg->clk, 60000000);
+
+	/*
+	 * USB Core is running its protocol engine based on CORE CLK,
 	 * CORE CLK  must be running at >55Mhz for correct HSUSB
 	 * operation and USB core cannot tolerate frequency changes on
 	 * CORE CLK. For such USB cores, vote for maximum clk frequency
 	 * on pclk source
 	 */
-	 if (motg->pdata->pclk_src_name) {
-		motg->pclk_src = clk_get(&pdev->dev,
-			motg->pdata->pclk_src_name);
-		if (IS_ERR(motg->pclk_src))
-			goto put_clk;
-		clk_set_rate(motg->pclk_src, INT_MAX);
-		clk_enable(motg->pclk_src);
-	} else
-		motg->pclk_src = ERR_PTR(-ENOENT);
-
-
-	motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
-	if (IS_ERR(motg->pclk)) {
-		dev_err(&pdev->dev, "failed to get usb_hs_pclk\n");
-		ret = PTR_ERR(motg->pclk);
-		goto put_pclk_src;
-	}
-
-	/*
-	 * USB core clock is not present on all MSM chips. This
-	 * clock is introduced to remove the dependency on AXI
-	 * bus frequency.
-	 */
-	motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk");
-	if (IS_ERR(motg->core_clk))
+	motg->core_clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(motg->core_clk)) {
 		motg->core_clk = NULL;
+		dev_err(&pdev->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(motg->core_clk);
+		goto put_clk;
+	}
+	clk_set_rate(motg->core_clk, INT_MAX);
+
+	motg->pclk = clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(motg->pclk)) {
+		dev_err(&pdev->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(motg->pclk);
+		goto put_core_clk;
+	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
 		dev_err(&pdev->dev, "failed to get platform resource mem\n");
 		ret = -ENODEV;
-		goto put_core_clk;
+		goto put_pclk;
 	}
 
 	motg->regs = ioremap(res->start, resource_size(res));
 	if (!motg->regs) {
 		dev_err(&pdev->dev, "ioremap failed\n");
 		ret = -ENOMEM;
-		goto put_core_clk;
+		goto put_pclk;
 	}
 	dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs);
 
@@ -1511,49 +3287,97 @@
 		goto free_regs;
 	}
 
-	clk_enable(motg->clk);
-	clk_enable(motg->pclk);
+	motg->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
+	if (IS_ERR(motg->xo_handle)) {
+		dev_err(&pdev->dev, "%s not able to get the handle "
+			"to vote for TCXO D0 buffer\n", __func__);
+		ret = PTR_ERR(motg->xo_handle);
+		goto free_regs;
+	}
 
-	ret = msm_hsusb_init_vddcx(motg, 1);
+	ret = msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO "
+			"D0 buffer%d\n", __func__, ret);
+		goto free_xo_handle;
+	}
+
+	clk_prepare_enable(motg->pclk);
+
+	motg->vdd_type = VDDCX_CORNER;
+	hsusb_vddcx = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
+	if (IS_ERR(hsusb_vddcx)) {
+		hsusb_vddcx = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
+		if (IS_ERR(hsusb_vddcx)) {
+			dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
+			goto devote_xo_handle;
+		}
+		motg->vdd_type = VDDCX;
+	}
+
+	ret = msm_hsusb_config_vddcx(1);
 	if (ret) {
 		dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
-		goto free_regs;
+		goto devote_xo_handle;
+	}
+
+	ret = regulator_enable(hsusb_vddcx);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable the hsusb vddcx\n");
+		goto free_config_vddcx;
 	}
 
 	ret = msm_hsusb_ldo_init(motg, 1);
 	if (ret) {
 		dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
-		goto vddcx_exit;
-	}
-	ret = msm_hsusb_ldo_set_mode(1);
-	if (ret) {
-		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
-		goto ldo_exit;
+		goto free_hsusb_vddcx;
 	}
 
-	if (motg->core_clk)
-		clk_enable(motg->core_clk);
+	if (pdata->mhl_enable) {
+		mhl_analog_switch = devm_regulator_get(motg->phy.dev,
+							"mhl_ext_3p3v");
+		if (IS_ERR(mhl_analog_switch)) {
+			dev_err(&pdev->dev, "Unable to get mhl_analog_switch\n");
+			goto free_ldo_init;
+		}
+	}
+
+	ret = msm_hsusb_ldo_enable(motg, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+		goto free_ldo_init;
+	}
+	clk_prepare_enable(motg->core_clk);
 
 	writel(0, USB_USBINTR);
 	writel(0, USB_OTGSC);
+	/* Ensure that above STOREs are completed before enabling interrupts */
+	mb();
 
+	wake_lock_init(&motg->wlock, WAKE_LOCK_SUSPEND, "msm_otg");
+	msm_otg_init_timer(motg);
 	INIT_WORK(&motg->sm_work, msm_otg_sm_work);
 	INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
+	setup_timer(&motg->id_timer, msm_otg_id_timer_func,
+				(unsigned long) motg);
 	ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
 					"msm_otg", motg);
 	if (ret) {
 		dev_err(&pdev->dev, "request irq failed\n");
-		goto disable_clks;
+		goto destroy_wlock;
 	}
 
 	phy->init = msm_otg_reset;
 	phy->set_power = msm_otg_set_power;
+	phy->set_suspend = msm_otg_set_suspend;
 
 	phy->io_ops = &msm_otg_io_ops;
 
 	phy->otg->phy = &motg->phy;
 	phy->otg->set_host = msm_otg_set_host;
 	phy->otg->set_peripheral = msm_otg_set_peripheral;
+	phy->otg->start_hnp = msm_otg_start_hnp;
+	phy->otg->start_srp = msm_otg_start_srp;
 
 	ret = usb_set_transceiver(&motg->phy);
 	if (ret) {
@@ -1561,47 +3385,104 @@
 		goto free_irq;
 	}
 
-	platform_set_drvdata(pdev, motg);
-	device_init_wakeup(&pdev->dev, 1);
-
 	if (motg->pdata->mode == USB_OTG &&
-			motg->pdata->otg_control == OTG_USER_CONTROL) {
-		ret = msm_otg_debugfs_init(motg);
-		if (ret)
-			dev_dbg(&pdev->dev, "mode debugfs file is"
-					"not available\n");
+		motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+		if (motg->pdata->pmic_id_irq) {
+			ret = request_irq(motg->pdata->pmic_id_irq,
+						msm_pmic_id_irq,
+						IRQF_TRIGGER_RISING |
+						IRQF_TRIGGER_FALLING,
+						"msm_otg", motg);
+			if (ret) {
+				dev_err(&pdev->dev, "request irq failed for PMIC ID\n");
+				goto remove_phy;
+			}
+		} else {
+			ret = -ENODEV;
+			dev_err(&pdev->dev, "PMIC IRQ for ID notifications doesn't exist\n");
+			goto remove_phy;
+		}
 	}
 
+	msm_hsusb_mhl_switch_enable(motg, 1);
+
+	platform_set_drvdata(pdev, motg);
+	device_init_wakeup(&pdev->dev, 1);
+	motg->mA_port = IUNIT;
+
+	ret = msm_otg_debugfs_init(motg);
+	if (ret)
+		dev_dbg(&pdev->dev, "mode debugfs file is"
+			"not available\n");
+
+	if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
+		pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
+
+	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY) {
+		if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
+			(!(motg->pdata->mode == USB_OTG) ||
+			 motg->pdata->pmic_id_irq))
+			motg->caps = ALLOW_PHY_POWER_COLLAPSE |
+				ALLOW_PHY_RETENTION;
+
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+			motg->caps = ALLOW_PHY_RETENTION;
+	}
+
+	if (motg->pdata->enable_lpm_on_dev_suspend)
+		motg->caps |= ALLOW_LPM_ON_DEV_SUSPEND;
+
+	wake_lock(&motg->wlock);
 	pm_runtime_set_active(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
+	if (motg->pdata->bus_scale_table) {
+		motg->bus_perf_client =
+		    msm_bus_scale_register_client(motg->pdata->bus_scale_table);
+		if (!motg->bus_perf_client)
+			dev_err(motg->phy.dev, "%s: Failed to register BUS "
+						"scaling client!!\n", __func__);
+		else
+			debug_bus_voting_enabled = true;
+	}
+
 	return 0;
+
+remove_phy:
+	usb_set_transceiver(NULL);
 free_irq:
 	free_irq(motg->irq, motg);
-disable_clks:
-	clk_disable(motg->pclk);
-	clk_disable(motg->clk);
-ldo_exit:
+destroy_wlock:
+	wake_lock_destroy(&motg->wlock);
+	clk_disable_unprepare(motg->core_clk);
+	msm_hsusb_ldo_enable(motg, 0);
+free_ldo_init:
 	msm_hsusb_ldo_init(motg, 0);
-vddcx_exit:
-	msm_hsusb_init_vddcx(motg, 0);
+free_hsusb_vddcx:
+	regulator_disable(hsusb_vddcx);
+free_config_vddcx:
+	regulator_set_voltage(hsusb_vddcx,
+		vdd_val[motg->vdd_type][VDD_NONE],
+		vdd_val[motg->vdd_type][VDD_MAX]);
+devote_xo_handle:
+	clk_disable_unprepare(motg->pclk);
+	msm_xo_mode_vote(motg->xo_handle, MSM_XO_MODE_OFF);
+free_xo_handle:
+	msm_xo_put(motg->xo_handle);
 free_regs:
 	iounmap(motg->regs);
-put_core_clk:
-	if (motg->core_clk)
-		clk_put(motg->core_clk);
+put_pclk:
 	clk_put(motg->pclk);
-put_pclk_src:
-	if (!IS_ERR(motg->pclk_src)) {
-		clk_disable(motg->pclk_src);
-		clk_put(motg->pclk_src);
-	}
+put_core_clk:
+	clk_put(motg->core_clk);
 put_clk:
-	clk_put(motg->clk);
-put_phy_reset_clk:
-	clk_put(motg->phy_reset_clk);
-free_motg:
+	if (!IS_ERR(motg->clk))
+		clk_put(motg->clk);
+	if (!IS_ERR(motg->phy_reset_clk))
+		clk_put(motg->phy_reset_clk);
+free_otg:
 	kfree(motg->phy.otg);
+free_motg:
 	kfree(motg);
 	return ret;
 }
@@ -1609,12 +3490,16 @@
 static int __devexit msm_otg_remove(struct platform_device *pdev)
 {
 	struct msm_otg *motg = platform_get_drvdata(pdev);
-	struct usb_phy *phy = &motg->phy;
+	struct usb_otg *otg = motg->phy.otg;
 	int cnt = 0;
 
-	if (phy->otg->host || phy->otg->gadget)
+	if (otg->host || otg->gadget)
 		return -EBUSY;
 
+	if (pdev->dev.of_node)
+		msm_otg_setup_devices(pdev, motg->pdata->mode, false);
+	if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
+		pm8921_charger_unregister_vbus_sn(0);
 	msm_otg_debugfs_cleanup();
 	cancel_delayed_work_sync(&motg->chg_work);
 	cancel_work_sync(&motg->sm_work);
@@ -1623,15 +3508,19 @@
 
 	device_init_wakeup(&pdev->dev, 0);
 	pm_runtime_disable(&pdev->dev);
+	wake_lock_destroy(&motg->wlock);
 
+	msm_hsusb_mhl_switch_enable(motg, 0);
+	if (motg->pdata->pmic_id_irq)
+		free_irq(motg->pdata->pmic_id_irq, motg);
 	usb_set_transceiver(NULL);
 	free_irq(motg->irq, motg);
 
 	/*
 	 * Put PHY in low power mode.
 	 */
-	ulpi_read(phy, 0x14);
-	ulpi_write(phy, 0x08, 0x09);
+	ulpi_read(otg->phy, 0x14);
+	ulpi_write(otg->phy, 0x08, 0x09);
 
 	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
 	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
@@ -1641,30 +3530,33 @@
 		cnt++;
 	}
 	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC)
-		dev_err(phy->dev, "Unable to suspend PHY\n");
+		dev_err(otg->phy->dev, "Unable to suspend PHY\n");
 
-	clk_disable(motg->pclk);
-	clk_disable(motg->clk);
-	if (motg->core_clk)
-		clk_disable(motg->core_clk);
-	if (!IS_ERR(motg->pclk_src)) {
-		clk_disable(motg->pclk_src);
-		clk_put(motg->pclk_src);
-	}
+	clk_disable_unprepare(motg->pclk);
+	clk_disable_unprepare(motg->core_clk);
+	msm_xo_put(motg->xo_handle);
+	msm_hsusb_ldo_enable(motg, 0);
 	msm_hsusb_ldo_init(motg, 0);
+	regulator_disable(hsusb_vddcx);
+	regulator_set_voltage(hsusb_vddcx,
+		vdd_val[motg->vdd_type][VDD_NONE],
+		vdd_val[motg->vdd_type][VDD_MAX]);
 
 	iounmap(motg->regs);
 	pm_runtime_set_suspended(&pdev->dev);
 
-	clk_put(motg->phy_reset_clk);
+	if (!IS_ERR(motg->phy_reset_clk))
+		clk_put(motg->phy_reset_clk);
 	clk_put(motg->pclk);
-	clk_put(motg->clk);
-	if (motg->core_clk)
-		clk_put(motg->core_clk);
+	if (!IS_ERR(motg->clk))
+		clk_put(motg->clk);
+	clk_put(motg->core_clk);
+
+	if (motg->bus_perf_client)
+		msm_bus_scale_unregister_client(motg->bus_perf_client);
 
 	kfree(motg->phy.otg);
 	kfree(motg);
-
 	return 0;
 }
 
@@ -1672,20 +3564,14 @@
 static int msm_otg_runtime_idle(struct device *dev)
 {
 	struct msm_otg *motg = dev_get_drvdata(dev);
-	struct usb_otg *otg = motg->phy.otg;
+	struct usb_phy *phy = &motg->phy;
 
 	dev_dbg(dev, "OTG runtime idle\n");
 
-	/*
-	 * It is observed some times that a spurious interrupt
-	 * comes when PHY is put into LPM immediately after PHY reset.
-	 * This 1 sec delay also prevents entering into LPM immediately
-	 * after asynchronous interrupt.
-	 */
-	if (otg->phy->state != OTG_STATE_UNDEFINED)
-		pm_schedule_suspend(dev, 1000);
-
-	return -EAGAIN;
+	if (phy->state == OTG_STATE_UNDEFINED)
+		return -EAGAIN;
+	else
+		return 0;
 }
 
 static int msm_otg_runtime_suspend(struct device *dev)
@@ -1701,6 +3587,7 @@
 	struct msm_otg *motg = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "OTG runtime resume\n");
+	pm_runtime_get_noresume(dev);
 	return msm_otg_resume(motg);
 }
 #endif
@@ -1708,32 +3595,42 @@
 #ifdef CONFIG_PM_SLEEP
 static int msm_otg_pm_suspend(struct device *dev)
 {
+	int ret = 0;
 	struct msm_otg *motg = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "OTG PM suspend\n");
-	return msm_otg_suspend(motg);
+
+	atomic_set(&motg->pm_suspended, 1);
+	ret = msm_otg_suspend(motg);
+	if (ret)
+		atomic_set(&motg->pm_suspended, 0);
+
+	return ret;
 }
 
 static int msm_otg_pm_resume(struct device *dev)
 {
+	int ret = 0;
 	struct msm_otg *motg = dev_get_drvdata(dev);
-	int ret;
 
 	dev_dbg(dev, "OTG PM resume\n");
 
-	ret = msm_otg_resume(motg);
-	if (ret)
-		return ret;
+	atomic_set(&motg->pm_suspended, 0);
+	if (motg->sm_work_pending) {
+		motg->sm_work_pending = false;
 
-	/*
-	 * Runtime PM Documentation recommends bringing the
-	 * device to full powered state upon resume.
-	 */
-	pm_runtime_disable(dev);
-	pm_runtime_set_active(dev);
-	pm_runtime_enable(dev);
+		pm_runtime_get_noresume(dev);
+		ret = msm_otg_resume(motg);
 
-	return 0;
+		/* Update runtime PM status */
+		pm_runtime_disable(dev);
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+
+		queue_work(system_nrt_wq, &motg->sm_work);
+	}
+
+	return ret;
 }
 #endif
 
@@ -1745,6 +3642,12 @@
 };
 #endif
 
+static struct of_device_id msm_otg_dt_match[] = {
+	{	.compatible = "qcom,hsusb-otg",
+	},
+	{}
+};
+
 static struct platform_driver msm_otg_driver = {
 	.remove = __devexit_p(msm_otg_remove),
 	.driver = {
@@ -1753,6 +3656,7 @@
 #ifdef CONFIG_PM
 		.pm = &msm_otg_dev_pm_ops,
 #endif
+		.of_match_table = msm_otg_dt_match,
 	},
 };
 
diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c
index 801e597..cf8676f 100644
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -100,3 +100,18 @@
 	}
 }
 EXPORT_SYMBOL(otg_state_string);
+
+int otg_send_event(enum usb_otg_event event)
+{
+	struct usb_phy *phy = usb_get_transceiver();
+	int ret = -ENOTSUPP;
+
+	if (phy && phy->otg && phy->otg->send_event)
+		ret = phy->otg->send_event(phy->otg, event);
+
+	if (phy)
+		usb_put_transceiver(phy);
+
+	return ret;
+}
+EXPORT_SYMBOL(otg_send_event);
diff --git a/drivers/usb/otg/otg_id.c b/drivers/usb/otg/otg_id.c
new file mode 100644
index 0000000..7c38390
--- /dev/null
+++ b/drivers/usb/otg/otg_id.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/usb/otg_id.h>
+
+static DEFINE_MUTEX(otg_id_lock);
+static struct plist_head otg_id_plist =
+	PLIST_HEAD_INIT(otg_id_plist);
+static struct otg_id_notifier_block *otg_id_active;
+static bool otg_id_cancelling;
+static bool otg_id_inited;
+static int otg_id_suspended;
+static bool otg_id_pending;
+
+static void otg_id_cancel(void)
+{
+	if (otg_id_active) {
+		otg_id_cancelling = true;
+		mutex_unlock(&otg_id_lock);
+
+		otg_id_active->cancel(otg_id_active);
+
+		mutex_lock(&otg_id_lock);
+		otg_id_cancelling = false;
+	}
+}
+
+static void __otg_id_notify(void)
+{
+	int ret = 0;
+	struct otg_id_notifier_block *otg_id_nb;
+	bool proxy_wait = false;
+	if (plist_head_empty(&otg_id_plist))
+		return;
+
+	plist_for_each_entry(otg_id_nb, &otg_id_plist, p) {
+		if (proxy_wait) {
+			if (otg_id_nb->proxy_wait)
+				ret = otg_id_nb->proxy_wait(otg_id_nb);
+		} else {
+			ret = otg_id_nb->detect(otg_id_nb);
+		}
+		if (ret == OTG_ID_HANDLED) {
+			otg_id_active = otg_id_nb;
+			return;
+		}
+		if (ret == OTG_ID_PROXY_WAIT)
+			proxy_wait = true;
+
+	}
+
+	WARN(1, "otg id event not handled");
+	otg_id_active = NULL;
+}
+
+int otg_id_init(void)
+{
+	mutex_lock(&otg_id_lock);
+
+	otg_id_inited = true;
+	__otg_id_notify();
+
+	mutex_unlock(&otg_id_lock);
+	return 0;
+}
+late_initcall(otg_id_init);
+
+/**
+ * otg_id_register_notifier
+ * @otg_id_nb: notifier block containing priority and callback function
+ *
+ * Register a notifier that will be called on any USB cable state change.
+ * The priority determines the order the callback will be called in, a higher
+ * number will be called first.  A callback function needs to determine the
+ * type of USB cable that is connected.  If it can determine the type, it
+ * should notify the appropriate drivers (for example, call an otg notifier
+ * with USB_EVENT_VBUS), and return OTG_ID_HANDLED.  Once a callback has
+ * returned OTG_ID_HANDLED, it is responsible for calling otg_id_notify() when
+ * the detected USB cable is disconnected.
+ */
+int otg_id_register_notifier(struct otg_id_notifier_block *otg_id_nb)
+{
+	plist_node_init(&otg_id_nb->p, otg_id_nb->priority);
+
+	mutex_lock(&otg_id_lock);
+	plist_add(&otg_id_nb->p, &otg_id_plist);
+
+	if (otg_id_inited) {
+		otg_id_cancel();
+		__otg_id_notify();
+	}
+
+	mutex_unlock(&otg_id_lock);
+
+	return 0;
+}
+
+void otg_id_unregister_notifier(struct otg_id_notifier_block *otg_id_nb)
+{
+	mutex_lock(&otg_id_lock);
+
+	plist_del(&otg_id_nb->p, &otg_id_plist);
+
+	if (otg_id_inited && (otg_id_active == otg_id_nb)) {
+		otg_id_cancel();
+		__otg_id_notify();
+	}
+
+	mutex_unlock(&otg_id_lock);
+}
+
+/**
+ * otg_id_notify
+ *
+ * Notify listeners on any USB cable state change.
+ *
+ * A driver may only call otg_id_notify if it returned OTG_ID_HANDLED the last
+ * time it's notifier was called, and it's cancel function has not been called.
+ */
+void otg_id_notify(void)
+{
+	mutex_lock(&otg_id_lock);
+
+	if (otg_id_cancelling)
+		goto out;
+
+	if (otg_id_suspended != 0) {
+		otg_id_pending = true;
+		goto out;
+	}
+
+	__otg_id_notify();
+out:
+	mutex_unlock(&otg_id_lock);
+}
+
+/**
+ * otg_id_suspend
+ *
+ * Mark the otg_id subsystem as going into suspend. From here on out,
+ * any notifications will be deferred until the last otg_id client resumes.
+ * If there is a pending notification when calling this function, it will
+ * return a negative errno and expects that the caller will abort suspend.
+ * Returs 0 on success.
+ */
+int otg_id_suspend(void)
+{
+	int ret = 0;
+
+	mutex_lock(&otg_id_lock);
+
+	/*
+	 * if there's a pending notification, tell the caller to abort suspend
+	 */
+	if (otg_id_suspended != 0 && otg_id_pending) {
+		pr_info("otg_id: pending notification, should abort suspend\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	otg_id_suspended++;
+out:
+	mutex_unlock(&otg_id_lock);
+	return ret;
+}
+
+/**
+ * otg_id_resume
+ *
+ * Inform the otg_id subsystem that a client is resuming. If this is the
+ * last client to be resumed and there's a pending notification,
+ * otg_id_notify() is called.
+ */
+void otg_id_resume(void)
+{
+	mutex_lock(&otg_id_lock);
+	if (WARN(!otg_id_suspended, "unbalanced otg_id_resume\n"))
+		goto out;
+	if (--otg_id_suspended == 0) {
+		if (otg_id_pending) {
+			pr_info("otg_id: had pending notification\n");
+			otg_id_pending = false;
+			__otg_id_notify();
+		}
+	}
+out:
+	mutex_unlock(&otg_id_lock);
+}
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 7141d65..90b68d1 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -669,6 +669,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ssu100.
 
+config USB_SERIAL_CSVT
+	tristate "USB serial driver for Circuit-Switched Video Telephony"
+	help
+	  Say Y here if you want to use usb serial driver for Circuit-Switched
+	  Video Telephony
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called csvt.
+
 config USB_SERIAL_DEBUG
 	tristate "USB Debugging Device"
 	help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 07f198e..708bdbb 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -62,3 +62,4 @@
 obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
 obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL)		+= vivopay-serial.o
 obj-$(CONFIG_USB_SERIAL_ZIO)			+= zio.o
+obj-$(CONFIG_USB_SERIAL_CSVT)			+= csvt.o
diff --git a/drivers/usb/serial/csvt.c b/drivers/usb/serial/csvt.c
new file mode 100644
index 0000000..3efdd77
--- /dev/null
+++ b/drivers/usb/serial/csvt.c
@@ -0,0 +1,443 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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/slab.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/serial.h>
+#include <asm/unaligned.h>
+
+
+/* output control lines*/
+#define CSVT_CTRL_DTR		0x01
+#define CSVT_CTRL_RTS		0x02
+
+/* input control lines*/
+#define CSVT_CTRL_CTS		0x01
+#define CSVT_CTRL_DSR		0x02
+#define CSVT_CTRL_RI		0x04
+#define CSVT_CTRL_CD		0x08
+
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+struct csvt_ctrl_dev {
+	struct mutex		dev_lock;
+
+	/* input control lines (DSR, CTS, CD, RI) */
+	unsigned int		cbits_tolocal;
+
+	/* output control lines (DTR, RTS) */
+	unsigned int		cbits_tomdm;
+};
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x05c6 , 0x904c, 0xff, 0xfe, 0xff)},
+	{}, /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver csvt_driver = {
+	.name			= "qc_csvt",
+	.probe			= usb_serial_probe,
+	.disconnect		= usb_serial_disconnect,
+	.id_table		= id_table,
+	.suspend		= usb_serial_suspend,
+	.resume			= usb_serial_resume,
+	.supports_autosuspend	= true,
+};
+
+#define CSVT_IFC_NUM	4
+
+static int csvt_probe(struct usb_serial *serial, const struct usb_device_id *id)
+{
+	struct usb_host_interface	*intf =
+		serial->interface->cur_altsetting;
+
+	pr_debug("%s:\n", __func__);
+
+	if (intf->desc.bInterfaceNumber != CSVT_IFC_NUM)
+		return -ENODEV;
+
+	usb_enable_autosuspend(serial->dev);
+
+	return 0;
+}
+
+static int csvt_ctrl_write_cmd(struct csvt_ctrl_dev	*dev,
+	struct usb_serial_port *port)
+{
+	struct usb_device	*udev = port->serial->dev;
+	struct usb_interface	*iface = port->serial->interface;
+	unsigned int		iface_num;
+	int			retval = 0;
+
+	retval = usb_autopm_get_interface(iface);
+	if (retval < 0) {
+		dev_err(&port->dev, "%s: Unable to resume interface: %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	dev_dbg(&port->dev, "%s: cbits to mdm 0x%x\n", __func__,
+		dev->cbits_tomdm);
+
+	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+
+	retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+		USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+		(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
+		dev->cbits_tomdm,
+		iface_num,
+		NULL, 0, USB_CTRL_SET_TIMEOUT);
+	usb_autopm_put_interface(iface);
+
+	return retval;
+}
+
+static void csvt_ctrl_dtr_rts(struct usb_serial_port *port, int on)
+{
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	if (!dev)
+		return;
+
+	dev_dbg(&port->dev, "%s", __func__);
+
+	mutex_lock(&dev->dev_lock);
+	if (on) {
+		dev->cbits_tomdm |= CSVT_CTRL_DTR;
+		dev->cbits_tomdm |= CSVT_CTRL_RTS;
+	} else {
+		dev->cbits_tomdm &= ~CSVT_CTRL_DTR;
+		dev->cbits_tomdm &= ~CSVT_CTRL_RTS;
+	}
+	mutex_unlock(&dev->dev_lock);
+
+	csvt_ctrl_write_cmd(dev, port);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct	tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line            = port->serial->minor;
+	tmp.port            = port->number;
+	tmp.baud_base       = tty_get_baud_rate(port->port.tty);
+	tmp.close_delay	    = port->port.close_delay / 10;
+	tmp.closing_wait    =
+		port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+				 ASYNC_CLOSING_WAIT_NONE :
+				 port->port.closing_wait / 10;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct usb_serial_port *port,
+			   struct serial_struct __user *newinfo)
+{
+	struct serial_struct	new_serial;
+	unsigned int		closing_wait;
+	unsigned int		close_delay;
+	int			retval = 0;
+
+	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+		return -EFAULT;
+
+	close_delay = new_serial.close_delay * 10;
+	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+			ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+
+	mutex_lock(&port->port.mutex);
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((close_delay != port->port.close_delay) ||
+		    (closing_wait != port->port.closing_wait))
+			retval = -EPERM;
+		else
+			retval = -EOPNOTSUPP;
+	} else {
+		port->port.close_delay  = close_delay;
+		port->port.closing_wait = closing_wait;
+	}
+
+	mutex_unlock(&port->port.mutex);
+	return retval;
+}
+
+static int csvt_ctrl_ioctl(struct tty_struct *tty, unsigned int cmd,
+	unsigned long arg)
+{
+	struct usb_serial_port	*port = tty->driver_data;
+
+	dev_dbg(&port->dev, "%s cmd 0x%04x", __func__, cmd);
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	case TIOCSSERIAL:
+		return set_serial_info(port,
+				       (struct serial_struct __user *) arg);
+	default:
+		break;
+	}
+
+	dev_err(&port->dev, "%s arg not supported", __func__);
+
+	return -ENOIOCTLCMD;
+}
+
+static int csvt_ctrl_tiocmget(struct tty_struct *tty)
+{
+	struct usb_serial_port	*port = tty->driver_data;
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+	unsigned int		control_state = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	mutex_lock(&dev->dev_lock);
+	control_state = (dev->cbits_tomdm & CSVT_CTRL_DTR ? TIOCM_DTR : 0) |
+		(dev->cbits_tomdm & CSVT_CTRL_RTS ? TIOCM_RTS : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_DSR ? TIOCM_DSR : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_RI ? TIOCM_RI : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_CD ? TIOCM_CD : 0) |
+		(dev->cbits_tolocal & CSVT_CTRL_CTS ? TIOCM_CTS : 0);
+	mutex_unlock(&dev->dev_lock);
+
+	dev_dbg(&port->dev, "%s -- %x", __func__, control_state);
+
+	return control_state;
+}
+
+static int csvt_ctrl_tiocmset(struct tty_struct *tty,
+			       unsigned int set, unsigned int clear)
+{
+	struct usb_serial_port	*port = tty->driver_data;
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev_dbg(&port->dev, "%s\n", __func__);
+
+	mutex_lock(&dev->dev_lock);
+	if (set & CSVT_CTRL_DTR)
+		dev->cbits_tomdm |= TIOCM_DTR;
+	if (set & CSVT_CTRL_RTS)
+		dev->cbits_tomdm |= TIOCM_RTS;
+
+	if (clear & CSVT_CTRL_DTR)
+		dev->cbits_tomdm &= ~TIOCM_DTR;
+	if (clear & CSVT_CTRL_RTS)
+		dev->cbits_tomdm &= ~TIOCM_RTS;
+	mutex_unlock(&dev->dev_lock);
+
+	return csvt_ctrl_write_cmd(dev, port);
+}
+
+static void csvt_ctrl_set_termios(struct tty_struct *tty,
+			  struct usb_serial_port *port,
+			  struct ktermios *old_termios)
+{
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	if (!dev)
+		return;
+
+	dev_dbg(&port->dev, "%s", __func__);
+
+	/* Doesn't support option setting */
+	tty_termios_copy_hw(tty->termios, old_termios);
+
+	csvt_ctrl_write_cmd(dev, port);
+}
+
+static void csvt_ctrl_int_cb(struct urb *urb)
+{
+	int				status;
+	struct usb_cdc_notification	*ctrl;
+	struct usb_serial_port		*port = urb->context;
+	struct csvt_ctrl_dev		*dev;
+	unsigned int			ctrl_bits;
+	unsigned char			*data;
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		break;
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		 /* unplug */
+		 return;
+	case -EPIPE:
+		dev_err(&port->dev, "%s: stall on int endpoint\n", __func__);
+		/* TBD : halt to be cleared in work */
+	case -EOVERFLOW:
+	default:
+		pr_debug_ratelimited("%s: non zero urb status = %d\n",
+					__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	dev = usb_get_serial_port_data(port);
+	if (!dev)
+		return;
+
+	ctrl = urb->transfer_buffer;
+	data = (unsigned char *)(ctrl + 1);
+
+	usb_serial_debug_data(debug, &port->dev, __func__,
+					urb->actual_length, data);
+
+	switch (ctrl->bNotificationType) {
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		dev_dbg(&port->dev, "%s network\n", ctrl->wValue ?
+					"connected to" : "disconnected from");
+		break;
+	case USB_CDC_NOTIFY_SERIAL_STATE:
+		ctrl_bits = get_unaligned_le16(data);
+		dev_dbg(&port->dev, "serial state: %d\n", ctrl_bits);
+		dev->cbits_tolocal = ctrl_bits;
+		break;
+	default:
+		dev_err(&port->dev, "%s: unknown notification %d received:"
+			"index %d len %d data0 %d data1 %d",
+			__func__, ctrl->bNotificationType, ctrl->wIndex,
+			ctrl->wLength, data[0], data[1]);
+	}
+
+resubmit_int_urb:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status)
+		dev_err(&port->dev, "%s: Error re-submitting Int URB %d\n",
+		__func__, status);
+
+}
+
+static int csvt_ctrl_open(struct tty_struct *tty,
+					struct usb_serial_port *port)
+{
+	int	retval;
+
+	dev_dbg(&port->dev, "%s port %d", __func__, port->number);
+
+	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
+		return retval;
+	}
+
+	retval = usb_serial_generic_open(tty, port);
+	if (retval)
+		usb_kill_urb(port->interrupt_in_urb);
+
+	return retval;
+}
+
+static void csvt_ctrl_close(struct usb_serial_port *port)
+{
+	dev_dbg(&port->dev, "%s port %d", __func__, port->number);
+
+	usb_serial_generic_close(port);
+	usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int csvt_ctrl_attach(struct usb_serial *serial)
+{
+	struct csvt_ctrl_dev	*dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	mutex_init(&dev->dev_lock);
+	usb_set_serial_port_data(serial->port[0], dev);
+
+	return 0;
+}
+
+static void csvt_ctrl_release(struct usb_serial *serial)
+{
+	struct usb_serial_port	*port = serial->port[0];
+	struct csvt_ctrl_dev	*dev = usb_get_serial_port_data(port);
+
+	dev_dbg(&port->dev, "%s", __func__);
+
+	kfree(dev);
+	usb_set_serial_port_data(port, NULL);
+}
+
+static struct usb_serial_driver csvt_device = {
+	.driver			= {
+		.owner	= THIS_MODULE,
+		.name	= "qc_csvt",
+	},
+	.description		= "qc_csvt",
+	.id_table		= id_table,
+	.num_ports		= 1,
+	.open			= csvt_ctrl_open,
+	.close			= csvt_ctrl_close,
+	.probe			= csvt_probe,
+	.dtr_rts		= csvt_ctrl_dtr_rts,
+	.tiocmget		= csvt_ctrl_tiocmget,
+	.tiocmset		= csvt_ctrl_tiocmset,
+	.ioctl			= csvt_ctrl_ioctl,
+	.set_termios		= csvt_ctrl_set_termios,
+	.read_int_callback	= csvt_ctrl_int_cb,
+	.attach			= csvt_ctrl_attach,
+	.release		= csvt_ctrl_release,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+	&csvt_device,
+	NULL,
+};
+
+static int __init csvt_init(void)
+{
+	int	retval;
+
+	retval = usb_serial_register_drivers(&csvt_driver, serial_drivers);
+	if (retval) {
+		err("%s: usb serial register failed\n", __func__);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void __exit csvt_exit(void)
+{
+	usb_serial_deregister_drivers(&csvt_driver, serial_drivers);
+}
+
+module_init(csvt_init);
+module_exit(csvt_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 0206b10..1f6d915 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -1,7 +1,7 @@
 /*
  * Qualcomm Serial USB driver
  *
- *	Copyright (c) 2008 QUALCOMM Incorporated.
+ *	Copyright (c) 2008, 2012 Code Aurora Forum. All rights reserved.
  *	Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
  *	Copyright (c) 2009 Novell Inc.
  *
@@ -108,10 +108,14 @@
 	{USB_DEVICE(0x1199, 0x9013)},	/* Sierra Wireless Gobi 3000 Modem device (MC8355) */
 	{USB_DEVICE(0x12D1, 0x14F0)},	/* Sony Gobi 3000 QDL */
 	{USB_DEVICE(0x12D1, 0x14F1)},	/* Sony Gobi 3000 Composite */
+	{USB_DEVICE(0x05c6, 0x9048)},	/* MDM9x15 device */
+	{USB_DEVICE(0x05c6, 0x904C)},	/* MDM9x15 device */
 	{ }				/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
+#define EFS_SYNC_IFC_NUM	2
+
 static struct usb_driver qcdriver = {
 	.name			= "qcserial",
 	.probe			= usb_serial_probe,
@@ -233,6 +237,14 @@
 		}
 		break;
 
+	case 9:
+		if (ifnum != EFS_SYNC_IFC_NUM) {
+			kfree(data);
+			break;
+		}
+
+		retval = 0;
+		break;
 	default:
 		dev_err(&serial->dev->dev,
 			"unknown number of interfaces: %d\n", nintf);
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index c47b6ec..de8d490 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -31,10 +31,10 @@
 
 /* per port private data */
 
-#define N_IN_URB 4
-#define N_OUT_URB 4
-#define IN_BUFLEN 4096
-#define OUT_BUFLEN 4096
+#define N_IN_URB 5
+#define N_OUT_URB 5
+#define IN_BUFLEN 65536
+#define OUT_BUFLEN 65536
 
 struct usb_wwan_intf_private {
 	spinlock_t susp_lock;
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index c88657d..519af39 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -406,6 +406,11 @@
 	portdata = usb_get_serial_port_data(port);
 	intfdata = serial->private;
 
+	/* explicitly set the driver mode to raw */
+	tty->raw = 1;
+	tty->real_raw = 1;
+
+	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
 	dbg("%s", __func__);
 
 	/* Start reading from the IN endpoint */
@@ -548,7 +553,7 @@
 		init_usb_anchor(&portdata->delayed);
 
 		for (j = 0; j < N_IN_URB; j++) {
-			buffer = (u8 *) __get_free_page(GFP_KERNEL);
+			buffer = kmalloc(IN_BUFLEN, GFP_KERNEL);
 			if (!buffer)
 				goto bail_out_error;
 			portdata->in_buffer[j] = buffer;
@@ -577,8 +582,7 @@
 		kfree(portdata->out_buffer[j]);
 bail_out_error:
 	for (j = 0; j < N_IN_URB; j++)
-		if (portdata->in_buffer[j])
-			free_page((unsigned long)portdata->in_buffer[j]);
+		kfree(portdata->in_buffer[j]);
 	kfree(portdata);
 	return 1;
 }
@@ -624,8 +628,7 @@
 
 		for (j = 0; j < N_IN_URB; j++) {
 			usb_free_urb(portdata->in_urbs[j]);
-			free_page((unsigned long)
-				  portdata->in_buffer[j]);
+			kfree(portdata->in_buffer[j]);
 			portdata->in_urbs[j] = NULL;
 		}
 		for (j = 0; j < N_OUT_URB; j++) {
@@ -731,6 +734,10 @@
 		}
 	}
 
+	spin_lock_irq(&intfdata->susp_lock);
+	intfdata->suspended = 0;
+	spin_unlock_irq(&intfdata->susp_lock);
+
 	for (i = 0; i < serial->num_ports; i++) {
 		/* walk all ports */
 		port = serial->port[i];
@@ -756,9 +763,6 @@
 		play_delayed(port);
 		spin_unlock_irq(&intfdata->susp_lock);
 	}
-	spin_lock_irq(&intfdata->susp_lock);
-	intfdata->suspended = 0;
-	spin_unlock_irq(&intfdata->susp_lock);
 err_out:
 	return err;
 }