Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/usb/function/msm_hsusb.c b/drivers/usb/function/msm_hsusb.c
new file mode 100644
index 0000000..eebd9d4
--- /dev/null
+++ b/drivers/usb/function/msm_hsusb.c
@@ -0,0 +1,3948 @@
+/* drivers/usb/function/msm_hsusb.c
+ *
+ * Driver for HighSpeed USB Client Controller in MSM7K
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008-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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <linux/delay.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/clk.h>
+#include <linux/spinlock.h>
+#include <linux/switch.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+#include <mach/vreg.h>
+#include <mach/board.h>
+#include <mach/msm_hsusb.h>
+#include <mach/rpc_hsusb.h>
+#include <mach/rpc_pmapp.h>
+#include <mach/gpio.h>
+#include <mach/msm_hsusb_hw.h>
+#include <mach/msm_otg.h>
+#include <linux/wakelock.h>
+#include <linux/pm_qos_params.h>
+#include <mach/clk.h>
+
+#define MSM_USB_BASE ((unsigned) ui->addr)
+
+#include "usb_function.h"
+
+#define EPT_FLAG_IN	0x0001
+#define USB_DIR_MASK	USB_DIR_IN
+#define SETUP_BUF_SIZE	4096
+
+/* IDs for string descriptors */
+#define STRING_LANGUAGE_ID      0
+#define STRING_SERIAL           1
+#define STRING_PRODUCT          2
+#define STRING_MANUFACTURER     3
+
+#define LANGUAGE_ID             0x0409 /* en-US */
+#define SOC_ROC_2_0		0x10002 /* ROC 2.0 */
+
+#define TRUE			1
+#define FALSE			0
+#define USB_LINK_RESET_TIMEOUT	(msecs_to_jiffies(10))
+#define USB_CHG_DET_DELAY	msecs_to_jiffies(1000)
+
+#define is_phy_45nm()     (PHY_MODEL(ui->phy_info) == USB_PHY_MODEL_45NM)
+#define is_phy_external() (PHY_TYPE(ui->phy_info) == USB_PHY_EXTERNAL)
+
+static int pid = 0x9018;
+
+struct usb_fi_ept {
+	struct usb_endpoint *ept;
+	struct usb_endpoint_descriptor desc;
+};
+
+struct usb_function_info {
+	struct list_head list;
+	unsigned enabled;
+	struct usb_function *func;
+};
+
+struct msm_request {
+	struct usb_request req;
+
+	struct usb_info *ui;
+	struct msm_request *next;
+
+	unsigned busy:1;
+	unsigned live:1;
+	unsigned alloced:1;
+	unsigned dead:1;
+
+	dma_addr_t dma;
+
+	struct ept_queue_item *item;
+	dma_addr_t item_dma;
+};
+static unsigned char str_lang_desc[] = {4,
+				USB_DT_STRING,
+				(unsigned char)LANGUAGE_ID,
+				(unsigned char)(LANGUAGE_ID >> 8)};
+
+#define to_msm_request(r) container_of(r, struct msm_request, req)
+static int usb_hw_reset(struct usb_info *ui);
+static void usb_vbus_online(struct usb_info *);
+static void usb_vbus_offline(struct usb_info *ui);
+static void usb_lpm_exit(struct usb_info *ui);
+static void usb_lpm_wakeup_phy(struct work_struct *);
+static void usb_exit(void);
+static int usb_is_online(struct usb_info *ui);
+static void usb_do_work(struct work_struct *w);
+static int usb_lpm_enter(struct usb_info *ui);
+int (*usb_lpm_config_gpio)(int);
+static void usb_enable_pullup(struct usb_info *ui);
+static void usb_disable_pullup(struct usb_info *ui);
+
+static struct workqueue_struct *usb_work;
+static void usb_chg_stop(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_CONFIGURE	0x0020
+#define USB_FLAG_RESUME	0x0040
+#define USB_FLAG_REG_OTG 0x0080
+
+#define USB_MSC_ONLY_FUNC_MAP	0x10
+#define DRIVER_NAME		"msm_hsusb_peripheral"
+
+struct lpm_info {
+	struct work_struct wakeup_phy;
+};
+
+enum charger_type {
+	USB_CHG_TYPE__SDP,
+	USB_CHG_TYPE__CARKIT,
+	USB_CHG_TYPE__WALLCHARGER,
+	USB_CHG_TYPE__INVALID
+};
+
+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 usb_request *ep0out_req;
+
+	struct platform_device *pdev;
+	struct msm_hsusb_platform_data *pdata;
+	int irq;
+	int gpio_irq[2];
+	void *addr;
+
+	unsigned state;
+	unsigned flags;
+
+	unsigned online;
+	unsigned running;
+	unsigned bound;
+
+	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;
+	unsigned stopped:1;
+	unsigned remote_wakeup:1;
+	unsigned configured:1;
+	unsigned selfpowered:1;
+	unsigned iad:1;
+	unsigned char maxpower;
+	enum usb_device_speed speed;
+	unsigned phy_info;
+
+	/* endpoints are ordered based on their status bits,
+	** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15
+	*/
+	struct usb_endpoint ept[32];
+
+	struct delayed_work work;
+	struct delayed_work chg_legacy_det;
+	unsigned phy_status;
+	unsigned phy_fail_count;
+	struct usb_composition *composition;
+
+	struct usb_function_info **func;
+	unsigned num_funcs;
+	struct usb_function_map *functions_map;
+
+#define MAX_INTERFACE_NUM	15
+	struct usb_function *func2ifc_map[MAX_INTERFACE_NUM];
+
+#define ep0out ept[0]
+#define ep0in  ept[16]
+
+	struct clk *clk;
+	struct clk *pclk;
+	struct clk *cclk;
+	unsigned int clk_enabled;
+
+	struct vreg *vreg;
+	unsigned int vreg_enabled;
+
+	unsigned in_lpm;
+	struct lpm_info li;
+
+	enum charger_type chg_type;
+	struct work_struct chg_stop;
+#define MAX_STRDESC_NUM		100
+	char **strdesc;
+	int strdesc_index;
+
+	u16 test_mode;
+	struct wake_lock wlock;
+	struct msm_otg_transceiver *xceiv;
+	int active;
+	enum usb_device_state usb_state;
+	int vbus_sn_notif;
+	struct switch_dev sdev;
+};
+static struct usb_info *the_usb_info;
+
+static unsigned short usb_validate_product_id(unsigned short pid);
+static unsigned short usb_get_product_id(unsigned long enabled_functions);
+static void usb_switch_composition(unsigned short pid);
+static unsigned short usb_set_composition(unsigned short pid);
+static void usb_configure_device_descriptor(struct usb_info *ui);
+static void usb_uninit(struct usb_info *ui);
+
+static unsigned ulpi_read(struct usb_info *ui, unsigned reg);
+static int ulpi_write(struct usb_info *ui, unsigned val, unsigned reg);
+
+
+
+struct usb_device_descriptor desc_device = {
+	.bLength = USB_DT_DEVICE_SIZE,
+	.bDescriptorType = USB_DT_DEVICE,
+	.bcdUSB = 0x0200,
+	.bDeviceClass = 0,
+	.bDeviceSubClass = 0,
+	.bDeviceProtocol = 0,
+	.bMaxPacketSize0 = 64,
+	/* the following fields are filled in by usb_probe */
+	.idVendor = 0,
+	.idProduct = 0,
+	.bcdDevice = 0,
+	.iManufacturer = 0,
+	.iProduct = 0,
+	.iSerialNumber = 0,
+	.bNumConfigurations = 1,
+};
+
+static void flush_endpoint(struct usb_endpoint *ept);
+static void msm_hsusb_suspend_locks_acquire(struct usb_info *, int);
+
+static ssize_t print_switch_name(struct switch_dev *sdev, char *buf)
+{
+	return sprintf(buf, "%s\n", DRIVER_NAME);
+}
+
+static ssize_t print_switch_state(struct switch_dev *sdev, char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+
+	return sprintf(buf, "%s\n", (ui->online ? "online" : "offline"));
+}
+
+#define USB_WALLCHARGER_CHG_CURRENT 1800
+static int usb_get_max_power(struct usb_info *ui)
+{
+	unsigned long flags;
+	enum charger_type temp;
+	int suspended;
+	int configured;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	temp = ui->chg_type;
+	suspended = ui->usb_state == USB_STATE_SUSPENDED ? 1 : 0;
+	configured = ui->configured;
+	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 ui->maxpower * 2;
+}
+
+static void usb_chg_legacy_detect(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long flags;
+	enum charger_type temp = USB_CHG_TYPE__INVALID;
+	int maxpower;
+	int ret = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (ui->usb_state == USB_STATE_NOTATTACHED) {
+		ret = -ENODEV;
+		goto chg_legacy_det_out;
+	}
+
+	if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) {
+		ui->chg_type = temp = USB_CHG_TYPE__WALLCHARGER;
+		goto chg_legacy_det_out;
+	}
+
+	ui->chg_type = temp = USB_CHG_TYPE__SDP;
+chg_legacy_det_out:
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	if (ret)
+		return;
+
+	msm_chg_usb_charger_connected(temp);
+	maxpower = usb_get_max_power(ui);
+	if (maxpower > 0)
+		msm_chg_usb_i_is_available(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. In order
+	 * to allow pc, driver has to initiate low power mode which it
+	 * cannot do as phy cannot be accessed when dedicated charger
+	 * is connected due to phy lockup issues. Just to allow idle &
+	 * suspend pc when dedicated charger is connected, release the
+	 * wakelock, set driver latency to default and act as if we are
+	 * in low power mode so that, driver will re-acquire wakelocks
+	 * for any sub-sequent usb interrupts.
+	 */
+	if (temp == USB_CHG_TYPE__WALLCHARGER) {
+		pr_info("\n%s: WALL-CHARGER\n", __func__);
+		spin_lock_irqsave(&ui->lock, flags);
+		if (ui->usb_state == USB_STATE_NOTATTACHED) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			return;
+		}
+		ui->in_lpm = 1;
+		spin_unlock_irqrestore(&ui->lock, flags);
+
+		msm_hsusb_suspend_locks_acquire(ui, 0);
+	} else
+		pr_info("\n%s: Standard Downstream Port\n", __func__);
+}
+
+int usb_msm_get_next_strdesc_id(char *str)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned id;
+	unsigned long flags;
+	int len;
+
+	len = strlen(str);
+	if (!len) {
+		printk(KERN_ERR "usb next_strdesc_id(); null string\n");
+		return -EPERM;
+	}
+	/* for null character */
+	len = len + 1;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	id = ui->strdesc_index;
+	if (id >= MAX_STRDESC_NUM) {
+		id = -EPERM;
+		printk(KERN_ERR "reached max strdesc number\n");
+		goto get_strd_id_exit;
+	}
+
+	ui->strdesc[id] = kmalloc(len, GFP_ATOMIC);
+	if (ui->strdesc[id]) {
+		memcpy(ui->strdesc[id], str, len);
+		ui->strdesc_index++;
+	} else {
+		id = -EPERM;
+		printk(KERN_ERR "usb next_strdesc_id(); Out of memory:(%s)\n",
+			str);
+	}
+
+get_strd_id_exit:
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return id;
+}
+EXPORT_SYMBOL(usb_msm_get_next_strdesc_id);
+
+
+inline int usb_msm_is_iad(void)
+{
+	return the_usb_info->iad;
+}
+EXPORT_SYMBOL(usb_msm_is_iad);
+
+inline void usb_msm_enable_iad(void)
+{
+	the_usb_info->iad = 1;
+}
+EXPORT_SYMBOL(usb_msm_enable_iad);
+
+int usb_msm_get_speed()
+{
+	return the_usb_info->speed;
+}
+EXPORT_SYMBOL(usb_msm_get_speed);
+
+int usb_msm_get_next_ifc_number(struct usb_function *driver)
+{
+	struct usb_info *ui = the_usb_info;
+	int ifc_num = -1;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	for (i = 0; i < ui->pdata->num_functions; i++) {
+		if (strcmp(ui->functions_map[i].name, driver->name))
+			continue;
+		if (!(ui->composition->functions & (1 << i)))
+			continue;
+		ifc_num = ui->next_ifc_num++;
+		ui->func2ifc_map[ifc_num] = driver;
+		break;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return ifc_num;
+}
+EXPORT_SYMBOL(usb_msm_get_next_ifc_number);
+
+static inline int usb_msm_get_selfpowered(void)
+{
+	struct usb_info *ui = the_usb_info;
+
+	return ui->selfpowered;
+}
+static inline int usb_msm_get_remotewakeup(void)
+{
+	struct usb_info *ui = the_usb_info;
+
+	return ui->remote_wakeup;
+}
+
+static void usb_clk_enable(struct usb_info *ui)
+{
+	if (!ui->clk_enabled) {
+		clk_enable(ui->pclk);
+		if (ui->cclk)
+			clk_enable(ui->cclk);
+		ui->clk_enabled = 1;
+	}
+}
+
+static void usb_clk_disable(struct usb_info *ui)
+{
+	if (ui->clk_enabled) {
+		clk_disable(ui->pclk);
+		if (ui->cclk)
+			clk_disable(ui->cclk);
+		ui->clk_enabled = 0;
+	}
+}
+
+static void usb_vreg_enable(struct usb_info *ui)
+{
+	if (ui->vreg && !IS_ERR(ui->vreg) && !ui->vreg_enabled) {
+		vreg_enable(ui->vreg);
+		ui->vreg_enabled = 1;
+	}
+}
+
+static void usb_vreg_disable(struct usb_info *ui)
+{
+	if (ui->vreg && !IS_ERR(ui->vreg) && ui->vreg_enabled) {
+		vreg_disable(ui->vreg);
+		ui->vreg_enabled = 0;
+	}
+}
+
+static unsigned ulpi_read(struct usb_info *ui, unsigned reg)
+{
+	unsigned timeout = 100000;
+
+	/* 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)) ;
+
+	if (timeout == 0) {
+		printk(KERN_ERR "ulpi_read: timeout %08x\n",
+			readl(USB_ULPI_VIEWPORT));
+		return 0xffffffff;
+	}
+	return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
+}
+
+static int ulpi_write(struct usb_info *ui, unsigned val, unsigned reg)
+{
+	unsigned timeout = 10000;
+
+	/* 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) {
+		printk(KERN_ERR "ulpi_write: timeout\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void msm_hsusb_suspend_locks_acquire(struct usb_info *ui, int acquire)
+{
+	if (acquire) {
+		wake_lock(&ui->wlock);
+		pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY,
+				DRIVER_NAME, ui->pdata->swfi_latency);
+		/* targets like 7x30 have introduced core clock
+		 * to remove the dependency on max axi frequency
+		 */
+		if (!ui->cclk)
+			pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ,
+					DRIVER_NAME, MSM_AXI_MAX_FREQ);
+	} else {
+		wake_lock_timeout(&ui->wlock, HZ / 2);
+		pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY,
+					DRIVER_NAME,
+					PM_QOS_DEFAULT_VALUE);
+		if (!ui->cclk)
+			pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ,
+					DRIVER_NAME, PM_QOS_DEFAULT_VALUE);
+	}
+}
+
+static void msm_hsusb_suspend_locks_init(struct usb_info *ui, int init)
+{
+	if (init) {
+		wake_lock_init(&ui->wlock, WAKE_LOCK_SUSPEND,
+				"usb_bus_active");
+		pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY,
+				DRIVER_NAME,
+				PM_QOS_DEFAULT_VALUE);
+		pm_qos_add_requirement(PM_QOS_SYSTEM_BUS_FREQ,
+				DRIVER_NAME, PM_QOS_DEFAULT_VALUE);
+	} else {
+		wake_lock_destroy(&ui->wlock);
+		pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, DRIVER_NAME);
+		pm_qos_remove_requirement(PM_QOS_SYSTEM_BUS_FREQ, DRIVER_NAME);
+	}
+}
+
+static void init_endpoints(struct usb_info *ui)
+{
+	unsigned n;
+
+	for (n = 0; n < 32; n++) {
+		struct usb_endpoint *ept = ui->ept + n;
+
+		ept->ui = ui;
+		ept->bit = n;
+		ept->num = n & 15;
+		ept->alloced = 0;
+
+		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;
+		}
+	}
+}
+
+void usb_configure_endpoint(struct usb_endpoint *ep,
+			struct usb_endpoint_descriptor *ep_desc)
+{
+	unsigned cfg = 0;
+	unsigned long flags;
+	struct usb_info *ui = ep->ui;
+
+	if (!ui)
+		return;
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (ep_desc) {
+		ep->max_pkt = ep_desc->wMaxPacketSize;
+		ep->ep_descriptor = ep_desc;
+	}
+
+	if (!ep->max_pkt) {
+		printk(KERN_ERR "cannot configure zero length max pkt\n");
+		goto cfg_ept_end;
+	}
+
+	cfg = CONFIG_MAX_PKT(ep->max_pkt) | CONFIG_ZLT;
+	/* ep0 out needs interrupt-on-setup */
+	if (ep->bit == 0)
+		cfg |= CONFIG_IOS;
+	ep->head->config = cfg;
+	ep->head->next = TERMINATE;
+
+	pr_debug("ept #%d %s max:%d head:%p bit:%d\n",
+		       ep->num,
+		       (ep->flags & EPT_FLAG_IN) ? "in" : "out",
+		       ep->max_pkt, ep->head, ep->bit);
+
+cfg_ept_end:
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+EXPORT_SYMBOL(usb_configure_endpoint);
+
+#define NUM_EPTS 15	/* number of in or out non-ctrl endpoints */
+struct usb_endpoint *usb_alloc_endpoint(unsigned direction)
+{
+	struct usb_info *ui = the_usb_info;
+	struct usb_endpoint *ept = NULL;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (direction & USB_DIR_IN)
+		ept = (&ui->ep0in);
+	else
+		ept = (&ui->ep0out);
+
+	for (i = 0; i < NUM_EPTS; i++) {
+		ept++;
+		if (!ept->alloced) {
+			ept->alloced = 1;
+			ept->ui = ui;
+			spin_unlock_irqrestore(&ui->lock, flags);
+			return ept;
+		}
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return NULL;
+}
+EXPORT_SYMBOL(usb_alloc_endpoint);
+
+int usb_free_endpoint(struct usb_endpoint *ept)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long flags;
+
+	if (!ept)
+		return -EINVAL;
+	spin_lock_irqsave(&ui->lock, flags);
+	ept->alloced = 0;
+	ept->ui = 0;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_free_endpoint);
+
+struct usb_request *usb_ept_alloc_req(struct usb_endpoint *ept,
+			unsigned bufsize)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req;
+
+	if (!ui)
+		return NULL;
+
+	req = kzalloc(sizeof(*req), GFP_ATOMIC);
+	if (!req)
+		goto fail1;
+
+	req->item = dma_pool_alloc(ui->pool, GFP_ATOMIC, &req->item_dma);
+	if (!req->item)
+		goto fail2;
+
+	if (bufsize) {
+		req->req.buf = kmalloc(bufsize, GFP_ATOMIC);
+		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 NULL;
+}
+EXPORT_SYMBOL(usb_ept_alloc_req);
+
+static void do_free_req(struct usb_info *ui, struct msm_request *req)
+{
+	if (req->alloced)
+		kfree(req->req.buf);
+
+	dma_pool_free(ui->pool, req->item, req->item_dma);
+	kfree(req);
+}
+
+void usb_ept_free_req(struct usb_endpoint *ept, struct usb_request *_req)
+{
+	struct msm_request *req, *temp_req, *prev_req;
+	struct usb_info *ui;
+	unsigned long flags;
+	int dead = 0;
+	if (!ept || !_req)
+		return;
+
+	ui = ept->ui;
+	if (!ui)
+		return;
+
+	req = to_msm_request(_req);
+	spin_lock_irqsave(&ui->lock, flags);
+	/* defer freeing resources if request is still busy */
+	if (req->busy)
+		dead = req->dead = 1;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	/* if req->dead, then we will clean up when the request finishes */
+	if (!dead) {
+		temp_req = ept->req;
+		prev_req = temp_req;
+		while (temp_req != NULL) {
+			if (req == temp_req && ept->req != temp_req)
+				prev_req->next = temp_req->next;
+
+			prev_req = temp_req;
+			temp_req = temp_req->next;
+		}
+		if (ept->req == req)
+			ept->req = req->next;
+		req->req.complete = NULL;
+		do_free_req(ui, req);
+	} else
+		pr_err("%s: req is busy, can't free req\n", __func__);
+}
+EXPORT_SYMBOL(usb_ept_free_req);
+
+void usb_ept_enable(struct usb_endpoint *ept, int yes)
+{
+	struct usb_info *ui;
+	int in;
+	unsigned n;
+	unsigned char xfer;
+
+	if (!ept || !ept->ui)
+		return;
+	ui = ept->ui;
+	in = ept->flags & EPT_FLAG_IN;
+	if (!ept->ep_descriptor)
+		return;
+
+	if (ui->in_lpm) {
+		pr_err("%s: controller is in lpm, cannot proceed\n", __func__);
+		return;
+	}
+
+	xfer = ept->ep_descriptor->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in) {
+		if (xfer == USB_ENDPOINT_XFER_BULK)
+			n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_BULK;
+		else if (xfer == USB_ENDPOINT_XFER_INT)
+			n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_INT;
+		if (yes)
+			n |= CTRL_TXE | CTRL_TXR;
+		else
+			n &= (~CTRL_TXE);
+	} else {
+		if (xfer == USB_ENDPOINT_XFER_BULK)
+			n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_BULK;
+		else if (xfer == USB_ENDPOINT_XFER_INT)
+			n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_INT;
+		if (yes)
+			n |= CTRL_RXE | CTRL_RXR;
+		else
+			n &= ~(CTRL_RXE);
+	}
+	/* complete all the updates to ept->head before enabling endpoint*/
+	dma_coherent_pre_ops();
+	writel(n, USB_ENDPTCTRL(ept->num));
+}
+EXPORT_SYMBOL(usb_ept_enable);
+
+static void usb_ept_start(struct usb_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req = ept->req;
+
+	BUG_ON(req->live);
+
+	/* link the hw queue head to the request's transaction item */
+	ept->head->next = req->item_dma;
+	ept->head->info = 0;
+
+	/* memory barrier to flush the data before priming endpoint*/
+	dma_coherent_pre_ops();
+	/* start the endpoint */
+	writel(1 << ept->bit, USB_ENDPTPRIME);
+
+	/* mark this chain of requests as live */
+	while (req) {
+		req->live = 1;
+		if (req->item->next == TERMINATE)
+			break;
+		req = req->next;
+	}
+}
+
+int usb_ept_queue_xfer(struct usb_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;
+	struct ept_queue_item *item = req->item;
+	unsigned length = req->req.length;
+
+	if (length > 0x4000)
+		return -EMSGSIZE;
+
+	if (ui->in_lpm) {
+		req->req.status = usb_remote_wakeup();
+		if (req->req.status) {
+			pr_debug("%s:RWakeup generation failed, EP = %x\n",
+							__func__, ept->bit);
+			return req->req.status;
+		}
+	}
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (req->busy) {
+		req->req.status = -EBUSY;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		printk(KERN_INFO
+		       "usb_ept_queue_xfer() tried to queue busy request\n");
+		return -EBUSY;
+	}
+
+	if (!ui->online && (ept->num != 0)) {
+		req->req.status = -ENODEV;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		printk(KERN_INFO "usb_ept_queue_xfer() tried to queue request"
+				"while offline; ept->bit: %x\n", ept->bit);
+		return -ENODEV;
+	}
+
+	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);
+
+	/* prepare the transaction descriptor item for the hardware */
+	item->next = TERMINATE;
+	item->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE;
+	item->page0 = req->dma;
+	item->page1 = (req->dma + 0x1000) & 0xfffff000;
+	item->page2 = (req->dma + 0x2000) & 0xfffff000;
+	item->page3 = (req->dma + 0x3000) & 0xfffff000;
+
+	/* 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;
+
+		/* only modify the hw transaction next pointer if
+		 * that request is not live
+		 */
+		if (!last->live)
+			last->item->next = req->item_dma;
+	} else {
+		/* queue was empty -- kick the hardware */
+		ept->req = req;
+		usb_ept_start(ept);
+	}
+	ept->last = req;
+
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(usb_ept_queue_xfer);
+
+int usb_ept_flush(struct usb_endpoint *ept)
+{
+	printk("usb_ept_flush \n");
+	flush_endpoint(ept);
+	return 0;
+}
+
+int usb_ept_get_max_packet(struct usb_endpoint *ept)
+{
+	return ept->max_pkt;
+}
+EXPORT_SYMBOL(usb_ept_get_max_packet);
+
+int usb_remote_wakeup(void)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (!ui->remote_wakeup) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_err("%s: remote wakeup not supported\n", __func__);
+		return -ENOTSUPP;
+	}
+
+	if (!ui->online) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_err("%s: device is not configured\n", __func__);
+		return -ENODEV;
+	}
+
+	if (ui->in_lpm)
+		usb_lpm_exit(ui);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	/* if usb_lpm_exit is unable to set PHCD,
+	 * it would initiate workthread to set the PHCD
+	 */
+	if (cancel_work_sync(&ui->li.wakeup_phy))
+		usb_lpm_wakeup_phy(NULL);
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->in_lpm) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_err("%s: cannot bring controller out of lpm\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!usb_is_online(ui)) {
+		pr_debug("%s: enabling force resume\n", __func__);
+		writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+	} else
+		pr_debug("%s: controller seems to be out of suspend already\n",
+				__func__);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_remote_wakeup);
+
+/* --- endpoint 0 handling --- */
+
+static void set_configuration(struct usb_info *ui, int yes)
+{
+	unsigned i;
+
+	ui->online = !!yes;
+
+	for (i = 0; i < ui->num_funcs; i++) {
+		struct usb_function_info *fi = ui->func[i];
+		if (!fi || !(ui->composition->functions & (1 << i)))
+			continue;
+		if (fi->func->configure)
+			fi->func->configure(yes, fi->func->context);
+	}
+}
+
+static void ep0out_complete(struct usb_endpoint *ept, struct usb_request *req)
+{
+	req->complete = 0;
+}
+
+static void ep0in_complete(struct usb_endpoint *ept, struct usb_request *req)
+{
+	/* queue up the receive of the ACK response from the host */
+	if (req->status == 0) {
+		struct usb_info *ui = ept->ui;
+		req->length = 0;
+		req->complete = ep0out_complete;
+		usb_ept_queue_xfer(&ui->ep0out, req);
+	}
+}
+
+static void ep0in_complete_sendzero(
+		struct usb_endpoint *ept, struct usb_request *req)
+{
+	if (req->status == 0) {
+		struct usb_info *ui = ept->ui;
+		req->length = 0;
+		req->complete = ep0in_complete;
+		usb_ept_queue_xfer(&ui->ep0in, req);
+	}
+}
+
+static void ep0_status_complete(
+		struct usb_endpoint *ept, struct usb_request *req)
+{
+	struct usb_info *ui = ept->ui;
+	unsigned int i;
+
+	if (!ui->test_mode)
+		return;
+
+	switch (ui->test_mode) {
+	case J_TEST:
+		pr_info("usb electrical test mode: (J)\n");
+		i = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(i | PORTSC_PTC_J_STATE, USB_PORTSC);
+		break;
+
+	case K_TEST:
+		pr_info("usb electrical test mode: (K)\n");
+		i = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(i | PORTSC_PTC_K_STATE, USB_PORTSC);
+		break;
+
+	case SE0_NAK_TEST:
+		pr_info("usb electrical test mode: (SE0-NAK)\n");
+		i = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(i | PORTSC_PTC_SE0_NAK, USB_PORTSC);
+		break;
+
+	case TST_PKT_TEST:
+		pr_info("usb electrical test mode: (TEST_PKT)\n");
+		i = readl(USB_PORTSC) & (~PORTSC_PTC);
+		writel(i | PORTSC_PTC_TST_PKT, USB_PORTSC);
+		break;
+	default:
+		pr_err("usb:%s: undefined test mode: (%x)\n",
+				__func__, ui->test_mode);
+	}
+
+}
+
+static void ep0_setup_ack(struct usb_info *ui)
+{
+	struct usb_request *req = ui->setup_req;
+	req->length = 0;
+	req->complete = ep0_status_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_receive(struct usb_info *ui, unsigned len)
+{
+	ui->ep0out_req->length = len;
+	usb_ept_queue_xfer(&ui->ep0out, ui->ep0out_req);
+}
+
+static void ep0_setup_send(struct usb_info *ui, unsigned wlen)
+{
+	struct usb_request *req = ui->setup_req;
+	struct usb_endpoint *ept = &ui->ep0in;
+
+	/* never send more data than the host requested */
+	if (req->length > wlen)
+		req->length = wlen;
+
+	/* if we are sending a short response that ends on
+	 * a packet boundary, we'll need to send a zero length
+	 * packet as well.
+	 */
+	if ((req->length != wlen) && ((req->length & 63) == 0)) {
+		req->complete = ep0in_complete_sendzero;
+	} else {
+		req->complete = ep0in_complete;
+	}
+
+	usb_ept_queue_xfer(ept, req);
+}
+
+
+static int usb_find_descriptor(struct usb_info *ui, struct usb_ctrlrequest *ctl,
+				struct usb_request *req);
+
+static void handle_setup(struct usb_info *ui)
+{
+	struct usb_ctrlrequest ctl;
+
+	memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl));
+	writel(EPT_RX(0), USB_ENDPTSETUPSTAT);
+
+	/* any pending ep0 transactions must be canceled */
+	flush_endpoint(&ui->ep0out);
+	flush_endpoint(&ui->ep0in);
+
+	/* let functions handle vendor and class requests */
+	if ((ctl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
+		struct usb_function *func;
+
+		/* Send stall if received interface number is invalid */
+		if (ctl.wIndex >= ui->next_ifc_num)
+			goto stall;
+
+		func = ui->func2ifc_map[ctl.wIndex];
+		if (func && func->setup) {
+			if (ctl.bRequestType & USB_DIR_IN) {
+				struct usb_request *req = ui->setup_req;
+				int ret = func->setup(&ctl,
+						req->buf, SETUP_BUF_SIZE,
+						func->context);
+				if (ret >= 0) {
+					req->length = ret;
+					ep0_setup_send(ui, ctl.wLength);
+					return;
+				}
+			} else {
+				int ret = func->setup(&ctl, NULL, 0,
+							func->context);
+				if (ret == 0) {
+					ep0_setup_ack(ui);
+					return;
+				} else if (ret > 0) {
+					ep0_setup_receive(ui, ret);
+					return;
+				}
+			}
+		}
+		goto stall;
+		return;
+	}
+
+	switch (ctl.bRequest) {
+	case USB_REQ_GET_STATUS:
+	{
+		struct usb_request *req = ui->setup_req;
+		if ((ctl.bRequestType & (USB_DIR_MASK)) != (USB_DIR_IN))
+			break;
+		if (ctl.wLength != 2)
+			break;
+		req->length = 2;
+		switch (ctl.bRequestType & USB_RECIP_MASK) {
+		case USB_RECIP_ENDPOINT:
+		{
+			unsigned num = ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+			struct usb_endpoint *ept;
+
+			if (num == 0)
+				break;
+			if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+				num += 16;
+			ept = ui->ept + num;
+			memcpy(req->buf, &ept->ept_halted, 2);
+			break;
+		}
+
+		case USB_RECIP_DEVICE:
+		{
+			unsigned short temp = 0;
+			if (usb_msm_get_selfpowered())
+				temp = 1 << USB_DEVICE_SELF_POWERED;
+			if (usb_msm_get_remotewakeup())
+				temp |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+			memcpy(req->buf, &temp, 2);
+			break;
+		}
+
+		case USB_RECIP_INTERFACE:
+			memset(req->buf, 0, 2);
+			break;
+		default:
+			printk(KERN_ERR "Unreconginized recipient\n");
+			break;
+		}
+
+		ep0_setup_send(ui, 2);
+		return;
+	}
+
+	case USB_REQ_GET_DESCRIPTOR:
+	{
+		struct usb_request *req;
+
+		if ((ctl.bRequestType & (USB_DIR_MASK)) != (USB_DIR_IN))
+			break;
+
+		req = ui->setup_req;
+		if (!usb_find_descriptor(ui, &ctl, req)) {
+			if (req->length > ctl.wLength)
+				req->length = ctl.wLength;
+			ep0_setup_send(ui, ctl.wLength);
+			return;
+		}
+		break;
+	}
+
+	case USB_REQ_SET_FEATURE:
+		if ((ctl.bRequestType & (USB_DIR_MASK)) != (USB_DIR_OUT))
+			break;
+		if (ctl.wLength != 0)
+			break;
+		switch (ctl.bRequestType & USB_RECIP_MASK) {
+		case USB_RECIP_DEVICE:
+			if (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP) {
+				ui->remote_wakeup = 1;
+				ep0_setup_ack(ui);
+				return;
+			} else if (ctl.wValue == USB_DEVICE_TEST_MODE) {
+				if (ctl.wIndex & 0x0f)
+					break;
+				ui->test_mode = ctl.wIndex;
+				ep0_setup_ack(ui);
+				return;
+			}
+			break;
+
+		case USB_RECIP_ENDPOINT:
+		{
+			unsigned num = ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+			if ((num == 0) || (ctl.wValue != 0))
+				break;
+			if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+				num += 16;
+			usb_ept_set_halt(ui->ept + num);
+			ep0_setup_ack(ui);
+			return;
+		}
+
+		default:
+			pr_err("usb: %s: set_feature: unrecognized recipient\n",
+					__func__);
+			break;
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+	{
+		if ((ctl.bRequestType & (USB_DIR_MASK)) != (USB_DIR_OUT))
+			break;
+		if (ctl.wLength != 0)
+			break;
+
+		switch (ctl.bRequestType & USB_RECIP_MASK) {
+		case USB_RECIP_DEVICE:
+			if (ctl.wValue != USB_DEVICE_REMOTE_WAKEUP)
+				break;
+			ui->remote_wakeup = 0;
+			ep0_setup_ack(ui);
+			return;
+		case USB_RECIP_ENDPOINT:
+		{
+			unsigned num;
+			if (ctl.wValue != USB_ENDPOINT_HALT)
+				break;
+			num = ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+			if (num != 0) {
+				if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+					num += 16;
+				usb_ept_clear_halt(ui->ept + num);
+			}
+			ep0_setup_ack(ui);
+			return;
+		}
+		default:
+			pr_info("unsupported clear feature command\n");
+			pr_info("Request-type:(%08x) wValue:(%08x) "
+					"wIndex:(%08x) wLength:(%08x)\n",
+						ctl.bRequestType, ctl.wValue,
+						ctl.wIndex, ctl.wLength);
+			break;
+		}
+		break;
+	}
+
+	case USB_REQ_SET_INTERFACE:
+		if ((ctl.bRequestType & (USB_DIR_MASK | USB_RECIP_MASK))
+			!= (USB_DIR_OUT | USB_RECIP_INTERFACE))
+			break;
+		if (ui->func2ifc_map[ctl.wIndex]->set_interface) {
+			ui->func2ifc_map[ctl.wIndex]->set_interface(ctl.wIndex,
+					ctl.wValue,
+					ui->func2ifc_map[ctl.wIndex]->context);
+			ep0_setup_ack(ui);
+			return;
+		}
+		break;
+	case USB_REQ_GET_INTERFACE:
+		{
+		struct usb_function *f;
+		struct usb_request *req = ui->setup_req;
+		int ifc_num = ctl.wIndex;
+		int ret = 0;
+
+		if ((ctl.bRequestType & (USB_DIR_MASK | USB_RECIP_MASK))
+					!= (USB_DIR_IN | USB_RECIP_INTERFACE))
+			break;
+
+		f = ui->func2ifc_map[ifc_num];
+		if (!f->get_interface)
+			break;
+		ret = f->get_interface(ifc_num,
+				ui->func2ifc_map[ifc_num]->context);
+		if (ret < 0)
+			break;
+		req->length = ctl.wLength;
+		memcpy(req->buf, &ret, req->length);
+		ep0_setup_send(ui, ctl.wLength);
+		return;
+		}
+	case USB_REQ_SET_CONFIGURATION:
+		if ((ctl.bRequestType & USB_DIR_MASK) != USB_DIR_OUT)
+			break;
+		ui->configured = ctl.wValue;
+		pr_info("hsusb set_configuration wValue = %d usbcmd = %x\n",
+						ctl.wValue, readl(USB_USBCMD));
+		set_configuration(ui, ctl.wValue);
+		ep0_setup_ack(ui);
+		ui->flags = USB_FLAG_CONFIGURE;
+		if (ui->configured)
+			ui->usb_state = USB_STATE_CONFIGURED;
+		queue_delayed_work(usb_work, &ui->work, 0);
+		return;
+
+	case USB_REQ_GET_CONFIGURATION:
+	{
+		unsigned conf;
+		struct usb_request *req = ui->setup_req;
+		req->length = 1;
+		conf = ui->configured;
+		memcpy(req->buf, &conf, req->length);
+		ep0_setup_send(ui, ctl.wLength);
+		return;
+	}
+
+	case USB_REQ_SET_ADDRESS:
+		if ((ctl.bRequestType & (USB_DIR_MASK | USB_RECIP_MASK))
+			!= (USB_DIR_OUT | USB_RECIP_DEVICE))
+			break;
+		ui->usb_state = USB_STATE_ADDRESS;
+		writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR);
+		ep0_setup_ack(ui);
+		return;
+	}
+
+stall:
+	ep0_setup_stall(ui);
+	return;
+
+}
+
+static void handle_endpoint(struct usb_info *ui, unsigned bit)
+{
+	struct usb_endpoint *ept = ui->ept + bit;
+	struct msm_request *req;
+	unsigned long flags;
+	unsigned info;
+
+#if 0
+	printk(KERN_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);
+#endif
+	if (!ept) {
+		pr_err("%s: ept is null: ep bit = %d\n", __func__, bit);
+		return;
+	}
+
+	/* expire all requests that are no longer active */
+	spin_lock_irqsave(&ui->lock, flags);
+	while ((req = ept->req)) {
+		/* clean speculative fetches on req->item->info */
+		dma_coherent_post_ops();
+		info = req->item->info;
+
+		/* 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;
+		}
+
+		/* if the transaction is still in-flight, stop here */
+		if (info & INFO_ACTIVE)
+			break;
+
+		/* 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;
+			printk(KERN_INFO "hsusb: 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->dead)
+			do_free_req(ui, req);
+
+		if (req->req.complete) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			req->req.complete(ept, &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?!)
+	*/
+
+	if (ui->in_lpm) {
+		pr_err("%s: controller is in lpm, cannot proceed\n", __func__);
+		return;
+	}
+
+	do {
+		writel(bits, USB_ENDPTFLUSH);
+		while (readl(USB_ENDPTFLUSH) & bits)
+			udelay(100);
+	} while (readl(USB_ENDPTSTAT) & bits);
+}
+
+static void flush_endpoint_sw(struct usb_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	struct msm_request *req, *next;
+	unsigned long flags;
+
+	/* inactive endpoints have nothing to do here */
+	if (!ui || !ept->alloced || !ept->max_pkt)
+		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) {
+		next = req->next;
+
+		req->busy = 0;
+		req->live = 0;
+		req->req.status = -ENODEV;
+		req->req.actual = 0;
+		if (req->req.complete) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			req->req.complete(ept, &req->req);
+			spin_lock_irqsave(&ui->lock, flags);
+		}
+		if (req->dead)
+			do_free_req(ui, req);
+		req = req->next;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void flush_endpoint(struct usb_endpoint *ept)
+{
+	if (!ept->ui)
+		return;
+
+	flush_endpoint_hw(ept->ui, (1 << ept->bit));
+	flush_endpoint_sw(ept);
+}
+
+static void flush_all_endpoints(struct usb_info *ui)
+{
+	unsigned n;
+
+	flush_endpoint_hw(ui, 0xffffffff);
+
+	for (n = 0; n < 32; n++)
+		flush_endpoint_sw(ui->ept + n);
+}
+
+#define HW_DELAY_FOR_LPM msecs_to_jiffies(1000)
+#define DELAY_FOR_USB_VBUS_STABILIZE msecs_to_jiffies(500)
+static irqreturn_t usb_interrupt(int irq, void *data)
+{
+	struct usb_info *ui = data;
+	unsigned n;
+	unsigned speed;
+
+	if (!ui->active)
+		return IRQ_HANDLED;
+
+	if (ui->in_lpm) {
+		usb_lpm_exit(ui);
+		return IRQ_HANDLED;
+	}
+
+	n = readl(USB_USBSTS);
+	writel(n, USB_USBSTS);
+
+	/* somehow we got an IRQ while in the reset sequence: ignore it */
+	if (ui->running == 0) {
+		pr_err("%s: ui->running is zero\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	if (n & STS_PCI) {
+		if (!(readl(USB_PORTSC) & PORTSC_PORT_RESET)) {
+			speed = (readl(USB_PORTSC) & PORTSC_PORT_SPEED_MASK);
+			switch (speed) {
+			case PORTSC_PORT_SPEED_HIGH:
+				pr_info("hsusb resume: speed = HIGH\n");
+				ui->speed = USB_SPEED_HIGH;
+				break;
+
+			case PORTSC_PORT_SPEED_FULL:
+				pr_info("hsusb resume: speed = FULL\n");
+				ui->speed = USB_SPEED_FULL;
+				break;
+
+			default:
+				pr_err("hsusb resume: Unknown Speed\n");
+				ui->speed = USB_SPEED_UNKNOWN;
+				break;
+			}
+		}
+
+		/* pci interrutpt would also be generated when resuming
+		 * from bus suspend, following check would avoid kick
+		 * starting usb main thread in case of pci interrupts
+		 * during enumeration
+		 */
+		if (ui->configured && ui->chg_type == USB_CHG_TYPE__SDP) {
+			ui->usb_state = USB_STATE_CONFIGURED;
+			ui->flags = USB_FLAG_RESUME;
+			queue_delayed_work(usb_work, &ui->work, 0);
+		}
+	}
+
+	if (n & STS_URI) {
+		pr_info("hsusb reset interrupt\n");
+		ui->usb_state = USB_STATE_DEFAULT;
+		ui->configured = 0;
+		schedule_work(&ui->chg_stop);
+
+		writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
+		writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
+		writel(0xffffffff, USB_ENDPTFLUSH);
+		writel(0, USB_ENDPTCTRL(1));
+
+		if (ui->online != 0) {
+			/* marking us offline will cause ept queue attempts to fail */
+			ui->online = 0;
+
+			flush_all_endpoints(ui);
+
+			/* XXX: we can't seem to detect going offline, so deconfigure
+			 * XXX: on reset for the time being
+			 */
+			set_configuration(ui, 0);
+		}
+	}
+
+	if (n & STS_SLI) {
+		pr_info("hsusb suspend interrupt\n");
+		ui->usb_state = USB_STATE_SUSPENDED;
+
+		/* stop usb charging */
+		schedule_work(&ui->chg_stop);
+	}
+
+	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));
+		}
+	}
+
+	n = readl(USB_OTGSC);
+	writel(n, USB_OTGSC);
+
+	if (n & OTGSC_BSVIS) {
+		/*Verify B Session Valid Bit to verify vbus status*/
+		if (B_SESSION_VALID & n)	{
+			pr_info("usb cable connected\n");
+			ui->usb_state = USB_STATE_POWERED;
+			ui->flags = USB_FLAG_VBUS_ONLINE;
+			/* Wait for 100ms to stabilize VBUS before initializing
+			 * USB and detecting charger type
+			 */
+			queue_delayed_work(usb_work, &ui->work, 0);
+		} else {
+			int i;
+
+			usb_disable_pullup(ui);
+
+			printk(KERN_INFO "usb cable disconnected\n");
+			ui->usb_state = USB_STATE_NOTATTACHED;
+			ui->configured = 0;
+			for (i = 0; i < ui->num_funcs; i++) {
+				struct usb_function_info *fi = ui->func[i];
+				if (!fi ||
+				!(ui->composition->functions & (1 << i)))
+					continue;
+				if (fi->func->disconnect)
+					fi->func->disconnect
+						(fi->func->context);
+			}
+			ui->flags = USB_FLAG_VBUS_OFFLINE;
+			queue_delayed_work(usb_work, &ui->work, 0);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void usb_prepare(struct usb_info *ui)
+{
+	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->speed = USB_SPEED_UNKNOWN;
+
+	init_endpoints(ui);
+
+	ui->ep0in.max_pkt = 64;
+	ui->ep0in.ui = ui;
+	ui->ep0in.alloced = 1;
+	ui->ep0out.max_pkt = 64;
+	ui->ep0out.ui = ui;
+	ui->ep0out.alloced = 1;
+
+	ui->setup_req = usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE);
+	ui->ep0out_req = usb_ept_alloc_req(&ui->ep0out, ui->ep0out.max_pkt);
+
+	INIT_WORK(&ui->chg_stop, usb_chg_stop);
+	INIT_WORK(&ui->li.wakeup_phy, usb_lpm_wakeup_phy);
+	INIT_DELAYED_WORK(&ui->work, usb_do_work);
+	INIT_DELAYED_WORK(&ui->chg_legacy_det, usb_chg_legacy_detect);
+}
+
+static int usb_is_online(struct usb_info *ui)
+{
+	/* continue lpm if bus is suspended or disconnected or stopped*/
+	if (((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) ||
+			((readl(USB_PORTSC) & PORTSC_CCS) == 0) ||
+			((readl(USB_USBCMD) & USBCMD_RS) == 0))
+		return 0;
+
+	pr_debug("usb is online\n");
+	pr_debug("usbcmd:(%08x) usbsts:(%08x) portsc:(%08x)\n",
+			readl(USB_USBCMD),
+			readl(USB_USBSTS),
+			readl(USB_PORTSC));
+	return -1;
+}
+
+static int usb_wakeup_phy(struct usb_info *ui)
+{
+	int i;
+
+	writel(readl(USB_USBCMD) & ~ULPI_STP_CTRL, USB_USBCMD);
+
+	/* some circuits automatically clear PHCD bit */
+	for (i = 0; i < 5 && (readl(USB_PORTSC) & PORTSC_PHCD); i++) {
+		writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC);
+		msleep(1);
+	}
+
+	if ((readl(USB_PORTSC) & PORTSC_PHCD)) {
+		pr_err("%s: cannot clear phcd bit\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int usb_suspend_phy(struct usb_info *ui)
+{
+	int i;
+	unsigned long flags;
+
+	if (usb_is_online(ui))
+		return -1;
+
+	/* spec talks about following bits in LPM for external phy.
+	 * But they are ignored because
+	 * 1. disabling interface protection circuit: by disabling
+	 * interface protection curcuit we cannot come out
+	 * of lpm as async interrupts would be disabled
+	 * 2. setting the suspendM bit: this bit would be set by usb
+	 * controller once we set phcd bit.
+	 */
+	switch (PHY_TYPE(ui->phy_info)) {
+	case USB_PHY_INTEGRATED:
+		if (!is_phy_45nm())
+			ulpi_read(ui, 0x14);
+
+		/* turn on/off otg comparators */
+		if (ui->vbus_sn_notif &&
+			ui->usb_state == USB_STATE_NOTATTACHED)
+			ulpi_write(ui, 0x00, 0x30);
+		else
+			ulpi_write(ui, 0x01, 0x30);
+
+		if (!is_phy_45nm())
+			ulpi_write(ui, 0x08, 0x09);
+
+		break;
+
+	case USB_PHY_UNDEFINED:
+		pr_err("%s: undefined phy type\n", __func__);
+		return -1;
+	}
+
+	/* loop for large amount of time */
+	for (i = 0; i < 500; i++) {
+		spin_lock_irqsave(&ui->lock, flags);
+		if (usb_is_online(ui)) {
+			spin_unlock_irqrestore(&ui->lock, flags);
+			return -1;
+		}
+		/* set phy to be in lpm */
+		writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+		spin_unlock_irqrestore(&ui->lock, flags);
+
+		msleep(1);
+		if (readl(USB_PORTSC) & PORTSC_PHCD)
+			goto blk_stp_sig;
+	}
+
+	if (!(readl(USB_PORTSC) & PORTSC_PHCD)) {
+		pr_err("unable to set phcd of portsc reg\n");
+		pr_err("Reset HW link and phy to recover from phcd error\n");
+		usb_hw_reset(ui);
+		return -1;
+	}
+
+	/* we have to set this bit again to work-around h/w bug */
+	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+
+blk_stp_sig:
+	/* block the stop signal */
+	writel(readl(USB_USBCMD) | ULPI_STP_CTRL, USB_USBCMD);
+
+	return 0;
+}
+
+/* SW workarounds
+Issue#2		- Integrated PHY Calibration
+Symptom		- Electrical compliance failure in eye-diagram tests
+SW workaround		- Try to raise amplitude to 400mV
+
+Issue#3		- AHB Posted Writes
+Symptom		- USB stability
+SW workaround		- This programs xtor ON, BURST disabled and
+			unspecified length of INCR burst enabled
+*/
+static int usb_hw_reset(struct usb_info *ui)
+{
+	unsigned i;
+	struct msm_hsusb_platform_data *pdata;
+	unsigned long timeout;
+	unsigned val = 0;
+
+	pdata = ui->pdev->dev.platform_data;
+
+	clk_enable(ui->clk);
+	/* reset the phy before resetting link */
+	if (readl(USB_PORTSC) & PORTSC_PHCD)
+		usb_wakeup_phy(ui);
+	/* rpc call for phy_reset */
+	if (ui->pdata->phy_reset)
+		ui->pdata->phy_reset(ui->addr);
+	else
+		msm_hsusb_phy_reset();
+	/* Give some delay to settle phy after reset */
+	msleep(100);
+
+	/* RESET */
+	writel(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	while (readl(USB_USBCMD) & USBCMD_RESET) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(&ui->pdev->dev, "usb link reset timeout\n");
+			break;
+		}
+		msleep(1);
+	}
+
+	/* select DEVICE mode with SDIS active */
+	writel((USBMODE_SDIS | USBMODE_DEVICE), USB_USBMODE);
+	msleep(1);
+
+	/* select ULPI phy */
+	i = (readl(USB_PORTSC) & ~PORTSC_PTS);
+	writel(i | PORTSC_PTS_ULPI, USB_PORTSC);
+	/* set usb controller interrupt latency to zero*/
+	writel((readl(USB_USBCMD) & ~USBCMD_ITC_MASK) | USBCMD_ITC(0),
+							USB_USBCMD);
+
+	/* If the target is 7x01 and roc version is > 1.2, set
+	 * the AHB mode to 2 for maximum performance, else set
+	 * it to 1, to bypass the AHB transactor for stability.
+	 */
+	if (PHY_TYPE(ui->phy_info) == USB_PHY_EXTERNAL) {
+		if (pdata->soc_version >= SOC_ROC_2_0)
+			writel(0x02, USB_ROC_AHB_MODE);
+		else
+			writel(0x01, USB_ROC_AHB_MODE);
+	} else {
+		unsigned cfg_val;
+
+		/* Raise  amplitude to 400mV
+		 * SW workaround, Issue#2
+		 */
+		cfg_val = ulpi_read(ui, ULPI_CONFIG_REG);
+		cfg_val |= ULPI_AMPLITUDE_MAX;
+		ulpi_write(ui, cfg_val, ULPI_CONFIG_REG);
+
+		writel(0x0, USB_AHB_BURST);
+		writel(0x00, USB_AHB_MODE);
+	}
+
+	/* TBD: do we have to add DpRise, ChargerRise and
+	 * IdFloatRise for 45nm
+	 */
+	/* Disable VbusValid and SessionEnd comparators */
+	val = ULPI_VBUS_VALID | ULPI_SESS_END;
+
+	/* enable id interrupt only when transceiver is available */
+	if (ui->xceiv)
+		writel(readl(USB_OTGSC) | OTGSC_BSVIE | OTGSC_IDIE, USB_OTGSC);
+	else {
+		writel((readl(USB_OTGSC) | OTGSC_BSVIE) & ~OTGSC_IDPU,
+							USB_OTGSC);
+		ulpi_write(ui, ULPI_IDPU, ULPI_OTG_CTRL_CLR);
+		val |= ULPI_HOST_DISCONNECT | ULPI_ID_GND;
+	}
+	ulpi_write(ui, val, ULPI_INT_RISE_CLR);
+	ulpi_write(ui, val, ULPI_INT_FALL_CLR);
+
+	/* we are just setting the pointer in the hwblock. Since the
+	 * endpoint isnt enabled the hw block doenst read the contents
+	 * of ui->dma - so we dont need a barrier here
+	 * */
+	writel(ui->dma, USB_ENDPOINTLISTADDR);
+
+	clk_disable(ui->clk);
+
+	return 0;
+}
+
+static void usb_reset(struct usb_info *ui)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->running = 0;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+#if 0
+	/* we should flush and shutdown cleanly if already running */
+	writel(0xffffffff, USB_ENDPTFLUSH);
+	msleep(2);
+#endif
+
+	if (usb_hw_reset(ui)) {
+		pr_info("%s: h/w reset failed\n", __func__);
+		return;
+	}
+
+	usb_configure_endpoint(&ui->ep0in, NULL);
+	usb_configure_endpoint(&ui->ep0out, NULL);
+
+	/* marking us offline will cause ept queue attempts to fail */
+	ui->online = 0;
+
+	/* terminate any pending transactions */
+	flush_all_endpoints(ui);
+
+	set_configuration(ui, 0);
+
+	spin_lock_irqsave(&ui->lock, flags);
+	ui->running = 1;
+	spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void usb_enable(void *handle, int enable)
+{
+	struct usb_info *ui = handle;
+	unsigned long flags;
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (enable) {
+		ui->flags |= USB_FLAG_RESET;
+		ui->active = 1;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		usb_do_work(&ui->work.work);
+	} else {
+		ui->active = 0;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		usb_clk_disable(ui);
+		msm_hsusb_suspend_locks_acquire(ui, 0);
+	}
+}
+
+static struct msm_otg_ops dcd_ops = {
+	.request = usb_enable,
+};
+
+void usb_start(struct usb_info *ui)
+{
+	int i, ret;
+
+	for (i = 0; i < ui->num_funcs; i++) {
+		struct usb_function_info *fi = ui->func[i];
+		if (!fi || !(ui->composition->functions & (1<<i)))
+			continue;
+		if (fi->enabled) {
+			pr_info("usb_bind_func() (%s)\n", fi->func->name);
+			fi->func->bind(fi->func->context);
+		}
+	}
+
+	ui->clk_enabled = 0;
+	ui->vreg_enabled = 0;
+
+	ui->xceiv = msm_otg_get_transceiver();
+	if (ui->xceiv) {
+		ui->flags = USB_FLAG_REG_OTG;
+		queue_delayed_work(usb_work, &ui->work, 0);
+	} else {
+		/*Initialize pm app RPC */
+		ret = msm_pm_app_rpc_init();
+		if (ret) {
+			pr_err("%s: pm_app_rpc connect failed\n", __func__);
+			goto out;
+		}
+		pr_info("%s: pm_app_rpc connect success\n", __func__);
+
+		ret = msm_pm_app_register_vbus_sn(&msm_hsusb_set_vbus_state);
+		if (ret) {
+			pr_err("%s:PMIC VBUS SN notif not supported\n", \
+					__func__);
+			msm_pm_app_rpc_deinit();
+			goto out;
+		}
+		pr_info("%s:PMIC VBUS SN notif supported\n", \
+					__func__);
+
+		ret = msm_pm_app_enable_usb_ldo(1);
+		if (ret) {
+			pr_err("%s: unable to turn on internal LDO", \
+					__func__);
+			msm_pm_app_unregister_vbus_sn(
+					&msm_hsusb_set_vbus_state);
+			msm_pm_app_rpc_deinit();
+			goto out;
+		}
+		ui->vbus_sn_notif = 1;
+out:
+		ui->active = 1;
+		ui->flags |= (USB_FLAG_START | USB_FLAG_RESET);
+		queue_delayed_work(usb_work, &ui->work, 0);
+	}
+
+}
+
+static LIST_HEAD(usb_function_list);
+static DEFINE_MUTEX(usb_function_list_lock);
+
+
+static struct usb_function_info *usb_find_function(const char *name)
+{
+	struct list_head *entry;
+	list_for_each(entry, &usb_function_list) {
+		struct usb_function_info *fi =
+			list_entry(entry, struct usb_function_info, list);
+		if (fi) {
+			if (!strcmp(name, fi->func->name))
+				return fi;
+		}
+	}
+
+	return NULL;
+}
+
+static void usb_try_to_bind(void)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long enabled_functions = 0;
+	int i;
+
+	if (!ui || ui->bound || !ui->pdev || !ui->composition)
+		return;
+
+	for (i = 0; i < ui->num_funcs; i++) {
+		if (ui->func[i])
+			enabled_functions |= (1 << i);
+	}
+	if ((enabled_functions & ui->composition->functions)
+					!= ui->composition->functions)
+		return;
+
+	usb_set_composition(ui->composition->product_id);
+	usb_configure_device_descriptor(ui);
+
+	/* we have found all the needed functions */
+	ui->bound = 1;
+	printk(KERN_INFO "msm_hsusb: functions bound. starting.\n");
+	usb_start(ui);
+}
+
+static int usb_get_function_index(const char *name)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+
+	for (i = 0; i < ui->num_funcs; i++) {
+		if (!strcmp(name, ui->functions_map[i].name))
+			return i;
+	}
+	return -1;
+}
+
+int usb_function_register(struct usb_function *driver)
+{
+	struct usb_info *ui = the_usb_info;
+	struct usb_function_info *fi;
+	int ret = 0;
+	int index;
+
+	mutex_lock(&usb_function_list_lock);
+
+	index = usb_get_function_index(driver->name);
+	if (index < 0) {
+		pr_err("%s: unsupported function = %s\n",
+				__func__, driver->name);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	fi = kzalloc(sizeof(*fi), GFP_KERNEL);
+	if (!fi) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	fi->func = driver;
+	list_add(&fi->list, &usb_function_list);
+	ui->func[index] = fi;
+	fi->func->ep0_out_req = ui->ep0out_req;
+	fi->func->ep0_in_req = ui->setup_req;
+	fi->func->ep0_out = &ui->ep0out;
+	fi->func->ep0_in = &ui->ep0in;
+	pr_info("%s: name = '%s',  map = %d\n", __func__, driver->name, index);
+
+	usb_try_to_bind();
+fail:
+	mutex_unlock(&usb_function_list_lock);
+	return ret;
+}
+EXPORT_SYMBOL(usb_function_register);
+
+static unsigned short usb_validate_product_id(unsigned short pid)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+
+	if (!ui || !ui->pdata)
+		return -1;
+
+	/* set idProduct based on which functions are enabled */
+	for (i = 0; i < ui->pdata->num_compositions; i++) {
+		if (ui->pdata->compositions[i].product_id == pid)
+			break;
+	}
+
+	if (i < ui->pdata->num_compositions) {
+		struct usb_composition *comp = &ui->pdata->compositions[i];
+		for (i = 0; i < ui->num_funcs; i++) {
+			if (comp->functions & (1 << i)) {
+				if (!ui->func[i]) {
+					pr_err("%s: func(%d) not available\n",
+								__func__, i);
+					return 0;
+				}
+			}
+		}
+		return comp->product_id;
+	} else
+		pr_err("%s: Product id (%x) is not supported\n", __func__, pid);
+	return 0;
+}
+
+static unsigned short usb_get_product_id(unsigned long enabled_functions)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+
+	if (!(ui && ui->pdata))
+		return -1;
+
+	/* set idProduct based on which functions are enabled */
+	for (i = 0; i < ui->pdata->num_compositions; i++) {
+		if (ui->pdata->compositions[i].functions == enabled_functions)
+			return ui->pdata->compositions[i].product_id;
+	}
+	return 0;
+}
+
+static void usb_uninit(struct usb_info *ui)
+{
+	int i;
+
+	for (i = 0; i < ui->strdesc_index; i++)
+		kfree(ui->strdesc[i]);
+	ui->strdesc_index = 1;
+	ui->next_ifc_num = 0;
+}
+
+static unsigned short usb_set_composition(unsigned short pid)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+
+	if (!(ui && ui->pdata))
+		return 0;
+
+	/* Retrieve product id on enabled functions */
+	for (i = 0; i < ui->pdata->num_compositions; i++) {
+		if (ui->pdata->compositions[i].product_id == pid) {
+			ui->composition = &ui->pdata->compositions[i];
+			for (i = 0; i < ui->num_funcs; i++) {
+				struct usb_function_info *fi = ui->func[i];
+				if (ui->func && fi && fi->func) {
+					fi->enabled = (ui->composition->
+							functions >> i) & 1;
+				}
+			}
+			pr_info("%s: composition set to product id = %x\n",
+				__func__, ui->composition->product_id);
+			return ui->composition->product_id;
+		}
+	}
+	pr_err("%s: product id (%x) not supported\n", __func__, pid);
+	return 0;
+}
+
+static void usb_switch_composition(unsigned short pid)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+	unsigned long flags;
+
+
+	if (!ui->active)
+		return;
+	if (!usb_validate_product_id(pid))
+		return;
+
+	disable_irq(ui->irq);
+	if (cancel_delayed_work_sync(&ui->work))
+		pr_info("%s: Removed work successfully\n", __func__);
+	if (ui->running) {
+		spin_lock_irqsave(&ui->lock, flags);
+		ui->running = 0;
+		ui->online = 0;
+		ui->bound = 0;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		/* we should come out of lpm to access registers */
+		if (ui->in_lpm) {
+			if (PHY_TYPE(ui->phy_info) == USB_PHY_EXTERNAL) {
+				disable_irq(ui->gpio_irq[0]);
+				disable_irq(ui->gpio_irq[1]);
+			}
+
+			if (ui->usb_state == USB_STATE_NOTATTACHED
+						&& ui->vbus_sn_notif)
+				msm_pm_app_enable_usb_ldo(1);
+
+			usb_lpm_exit(ui);
+			if (cancel_work_sync(&ui->li.wakeup_phy))
+				usb_lpm_wakeup_phy(NULL);
+			ui->in_lpm = 0;
+		}
+		/* disable usb and session valid interrupts */
+		writel(0, USB_USBINTR);
+		writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
+
+		/* stop the controller */
+		usb_disable_pullup(ui);
+		ui->usb_state = USB_STATE_NOTATTACHED;
+		switch_set_state(&ui->sdev, 0);
+		/* Before starting again, wait for 300ms
+		 * to make sure host detects soft disconnection
+		 **/
+		msleep(300);
+	}
+
+	for (i = 0; i < ui->num_funcs; i++) {
+		struct usb_function_info *fi = ui->func[i];
+		if (!fi || !fi->func || !fi->enabled)
+			continue;
+		if (fi->func->configure)
+			fi->func->configure(0, fi->func->context);
+		if (fi->func->unbind)
+			fi->func->unbind(fi->func->context);
+	}
+
+	usb_uninit(ui);
+	usb_set_composition(pid);
+	usb_configure_device_descriptor(ui);
+
+	/* initialize functions */
+	for (i = 0; i < ui->num_funcs; i++) {
+		struct usb_function_info *fi = ui->func[i];
+		if (!fi || !(ui->composition->functions & (1 << i)))
+			continue;
+		if (fi->enabled) {
+			if (fi->func->bind)
+				fi->func->bind(fi->func->context);
+		}
+	}
+
+	ui->bound = 1;
+	ui->flags = USB_FLAG_RESET;
+	queue_delayed_work(usb_work, &ui->work, 0);
+	enable_irq(ui->irq);
+}
+
+void usb_function_enable(const char *function, int enable)
+{
+	struct usb_function_info *fi;
+	struct usb_info *ui = the_usb_info;
+	unsigned long functions_mask;
+	int curr_enable;
+	unsigned short pid;
+	int i;
+
+	if (!ui)
+		return;
+
+	pr_info("%s: name = %s, enable = %d\n", __func__, function, enable);
+
+	fi = usb_find_function(function);
+	if (!fi) {
+		pr_err("%s: function (%s) not registered with DCD\n",
+							__func__, function);
+		return;
+	}
+	if (fi->enabled == enable) {
+		pr_err("%s: function (%s) state is same\n",
+						__func__, function);
+		return;
+	}
+	functions_mask = 0;
+	curr_enable = fi->enabled;
+	fi->enabled = enable;
+	for (i = 0; i < ui->num_funcs; i++) {
+		struct usb_function_info *fi = ui->func[i];
+		if (fi && fi->enabled)
+			functions_mask |= (1 << i);
+	}
+
+	pid = usb_get_product_id(functions_mask);
+	if (!pid) {
+		fi->enabled = curr_enable;
+		pr_err("%s: mask (%lx) not matching with any products\n",
+						__func__, functions_mask);
+		pr_err("%s: continuing with current composition\n", __func__);
+		return;
+	}
+	usb_switch_composition(pid);
+}
+EXPORT_SYMBOL(usb_function_enable);
+
+static int usb_free(struct usb_info *ui, int ret)
+{
+	disable_irq_wake(ui->irq);
+	free_irq(ui->irq, ui);
+	if (ui->gpio_irq[0])
+		free_irq(ui->gpio_irq[0], NULL);
+	if (ui->gpio_irq[1])
+		free_irq(ui->gpio_irq[1], NULL);
+
+	dma_pool_destroy(ui->pool);
+	dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma);
+	kfree(ui->func);
+	kfree(ui->strdesc);
+	iounmap(ui->addr);
+	clk_put(ui->clk);
+	clk_put(ui->pclk);
+	clk_put(ui->cclk);
+	msm_hsusb_suspend_locks_init(ui, 0);
+	kfree(ui);
+
+	return ret;
+}
+
+static int usb_vbus_is_on(struct usb_info *ui)
+{
+	unsigned tmp;
+
+	/* disable session valid raising and falling interrupts */
+	ulpi_write(ui, ULPI_SESSION_VALID_RAISE, ULPI_USBINTR_ENABLE_RASING_C);
+	ulpi_write(ui, ULPI_SESSION_VALID_FALL, ULPI_USBINTR_ENABLE_FALLING_C);
+
+	tmp = ulpi_read(ui, ULPI_USBINTR_STATUS);
+
+	/* enable session valid raising and falling interrupts */
+	ulpi_write(ui, ULPI_SESSION_VALID_RAISE, ULPI_USBINTR_ENABLE_RASING_S);
+	ulpi_write(ui, ULPI_SESSION_VALID_FALL, ULPI_USBINTR_ENABLE_FALLING_S);
+
+	if (tmp & (1 << 2))
+		return 1;
+	return 0;
+}
+static void usb_do_work(struct work_struct *w)
+{
+	struct usb_info *ui = container_of(w, struct usb_info, work.work);
+	unsigned long iflags;
+	unsigned long flags, ret;
+
+	for (;;) {
+		spin_lock_irqsave(&ui->lock, iflags);
+		flags = ui->flags;
+		ui->flags = 0;
+		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_REG_OTG) {
+				dcd_ops.handle = (void *) ui;
+				ret = ui->xceiv->set_peripheral(ui->xceiv,
+								&dcd_ops);
+				if (ret)
+					pr_err("%s: Can't register peripheral"
+						"driver with OTG", __func__);
+				break;
+			}
+			if ((flags & USB_FLAG_START) ||
+					(flags & USB_FLAG_RESET)) {
+				disable_irq(ui->irq);
+				if (ui->vbus_sn_notif)
+					msm_pm_app_enable_usb_ldo(1);
+				usb_clk_enable(ui);
+				usb_vreg_enable(ui);
+				usb_vbus_online(ui);
+
+				/* if VBUS is present move to ONLINE state
+				 * otherwise move to OFFLINE state
+				 */
+				if (usb_vbus_is_on(ui)) {
+					ui->usb_state = USB_STATE_POWERED;
+					msm_hsusb_suspend_locks_acquire(ui, 1);
+					ui->state = USB_STATE_ONLINE;
+					usb_enable_pullup(ui);
+					schedule_delayed_work(
+							&ui->chg_legacy_det,
+							USB_CHG_DET_DELAY);
+					pr_info("hsusb: IDLE -> ONLINE\n");
+				} else {
+					ui->usb_state = USB_STATE_NOTATTACHED;
+					ui->state = USB_STATE_OFFLINE;
+
+					msleep(500);
+					usb_lpm_enter(ui);
+					pr_info("hsusb: IDLE -> OFFLINE\n");
+					if (ui->vbus_sn_notif)
+						msm_pm_app_enable_usb_ldo(0);
+				}
+				enable_irq(ui->irq);
+				break;
+			}
+			goto reset;
+
+		case USB_STATE_ONLINE:
+			/* 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) {
+				enum charger_type temp;
+				unsigned long f;
+
+				cancel_delayed_work_sync(&ui->chg_legacy_det);
+
+				spin_lock_irqsave(&ui->lock, f);
+				temp = ui->chg_type;
+				ui->chg_type = USB_CHG_TYPE__INVALID;
+				spin_unlock_irqrestore(&ui->lock, f);
+
+				if (temp != USB_CHG_TYPE__INVALID) {
+					/* re-acquire wakelock and restore axi
+					 * freq if they have been reduced by
+					 * charger work item
+					 */
+					msm_hsusb_suspend_locks_acquire(ui, 1);
+
+					msm_chg_usb_i_is_not_available();
+					msm_chg_usb_charger_disconnected();
+				}
+
+				/* reset usb core and usb phy */
+				disable_irq(ui->irq);
+				if (ui->in_lpm)
+					usb_lpm_exit(ui);
+				usb_vbus_offline(ui);
+				usb_lpm_enter(ui);
+				if ((ui->vbus_sn_notif) &&
+				(ui->usb_state == USB_STATE_NOTATTACHED))
+					msm_pm_app_enable_usb_ldo(0);
+				ui->state = USB_STATE_OFFLINE;
+				enable_irq(ui->irq);
+				switch_set_state(&ui->sdev, 0);
+				pr_info("hsusb: ONLINE -> OFFLINE\n");
+				break;
+			}
+			if (flags & USB_FLAG_SUSPEND) {
+				ui->usb_state = USB_STATE_SUSPENDED;
+				usb_lpm_enter(ui);
+				msm_hsusb_suspend_locks_acquire(ui, 1);
+				break;
+			}
+			if ((flags & USB_FLAG_RESUME) ||
+					(flags & USB_FLAG_CONFIGURE)) {
+				int maxpower = usb_get_max_power(ui);
+
+				if (maxpower > 0)
+					msm_chg_usb_i_is_available(maxpower);
+
+				if (flags & USB_FLAG_CONFIGURE)
+					switch_set_state(&ui->sdev, 1);
+
+				break;
+			}
+			goto reset;
+
+		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)) {
+				msm_hsusb_suspend_locks_acquire(ui, 1);
+				disable_irq(ui->irq);
+				ui->state = USB_STATE_ONLINE;
+				if (ui->in_lpm)
+					usb_lpm_exit(ui);
+				usb_vbus_online(ui);
+				if (!(B_SESSION_VALID & readl(USB_OTGSC))) {
+					writel(((readl(USB_OTGSC) &
+						~OTGSC_INTR_STS_MASK) |
+						OTGSC_BSVIS), USB_OTGSC);
+					enable_irq(ui->irq);
+					goto reset;
+				}
+				usb_enable_pullup(ui);
+				schedule_delayed_work(
+						&ui->chg_legacy_det,
+						USB_CHG_DET_DELAY);
+				pr_info("hsusb: OFFLINE -> ONLINE\n");
+				enable_irq(ui->irq);
+				break;
+			}
+			if (flags & USB_FLAG_SUSPEND) {
+				usb_lpm_enter(ui);
+				wake_unlock(&ui->wlock);
+				break;
+			}
+		default:
+reset:
+			/* For RESET or any unknown flag in a particular state
+			 * go to IDLE state and reset HW to bring to known state
+			 */
+			ui->flags = USB_FLAG_RESET;
+			ui->state = USB_STATE_IDLE;
+		}
+	}
+}
+
+void msm_hsusb_set_vbus_state(int online)
+{
+	struct usb_info *ui = the_usb_info;
+
+	if (ui && online) {
+		msm_pm_app_enable_usb_ldo(1);
+		usb_lpm_exit(ui);
+		/* Turn on PHY comparators */
+		if (!(ulpi_read(ui, 0x30) & 0x01))
+				ulpi_write(ui, 0x01, 0x30);
+	}
+}
+
+static irqreturn_t usb_lpm_gpio_isr(int irq, void *data)
+{
+	disable_irq(irq);
+
+	return IRQ_HANDLED;
+}
+
+static void usb_lpm_exit(struct usb_info *ui)
+{
+	if (ui->in_lpm == 0)
+		return;
+
+	if (usb_lpm_config_gpio)
+		usb_lpm_config_gpio(0);
+
+	wake_lock(&ui->wlock);
+	usb_clk_enable(ui);
+	usb_vreg_enable(ui);
+
+	writel(readl(USB_USBCMD) & ~ASYNC_INTR_CTRL, USB_USBCMD);
+	writel(readl(USB_USBCMD) & ~ULPI_STP_CTRL, USB_USBCMD);
+
+	if (readl(USB_PORTSC) & PORTSC_PHCD) {
+		disable_irq(ui->irq);
+		schedule_work(&ui->li.wakeup_phy);
+	} else {
+		ui->in_lpm = 0;
+		if (ui->xceiv)
+			ui->xceiv->set_suspend(ui->xceiv, 0);
+	}
+	pr_info("%s(): USB exited from low power mode\n", __func__);
+}
+
+static int usb_lpm_enter(struct usb_info *ui)
+{
+	unsigned long flags;
+	unsigned connected;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (ui->in_lpm) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_debug("already in lpm, nothing to do\n");
+		return 0;
+	}
+
+	if (usb_is_online(ui)) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_info("%s: lpm procedure aborted\n", __func__);
+		return -1;
+	}
+
+	ui->in_lpm = 1;
+	if (ui->xceiv)
+		ui->xceiv->set_suspend(ui->xceiv, 1);
+	disable_irq(ui->irq);
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	if (usb_suspend_phy(ui)) {
+		ui->in_lpm = 0;
+		ui->flags = USB_FLAG_RESET;
+		enable_irq(ui->irq);
+		pr_err("%s: phy suspend failed, lpm procedure aborted\n",
+				__func__);
+		return -1;
+	}
+
+	if ((B_SESSION_VALID & readl(USB_OTGSC)) &&
+				(ui->usb_state == USB_STATE_NOTATTACHED)) {
+		ui->in_lpm = 0;
+		writel(((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) |
+						OTGSC_BSVIS), USB_OTGSC);
+		ui->flags = USB_FLAG_VBUS_ONLINE;
+		ui->usb_state = USB_STATE_POWERED;
+		usb_wakeup_phy(ui);
+		enable_irq(ui->irq);
+		return -1;
+	}
+
+	/* enable async interrupt */
+	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL, USB_USBCMD);
+	connected = readl(USB_USBCMD) & USBCMD_RS;
+
+	usb_vreg_disable(ui);
+	usb_clk_disable(ui);
+
+	if (usb_lpm_config_gpio) {
+		if (usb_lpm_config_gpio(1)) {
+			spin_lock_irqsave(&ui->lock, flags);
+			usb_lpm_exit(ui);
+			spin_unlock_irqrestore(&ui->lock, flags);
+			enable_irq(ui->irq);
+			return -1;
+		}
+		enable_irq(ui->gpio_irq[0]);
+		enable_irq(ui->gpio_irq[1]);
+	}
+
+	enable_irq(ui->irq);
+	msm_hsusb_suspend_locks_acquire(ui, 0);
+	pr_info("%s: usb in low power mode\n", __func__);
+	return 0;
+}
+
+static void usb_enable_pullup(struct usb_info *ui)
+{
+	disable_irq(ui->irq);
+	writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
+	writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD);
+	enable_irq(ui->irq);
+}
+
+/* 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
+*/
+static void usb_disable_pullup(struct usb_info *ui)
+{
+	disable_irq(ui->irq);
+	writel(readl(USB_USBINTR) & ~(STS_URI | STS_SLI | STS_UI | STS_PCI),
+			USB_USBINTR);
+	writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD);
+
+	/* S/W workaround, Issue#1 */
+	if (!is_phy_external() && !is_phy_45nm())
+		ulpi_write(ui, 0x48, 0x04);
+
+	enable_irq(ui->irq);
+}
+
+static void usb_chg_stop(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+	enum charger_type temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	temp = ui->chg_type;
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	if (temp == USB_CHG_TYPE__SDP)
+		msm_chg_usb_i_is_not_available();
+}
+
+static void usb_vbus_online(struct usb_info *ui)
+{
+	if (ui->in_lpm) {
+		if (usb_lpm_config_gpio)
+			usb_lpm_config_gpio(0);
+		usb_vreg_enable(ui);
+		usb_clk_enable(ui);
+		usb_wakeup_phy(ui);
+		ui->in_lpm = 0;
+	}
+
+	usb_reset(ui);
+}
+
+static void usb_vbus_offline(struct usb_info *ui)
+{
+	unsigned long timeout;
+	unsigned val = 0;
+
+	if (ui->online != 0) {
+		ui->online = 0;
+		flush_all_endpoints(ui);
+		set_configuration(ui, 0);
+	}
+
+	/* reset h/w at cable disconnetion becasuse
+	 * of h/w bugs and to flush any resource that
+	 * h/w might be holding
+	 */
+	clk_enable(ui->clk);
+
+	if (readl(USB_PORTSC) & PORTSC_PHCD)
+		usb_wakeup_phy(ui);
+
+	if (ui->pdata->phy_reset)
+		ui->pdata->phy_reset(ui->addr);
+	else
+		msm_hsusb_phy_reset();
+	/* Give some delay to settle phy after reset */
+	msleep(100);
+
+	writel(USBCMD_RESET, USB_USBCMD);
+	timeout = jiffies + USB_LINK_RESET_TIMEOUT;
+	while (readl(USB_USBCMD) & USBCMD_RESET) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(&ui->pdev->dev, "usb link reset timeout\n");
+			break;
+		}
+		msleep(1);
+	}
+
+	/* Disable VbusValid and SessionEnd comparators */
+	val = ULPI_VBUS_VALID | ULPI_SESS_END;
+
+	/* enable id interrupt only when transceiver is available */
+	if (ui->xceiv)
+		writel(readl(USB_OTGSC) | OTGSC_BSVIE | OTGSC_IDIE, USB_OTGSC);
+	else {
+		writel((readl(USB_OTGSC) | OTGSC_BSVIE) & ~OTGSC_IDPU,
+							USB_OTGSC);
+		ulpi_write(ui, ULPI_IDPU, ULPI_OTG_CTRL_CLR);
+		val |= ULPI_HOST_DISCONNECT | ULPI_ID_GND;
+	}
+	ulpi_write(ui, val, ULPI_INT_RISE_CLR);
+	ulpi_write(ui, val, ULPI_INT_FALL_CLR);
+
+	clk_disable(ui->clk);
+}
+
+static void usb_lpm_wakeup_phy(struct work_struct *w)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long flags;
+
+	if (usb_wakeup_phy(ui)) {
+		pr_err("fatal error: cannot bring phy out of lpm\n");
+		pr_err("%s: resetting controller\n", __func__);
+
+		spin_lock_irqsave(&ui->lock, flags);
+		usb_disable_pullup(ui);
+		ui->flags = USB_FLAG_RESET;
+		queue_delayed_work(usb_work, &ui->work, 0);
+		enable_irq(ui->irq);
+		spin_unlock_irqrestore(&ui->lock, flags);
+		return;
+	}
+
+	ui->in_lpm = 0;
+	if (ui->xceiv)
+		ui->xceiv->set_suspend(ui->xceiv, 0);
+	enable_irq(ui->irq);
+}
+
+void usb_function_reenumerate(void)
+{
+	struct usb_info *ui = the_usb_info;
+
+	/* disable and re-enable the D+ pullup */
+	pr_info("hsusb: disable pullup\n");
+	usb_disable_pullup(ui);
+
+	msleep(10);
+
+	pr_info("hsusb: enable pullup\n");
+	usb_enable_pullup(ui);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+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 usb_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->max_pkt == 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;
+	queue_delayed_work(usb_work, &ui->work, 0);
+	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 struct dentry *debugfs_dent;
+static struct dentry *debugfs_status;
+static struct dentry *debugfs_reset;
+static struct dentry *debugfs_cycle;
+static void usb_debugfs_init(struct usb_info *ui)
+{
+	debugfs_dent = debugfs_create_dir("usb", 0);
+	if (IS_ERR(debugfs_dent))
+		return;
+
+	debugfs_status = debugfs_create_file("status", 0444,
+				debugfs_dent, ui, &debug_stat_ops);
+	debugfs_reset = debugfs_create_file("reset", 0222,
+				debugfs_dent, ui, &debug_reset_ops);
+	debugfs_cycle = debugfs_create_file("cycle", 0222,
+				debugfs_dent, ui, &debug_cycle_ops);
+}
+
+static void usb_debugfs_uninit(void)
+{
+	debugfs_remove(debugfs_status);
+	debugfs_remove(debugfs_reset);
+	debugfs_remove(debugfs_cycle);
+	debugfs_remove(debugfs_dent);
+}
+
+#else
+static void usb_debugfs_init(struct usb_info *ui) {}
+static void usb_debugfs_uninit(void) {}
+#endif
+
+static void usb_configure_device_descriptor(struct usb_info *ui)
+{
+	desc_device.idVendor = ui->pdata->vendor_id;
+	desc_device.idProduct = ui->composition->product_id;
+	desc_device.bcdDevice = ui->pdata->version;
+
+	if (ui->pdata->serial_number)
+		desc_device.iSerialNumber =
+			usb_msm_get_next_strdesc_id(ui->pdata->serial_number);
+	if (ui->pdata->product_name)
+		desc_device.iProduct =
+			usb_msm_get_next_strdesc_id(ui->pdata->product_name);
+	if (ui->pdata->manufacturer_name)
+		desc_device.iManufacturer =
+			usb_msm_get_next_strdesc_id(
+				ui->pdata->manufacturer_name);
+
+	/* Send Serial number to A9 for software download */
+	if (ui->pdata->serial_number) {
+		msm_hsusb_is_serial_num_null(FALSE);
+		msm_hsusb_send_serial_number(ui->pdata->serial_number);
+	} else
+		msm_hsusb_is_serial_num_null(TRUE);
+
+	msm_hsusb_send_productID(desc_device.idProduct);
+
+}
+static ssize_t msm_hsusb_store_func_enable(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t size)
+{
+	char name[20];
+	int enable = 0;
+	int i;
+
+	for (i = 0; buf[i] != 0; i++) {
+		if (buf[i] == '=')
+			break;
+		name[i] = buf[i];
+	}
+	name[i++] = 0;
+	if (buf[i] == '0' || buf[i] == '1')
+		enable = buf[i] - '0';
+	else
+		return size;
+
+	pr_info("%s: name = %s, enable = %d\n", __func__, name, enable);
+	usb_function_enable(name, enable);
+	return size;
+}
+static ssize_t msm_hsusb_show_compswitch(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+
+	if (ui->composition)
+		i = scnprintf(buf, PAGE_SIZE,
+				"composition product id = %x\n",
+					ui->composition->product_id);
+	else
+		i = scnprintf(buf, PAGE_SIZE,
+				"composition product id = 0\n");
+	return i;
+}
+
+static ssize_t msm_hsusb_store_compswitch(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t size)
+{
+	unsigned long pid;
+
+	if (!strict_strtoul(buf, 16, &pid)) {
+		pr_info("%s: Requested New Product id = %lx\n", __func__, pid);
+		usb_switch_composition((unsigned short)pid);
+	} else
+		pr_info("%s: strict_strtoul conversion failed\n", __func__);
+
+	return size;
+}
+static ssize_t msm_hsusb_store_autoresume(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t size)
+{
+	usb_remote_wakeup();
+
+	return size;
+}
+
+static ssize_t msm_hsusb_show_state(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	int 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[ui->usb_state]);
+	return i;
+}
+
+static ssize_t msm_hsusb_show_lpm(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+
+	i = scnprintf(buf, PAGE_SIZE, "%d\n", ui->in_lpm);
+	return i;
+}
+
+static ssize_t msm_hsusb_show_speed(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+	char *speed[] = {"USB_SPEED_UNKNOWN", "USB_SPEED_LOW",
+			"USB_SPEED_FULL", "USB_SPEED_HIGH"};
+
+	i = scnprintf(buf, PAGE_SIZE, "%s\n", speed[ui->speed]);
+	return i;
+}
+
+static DEVICE_ATTR(composition, 0664,
+		msm_hsusb_show_compswitch, msm_hsusb_store_compswitch);
+static DEVICE_ATTR(func_enable, S_IWUSR,
+		NULL, msm_hsusb_store_func_enable);
+static DEVICE_ATTR(autoresume, 0222,
+		NULL, msm_hsusb_store_autoresume);
+static DEVICE_ATTR(state, 0664, msm_hsusb_show_state, NULL);
+static DEVICE_ATTR(lpm, 0664, msm_hsusb_show_lpm, NULL);
+static DEVICE_ATTR(speed, 0664, msm_hsusb_show_speed, NULL);
+
+static struct attribute *msm_hsusb_attrs[] = {
+	&dev_attr_composition.attr,
+	&dev_attr_func_enable.attr,
+	&dev_attr_autoresume.attr,
+	&dev_attr_state.attr,
+	&dev_attr_lpm.attr,
+	&dev_attr_speed.attr,
+	NULL,
+};
+static struct attribute_group msm_hsusb_attr_grp = {
+	.attrs = msm_hsusb_attrs,
+};
+
+#define msm_hsusb_func_attr(function, index)				\
+static ssize_t  show_##function(struct device *dev,			\
+		struct device_attribute *attr, char *buf)		\
+{									\
+	struct usb_info *ui = the_usb_info;				\
+	struct usb_function_info *fi = ui->func[index];			\
+									\
+	return sprintf(buf, "%d", fi->enabled);				\
+									\
+}									\
+									\
+static DEVICE_ATTR(function, S_IRUGO, show_##function, NULL);
+
+msm_hsusb_func_attr(diag, 0);
+msm_hsusb_func_attr(adb, 1);
+msm_hsusb_func_attr(modem, 2);
+msm_hsusb_func_attr(nmea, 3);
+msm_hsusb_func_attr(mass_storage, 4);
+msm_hsusb_func_attr(ethernet, 5);
+msm_hsusb_func_attr(rmnet, 6);
+
+static struct attribute *msm_hsusb_func_attrs[] = {
+	&dev_attr_diag.attr,
+	&dev_attr_adb.attr,
+	&dev_attr_modem.attr,
+	&dev_attr_nmea.attr,
+	&dev_attr_mass_storage.attr,
+	&dev_attr_ethernet.attr,
+	&dev_attr_rmnet.attr,
+	NULL,
+};
+
+static struct attribute_group msm_hsusb_func_attr_grp = {
+	.name  = "functions",
+	.attrs = msm_hsusb_func_attrs,
+};
+
+static int __init usb_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct usb_info *ui;
+	int irq;
+	int ulpi_irq1 = 0;
+	int ulpi_irq2 = 0;
+	int i;
+	int ret = 0;
+
+	if (!pdev || !pdev->dev.platform_data) {
+		pr_err("%s:pdev or platform data is null\n", __func__);
+		return -ENODEV;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		pr_err("%s: failed to get irq num from platform_get_irq\n",
+				__func__);
+		return -ENODEV;
+	}
+
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_err("%s: failed to get mem resource\n", __func__);
+		return -ENODEV;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &msm_hsusb_attr_grp);
+	if (ret) {
+		pr_err("%s: unable to create sysfs group\n", __func__);
+		return ret;
+	}
+
+	usb_work = create_singlethread_workqueue("usb_work");
+	if (!usb_work) {
+		pr_err("%s: unable to create work queue\n", __func__);
+		ret = -ENOMEM;
+		goto free_sysfs_grp;
+	}
+
+	ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL);
+	if (!ui) {
+		pr_err("%s: unable to allocate memory for ui\n", __func__);
+		ret = -ENOMEM;
+		goto free_workqueue;
+	}
+
+	ui->pdev = pdev;
+	ui->pdata = pdev->dev.platform_data;
+
+	for (i = 0; i < ui->pdata->num_compositions; i++)
+		if (ui->pdata->compositions[i].product_id == pid) {
+			ui->composition = &ui->pdata->compositions[i];
+			break;
+		}
+	if (!ui->composition) {
+		pr_err("%s: unable to find the composition with pid:(%d)\n",
+				__func__, pid);
+		ret = -ENODEV;
+		goto free_ui;
+	}
+
+	ui->phy_info = ui->pdata->phy_info;
+	if (ui->phy_info == USB_PHY_UNDEFINED) {
+		pr_err("undefined phy_info: (%d)\n", ui->phy_info);
+		ret = -ENOMEM;
+		goto free_ui;
+	}
+
+	/* zero is reserved for language id */
+	ui->strdesc_index = 1;
+	ui->strdesc = kzalloc(sizeof(char *) * MAX_STRDESC_NUM, GFP_KERNEL);
+	if (!ui->strdesc) {
+		pr_err("%s: unable allocate mem for string descriptors\n",
+				__func__);
+		ret = -ENOMEM;
+		goto free_ui;
+	}
+
+	ui->num_funcs = ui->pdata->num_functions;
+	ui->func = kzalloc(sizeof(struct usb_function *) * ui->num_funcs,
+				GFP_KERNEL);
+	if (!ui->func) {
+		pr_err("%s: unable allocate mem for functions\n", __func__);
+		ret = -ENOMEM;
+		goto free_str_desc;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &msm_hsusb_func_attr_grp);
+	if (ret) {
+		pr_err("%s: unable to create functions sysfs group\n",
+				__func__);
+		goto free_func;
+	}
+
+	ui->addr = ioremap(res->start, resource_size(res));
+	if (!ui->addr) {
+		pr_err("%s: unable ioremap\n", __func__);
+		ret = -ENOMEM;
+		goto free_func_sysfs_grp;
+	}
+
+	ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL);
+	if (!ui->buf) {
+		pr_err("%s: failed allocate dma coherent memory\n", __func__);
+		ret = -ENOMEM;
+		goto free_iounmap;
+	}
+
+	ui->pool = dma_pool_create("hsusb", NULL, 32, 32, 0);
+	if (!ui->pool) {
+		pr_err("%s: unable to allocate dma pool\n", __func__);
+		ret = -ENOMEM;
+		goto free_dma_coherent;
+	}
+
+	ui->clk = clk_get(&pdev->dev, "usb_hs_clk");
+	if (IS_ERR(ui->clk)) {
+		pr_err("%s: unable get usb_hs_clk\n", __func__);
+		ret = PTR_ERR(ui->clk);
+		goto free_dma_pool;
+	}
+
+	ui->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
+	if (IS_ERR(ui->pclk)) {
+		pr_err("%s: unable get usb_hs_pclk\n", __func__);
+		ret = PTR_ERR(ui->pclk);
+		goto free_hs_clk;
+	}
+
+	if (ui->pdata->core_clk) {
+		ui->cclk = clk_get(&pdev->dev, "usb_hs_core_clk");
+		if (IS_ERR(ui->cclk)) {
+			pr_err("%s: unable get usb_hs_core_clk\n", __func__);
+			ret = PTR_ERR(ui->cclk);
+			goto free_hs_pclk;
+		}
+	}
+
+	if (ui->pdata->vreg5v_required) {
+		ui->vreg = vreg_get(NULL, "boost");
+		if (IS_ERR(ui->vreg)) {
+			pr_err("%s: vreg get failed\n", __func__);
+			ui->vreg = NULL;
+			ret = PTR_ERR(ui->vreg);
+			goto free_hs_cclk;
+		}
+	}
+
+	/* disable interrupts before requesting irq */
+	usb_clk_enable(ui);
+	writel(0, USB_USBINTR);
+	writel(readl(USB_OTGSC) & ~OTGSC_INTR_MASK, USB_OTGSC);
+	usb_clk_disable(ui);
+
+	ret = request_irq(irq, usb_interrupt, IRQF_SHARED, pdev->name, ui);
+	if (ret) {
+		pr_err("%s: request_irq failed\n", __func__);
+		goto free_vreg5v;
+	}
+	ui->irq = irq;
+
+	if (ui->pdata->config_gpio) {
+		usb_lpm_config_gpio = ui->pdata->config_gpio;
+
+		ulpi_irq1 = platform_get_irq_byname(pdev, "vbus_interrupt");
+		if (ulpi_irq1 < 0) {
+			pr_err("%s: failed to get vbus gpio interrupt\n",
+					__func__);
+			return -ENODEV;
+		}
+
+		ulpi_irq2 = platform_get_irq_byname(pdev, "id_interrupt");
+		if (ulpi_irq2 < 0) {
+			pr_err("%s: failed to get id gpio interrupt\n",
+					__func__);
+			return -ENODEV;
+		}
+
+		ret = request_irq(ulpi_irq1,
+				&usb_lpm_gpio_isr,
+				IRQF_TRIGGER_HIGH,
+				"vbus_interrupt", NULL);
+		if (ret) {
+			pr_err("%s: failed to request vbus interrupt:(%d)\n",
+					__func__, ulpi_irq1);
+			goto free_irq;
+		}
+
+		ret = request_irq(ulpi_irq2,
+				&usb_lpm_gpio_isr,
+				IRQF_TRIGGER_RISING,
+				"usb_ulpi_data3", NULL);
+		if (ret) {
+			pr_err("%s: failed to request irq ulpi_data_3:(%d)\n",
+							__func__, ulpi_irq2);
+			goto free_ulpi_irq1;
+		}
+
+		ui->gpio_irq[0] = ulpi_irq1;
+		ui->gpio_irq[1] = ulpi_irq2;
+	}
+
+	ui->sdev.name = DRIVER_NAME;
+	ui->sdev.print_name = print_switch_name;
+	ui->sdev.print_state = print_switch_state;
+
+	ret = switch_dev_register(&ui->sdev);
+	if (ret < 0) {
+		pr_err("%s(): switch_dev_register failed ret = %d\n",
+				__func__, ret);
+		goto free_ulpi_irq2;
+	}
+
+	the_usb_info = ui;
+	ui->functions_map = ui->pdata->function_map;
+	ui->selfpowered = 0;
+	ui->remote_wakeup = 0;
+	ui->maxpower = 0xFA;
+	ui->chg_type = USB_CHG_TYPE__INVALID;
+	/* to allow swfi latency, driver latency
+	 * must be above listed swfi latency
+	 */
+	ui->pdata->swfi_latency += 1;
+
+	spin_lock_init(&ui->lock);
+	msm_hsusb_suspend_locks_init(ui, 1);
+	enable_irq_wake(irq);
+
+	/* memory barrier initialization in non-interrupt context */
+	dmb();
+
+	usb_debugfs_init(ui);
+	usb_prepare(ui);
+
+	pr_info("%s: io=%p, irq=%d, dma=%p(%x)\n",
+			__func__, ui->addr, ui->irq, ui->buf, ui->dma);
+	return 0;
+
+free_ulpi_irq2:
+	free_irq(ulpi_irq2, NULL);
+free_ulpi_irq1:
+	free_irq(ulpi_irq1, NULL);
+free_irq:
+	free_irq(ui->irq, ui);
+free_vreg5v:
+	if (ui->pdata->vreg5v_required)
+		vreg_put(ui->vreg);
+free_hs_cclk:
+	clk_put(ui->cclk);
+free_hs_pclk:
+	clk_put(ui->pclk);
+free_hs_clk:
+	clk_put(ui->clk);
+free_dma_pool:
+	dma_pool_destroy(ui->pool);
+free_dma_coherent:
+	dma_free_coherent(&pdev->dev, 4096, ui->buf, ui->dma);
+free_iounmap:
+	iounmap(ui->addr);
+free_func_sysfs_grp:
+	sysfs_remove_group(&pdev->dev.kobj, &msm_hsusb_func_attr_grp);
+free_func:
+	kfree(ui->func);
+free_str_desc:
+	kfree(ui->strdesc);
+free_ui:
+	kfree(ui);
+free_workqueue:
+	destroy_workqueue(usb_work);
+free_sysfs_grp:
+	sysfs_remove_group(&pdev->dev.kobj, &msm_hsusb_attr_grp);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int usb_platform_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ui->lock, flags);
+
+	if (!ui->active) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_info("%s: peripheral mode is not active"
+				"nothing to be done\n", __func__);
+		return 0;
+	}
+
+	if (ui->in_lpm) {
+		spin_unlock_irqrestore(&ui->lock, flags);
+		pr_info("%s: we are already in lpm, nothing to be done\n",
+					__func__);
+		return 0;
+	}
+	spin_unlock_irqrestore(&ui->lock, flags);
+
+	ret = usb_lpm_enter(ui);
+	if (ret)
+		pr_err("%s: failed to enter lpm\n", __func__);
+
+	return ret;
+}
+#endif
+
+static struct platform_driver usb_driver = {
+	.probe = usb_probe,
+#ifdef CONFIG_PM
+	.suspend = usb_platform_suspend,
+#endif
+	.driver = { .name = DRIVER_NAME, },
+};
+
+static int __init usb_module_init(void)
+{
+	/* rpc connect for phy_reset */
+	msm_hsusb_rpc_connect();
+	/* rpc connect for charging */
+	msm_chg_rpc_connect();
+
+	return platform_driver_register(&usb_driver);
+}
+
+static void free_usb_info(void)
+{
+	struct usb_info *ui = the_usb_info;
+	unsigned long flags;
+	int i;
+	if (ui) {
+		INIT_LIST_HEAD(&usb_function_list);
+
+		for (i = 0; i < ui->num_funcs; i++)
+			kfree(ui->func[i]);
+		ui->num_funcs = 0;
+		usb_uninit(ui);
+		kfree(ui->strdesc);
+		usb_ept_free_req(&ui->ep0in, ui->setup_req);
+		if (ui->ept[0].ui == ui)
+			flush_all_endpoints(ui);
+		spin_lock_irqsave(&ui->lock, flags);
+		usb_clk_disable(ui);
+		usb_vreg_disable(ui);
+		spin_unlock_irqrestore(&ui->lock, flags);
+		usb_free(ui, 0);
+		the_usb_info = NULL;
+	}
+}
+static void usb_exit(void)
+{
+	struct usb_info *ui = the_usb_info;
+	/* free the dev state structure */
+	if (!ui)
+		return;
+
+	if (ui->xceiv) {
+		ui->xceiv->set_peripheral(ui->xceiv, NULL);
+		msm_otg_put_transceiver(ui->xceiv);
+	}
+
+	cancel_work_sync(&ui->li.wakeup_phy);
+
+	destroy_workqueue(usb_work);
+	/* free the usb_info structure */
+	free_usb_info();
+	switch_dev_unregister(&ui->sdev);
+	sysfs_remove_group(&ui->pdev->dev.kobj, &msm_hsusb_func_attr_grp);
+	sysfs_remove_group(&ui->pdev->dev.kobj, &msm_hsusb_attr_grp);
+	usb_debugfs_uninit();
+	platform_driver_unregister(&usb_driver);
+	msm_hsusb_rpc_close();
+	msm_chg_rpc_close();
+	msm_pm_app_unregister_vbus_sn(&msm_hsusb_set_vbus_state);
+	msm_pm_app_rpc_deinit();
+}
+
+static void __exit usb_module_exit(void)
+{
+	usb_exit();
+}
+
+module_param(pid, int, 0);
+MODULE_PARM_DESC(pid, "Product ID of the desired composition");
+
+module_init(usb_module_init);
+module_exit(usb_module_exit);
+
+static void copy_string_descriptor(char *string, char *buffer)
+{
+	int length, i;
+
+	if (string) {
+		length = strlen(string);
+		buffer[0] = 2 * length + 2;
+		buffer[1] = USB_DT_STRING;
+		for (i = 0; i < length; i++) {
+			buffer[2 * i + 2] = string[i];
+			buffer[2 * i + 3] = 0;
+		}
+	}
+}
+static int get_qualifier_descriptor(struct usb_qualifier_descriptor *dq)
+{
+	struct usb_qualifier_descriptor *dev_qualifier = dq;
+	dev_qualifier->bLength = sizeof(struct usb_qualifier_descriptor),
+	dev_qualifier->bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+	dev_qualifier->bcdUSB =  __constant_cpu_to_le16(0x0200),
+	dev_qualifier->bDeviceClass = USB_CLASS_PER_INTERFACE,
+	dev_qualifier->bDeviceSubClass = 0;
+	dev_qualifier->bDeviceProtocol = 0;
+	dev_qualifier->bMaxPacketSize0 = 64;
+	dev_qualifier->bNumConfigurations = 1;
+	dev_qualifier->bRESERVED = 0;
+	return sizeof(struct usb_qualifier_descriptor);
+}
+
+static int usb_fill_descriptors(void *ptr,
+		struct usb_descriptor_header **descriptors)
+{
+	unsigned char *buf = ptr;
+	struct usb_descriptor_header *item = descriptors[0];
+	unsigned cnt = 0;
+
+	while (NULL != item) {
+		unsigned len = item->bLength;
+		memcpy(buf, item, len);
+		buf += len;
+		cnt++;
+		item = descriptors[cnt];
+	}
+
+	return buf-(u8 *)ptr;
+}
+
+static int usb_find_descriptor(struct usb_info *ui, struct usb_ctrlrequest *ctl,
+				struct usb_request *req)
+{
+	int i;
+	unsigned short id = ctl->wValue;
+	unsigned short type = id >> 8;
+	id &= 0xff;
+
+	if ((type == USB_DT_DEVICE) && (id == 0)) {
+		req->length = sizeof(desc_device);
+		if (usb_msm_is_iad()) {
+			desc_device.bDeviceClass = 0xEF;
+			desc_device.bDeviceSubClass = 0x02;
+			desc_device.bDeviceProtocol = 0x01;
+		}
+		memcpy(req->buf, &desc_device, req->length);
+		return 0;
+	}
+	if ((type == USB_DT_DEVICE_QUALIFIER) && (id == 0)) {
+		struct usb_qualifier_descriptor dq;
+		req->length = get_qualifier_descriptor(&dq);
+		if (usb_msm_is_iad()) {
+			dq.bDeviceClass = 0xEF;
+			dq.bDeviceSubClass = 0x02;
+			dq.bDeviceProtocol = 0x01;
+		}
+		memcpy(req->buf, &dq, req->length);
+		return 0;
+	}
+
+	if ((type == USB_DT_OTHER_SPEED_CONFIG) && (id == 0))
+		goto get_config;
+
+	if ((type == USB_DT_CONFIG) && (id == 0)) {
+		struct usb_config_descriptor cfg;
+		unsigned ifc_count = 0;
+		char *ptr, *start;
+get_config:
+		ifc_count = 0;
+		start = req->buf;
+		ptr = start + USB_DT_CONFIG_SIZE;
+		ifc_count = ui->next_ifc_num;
+
+		for (i = 0; i < ui->num_funcs; i++) {
+			struct usb_function_info *fi = ui->func[i];
+			struct usb_descriptor_header **dh = NULL;
+
+			if (!fi || !(ui->composition->functions & (1 << i)))
+				continue;
+			switch (ui->speed) {
+			case USB_SPEED_HIGH:
+				if (type == USB_DT_OTHER_SPEED_CONFIG)
+					dh = fi->func->fs_descriptors;
+				else
+					dh = fi->func->hs_descriptors;
+				break;
+
+			case USB_SPEED_FULL:
+				if (type == USB_DT_OTHER_SPEED_CONFIG)
+					dh = fi->func->hs_descriptors;
+				else
+					dh = fi->func->fs_descriptors;
+				break;
+
+			default:
+				printk(KERN_ERR "Unsupported speed(%x)\n",
+						ui->speed);
+				return -1;
+			}
+			ptr += usb_fill_descriptors(ptr, dh);
+		}
+
+#define	USB_REMOTE_WAKEUP_SUPPORT	1
+		cfg.bLength = USB_DT_CONFIG_SIZE;
+		if (type == USB_DT_OTHER_SPEED_CONFIG)
+			cfg.bDescriptorType =  USB_DT_OTHER_SPEED_CONFIG;
+		else
+			cfg.bDescriptorType = USB_DT_CONFIG;
+		cfg.wTotalLength = ptr - start;
+		cfg.bNumInterfaces = ifc_count;
+		cfg.bConfigurationValue = 1;
+		cfg.iConfiguration = 0;
+		cfg.bmAttributes = USB_CONFIG_ATT_ONE |
+			ui->selfpowered << USB_CONFIG_ATT_SELFPOWER_POS |
+			USB_REMOTE_WAKEUP_SUPPORT << USB_CONFIG_ATT_WAKEUP_POS;
+		cfg.bMaxPower = ui->maxpower;
+
+		memcpy(start, &cfg, USB_DT_CONFIG_SIZE);
+
+		req->length = ptr - start;
+		return 0;
+	}
+
+	if (type == USB_DT_STRING) {
+		char *buffer = req->buf;
+
+		buffer[0] = 0;
+		if (id > ui->strdesc_index)
+			return -1;
+		 if (id == STRING_LANGUAGE_ID)
+			memcpy(buffer, str_lang_desc, str_lang_desc[0]);
+		 else
+			copy_string_descriptor(ui->strdesc[id], buffer);
+
+		if (buffer[0]) {
+			req->length = buffer[0];
+			return 0;
+		} else
+			return -1;
+	}
+	return -1;
+}
+
+/*****Gadget Framework Functions***/
+struct device *usb_get_device(void)
+{
+	if (the_usb_info) {
+		if (the_usb_info->pdev)
+			return &(the_usb_info->pdev->dev);
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(usb_get_device);
+
+int usb_ept_cancel_xfer(struct usb_endpoint *ept, struct usb_request *_req)
+{
+	struct usb_info 	*ui = the_usb_info;
+	struct msm_request      *req = to_msm_request(_req);
+	struct msm_request 	*temp_req, *prev_req;
+	unsigned long		flags;
+
+	if (!(ui && req && ept->req))
+		return -EINVAL;
+
+	spin_lock_irqsave(&ui->lock, flags);
+	if (req->busy) {
+		req->req.status = 0;
+		req->busy = 0;
+
+		/* See if the request is the first request in the ept queue */
+		if (ept->req == req) {
+			/* Stop the transfer */
+			do {
+				writel((1 << ept->bit), USB_ENDPTFLUSH);
+				while (readl(USB_ENDPTFLUSH) & (1 << ept->bit))
+					udelay(100);
+			} while (readl(USB_ENDPTSTAT) & (1 << ept->bit));
+			if (!req->next)
+				ept->last = NULL;
+			ept->req = req->next;
+			ept->head->next = req->item->next;
+			goto cancel_req;
+		}
+		/* Request could be in the middle of ept queue */
+		prev_req = temp_req = ept->req;
+		do {
+			if (req == temp_req) {
+				if (req->live) {
+					/* Stop the transfer */
+					do {
+						writel((1 << ept->bit),
+							USB_ENDPTFLUSH);
+						while (readl(USB_ENDPTFLUSH) &
+							(1 << ept->bit))
+							udelay(100);
+					} while (readl(USB_ENDPTSTAT) &
+						(1 << ept->bit));
+				}
+				prev_req->next = temp_req->next;
+				prev_req->item->next = temp_req->item->next;
+				if (!req->next)
+					ept->last = prev_req;
+				goto cancel_req;
+			}
+			prev_req = temp_req;
+			temp_req = temp_req->next;
+		} while (temp_req != NULL);
+		goto error;
+cancel_req:
+	if (req->live) {
+		/* prepare the transaction descriptor item for the hardware */
+		req->item->next = TERMINATE;
+		req->item->info = 0;
+		req->live = 0;
+		dma_unmap_single(NULL, req->dma, req->req.length,
+				(ept->flags & EPT_FLAG_IN) ?
+				DMA_TO_DEVICE : DMA_FROM_DEVICE);
+		/* Reprime the endpoint for the remaining transfers */
+		if (ept->req) {
+			temp_req = ept->req;
+			while (temp_req != NULL) {
+				temp_req->live = 0;
+				temp_req = temp_req->next;
+			}
+			usb_ept_start(ept);
+		}
+	} else
+		dma_unmap_single(NULL, req->dma, req->req.length,
+				(ept->flags & EPT_FLAG_IN) ?
+				DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return 0;
+	}
+error:
+	spin_unlock_irqrestore(&ui->lock, flags);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(usb_ept_cancel_xfer);
+
+int usb_ept_set_halt(struct usb_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	int in = ept->flags & EPT_FLAG_IN;
+	unsigned n;
+
+	if (ui->in_lpm) {
+		pr_err("%s: controller is in lpm, cannot proceed\n", __func__);
+		return -1;
+	}
+
+	ept->ept_halted = 1;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in)
+		n |= CTRL_TXS;
+	else
+		n |= CTRL_RXS;
+
+	writel(n, USB_ENDPTCTRL(ept->num));
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_ept_set_halt);
+
+int usb_ept_clear_halt(struct usb_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	int in = ept->flags & EPT_FLAG_IN;
+	unsigned n;
+
+	if (ui->in_lpm) {
+		pr_err("%s: controller is in lpm, cannot proceed\n", __func__);
+		return -1;
+	}
+
+	if (ept->ept_halted)
+		ept->ept_halted = 0;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	/*clear stall bit and set data toggle bit*/
+	if (in) {
+		n &= (~CTRL_TXS);
+		n |= (CTRL_TXR);
+	} else {
+		n &= ~(CTRL_RXS);
+		n |= (CTRL_RXR);
+	}
+
+	writel(n, USB_ENDPTCTRL(ept->num));
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_ept_clear_halt);
+
+int usb_ept_is_stalled(struct usb_endpoint *ept)
+{
+	struct usb_info *ui = ept->ui;
+	int in = ept->flags & EPT_FLAG_IN;
+	unsigned n;
+
+	n = readl(USB_ENDPTCTRL(ept->num));
+
+	if (in && (n & CTRL_TXS))
+		return 1;
+	else if (n & CTRL_RXS)
+		return 1;
+	return 0;
+}
+
+void usb_ept_fifo_flush(struct usb_endpoint *ept)
+{
+	flush_endpoint(ept);
+}
+EXPORT_SYMBOL(usb_ept_fifo_flush);
+
+struct usb_function *usb_ept_get_function(struct usb_endpoint *ept)
+{
+	return NULL;
+}
+EXPORT_SYMBOL(usb_ept_get_function);
+
+
+void usb_free_endpoint_all_req(struct usb_endpoint *ep)
+{
+	struct msm_request *temp;
+	struct msm_request *req;
+	if (!ep)
+		return;
+	req = ep->req;
+	while (req) {
+		temp = req->next;
+		req->busy = 0;
+		if (&req->req)
+			usb_ept_free_req(ep, &req->req);
+		req = temp;
+	}
+}
+EXPORT_SYMBOL(usb_free_endpoint_all_req);
+
+int usb_function_unregister(struct usb_function *func)
+{
+	struct usb_info *ui = the_usb_info;
+	int i;
+	struct usb_function_info *fi;
+	unsigned long flags;
+
+	if (!func)
+		return -EINVAL;
+
+	fi = usb_find_function(func->name);
+	if (!fi)
+		return -EINVAL;
+
+	if (ui->running) {
+		disable_irq(ui->irq);
+		spin_lock_irqsave(&ui->lock, flags);
+		ui->running = 0;
+		ui->online = 0;
+		ui->bound = 0;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		usb_uninit(ui);
+		/* we should come out of lpm to access registers */
+		if (ui->in_lpm) {
+			if (PHY_TYPE(ui->phy_info) == USB_PHY_EXTERNAL) {
+				disable_irq(ui->gpio_irq[0]);
+				disable_irq(ui->gpio_irq[1]);
+			}
+			usb_lpm_exit(ui);
+			if (cancel_work_sync(&ui->li.wakeup_phy))
+				usb_lpm_wakeup_phy(NULL);
+			ui->in_lpm = 0;
+		}
+		/* disable usb and session valid interrupts */
+		writel(0, USB_USBINTR);
+		writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
+
+		/* stop the controller */
+		usb_disable_pullup(ui);
+		msleep(100);
+		enable_irq(ui->irq);
+	}
+
+	pr_info("%s: func->name = %s\n", __func__, func->name);
+
+	ui->composition = NULL;
+
+	if (func->configure)
+		func->configure(0, func->context);
+	if (func->unbind)
+		func->unbind(func->context);
+
+	list_del(&fi->list);
+	for (i = 0; i < ui->num_funcs; i++)
+		if (fi == ui->func[i])
+			ui->func[i] = NULL;
+	kfree(fi);
+	return 0;
+}
+EXPORT_SYMBOL(usb_function_unregister);
+
+MODULE_LICENSE("GPL");
+