zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/Kconfig b/ap/os/linux/linux-3.4.x/drivers/usb/otg/Kconfig
new file mode 100644
index 0000000..c2902a8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/Kconfig
@@ -0,0 +1,139 @@
+#
+# USB OTG infrastructure may be needed for peripheral-only, host-only,
+# or OTG-capable configurations when OTG transceivers or controllers
+# are used.
+#
+
+comment "OTG and related infrastructure"
+
+config USB_OTG_UTILS
+	bool
+	help
+	  Select this to make sure the build includes objects from
+	  the OTG infrastructure directory.
+
+config USB_OTG_WAKELOCK
+	bool "Hold a wakelock when USB connected"
+	depends on WAKELOCK
+	select USB_OTG_UTILS
+	help
+	  Select this to automatically hold a wakelock when USB is
+	  connected, preventing suspend.
+
+if USB || USB_GADGET
+
+#
+# USB Transceiver Drivers
+#
+config USB_GPIO_VBUS
+	tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
+	depends on GENERIC_GPIO
+	select USB_OTG_UTILS
+	help
+	  Provides simple GPIO VBUS sensing for controllers with an
+	  internal transceiver via the usb_phy interface, and
+	  optionally control of a D+ pullup GPIO as well as a VBUS
+	  current limit regulator.
+
+config ISP1301_OMAP
+	tristate "Philips ISP1301 with OMAP OTG"
+	depends on I2C && ARCH_OMAP_OTG
+	select USB_OTG_UTILS
+	help
+	  If you say yes here you get support for the Philips ISP1301
+	  USB-On-The-Go transceiver working with the OMAP OTG controller.
+	  The ISP1301 is a full speed USB  transceiver which is used in
+	  products including H2, H3, and H4 development boards for Texas
+	  Instruments OMAP processors.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called isp1301_omap.
+
+config USB_ULPI
+	bool "Generic ULPI Transceiver Driver"
+	depends on ARM
+	select USB_OTG_UTILS
+	help
+	  Enable this to support ULPI connected USB OTG transceivers which
+	  are likely found on embedded boards.
+
+config USB_ULPI_VIEWPORT
+	bool
+	depends on USB_ULPI
+	help
+	  Provides read/write operations to the ULPI phy register set for
+	  controllers with a viewport register (e.g. Chipidea/ARC controllers).
+
+config TWL4030_USB
+	tristate "TWL4030 USB Transceiver Driver"
+	depends on TWL4030_CORE && REGULATOR_TWL4030
+	select USB_OTG_UTILS
+	help
+	  Enable this to support the USB OTG transceiver on TWL4030
+	  family chips (including the TWL5030 and TPS659x0 devices).
+	  This transceiver supports high and full speed devices plus,
+	  in host mode, low speed.
+
+config TWL6030_USB
+	tristate "TWL6030 USB Transceiver Driver"
+	depends on TWL4030_CORE
+	select USB_OTG_UTILS
+	help
+	  Enable this to support the USB OTG transceiver on TWL6030
+	  family chips. This TWL6030 transceiver has the VBUS and ID GND
+	  and OTG SRP events capabilities. For all other transceiver functionality
+	  UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs
+	  are hooked to this driver through platform_data structure.
+	  The definition of internal PHY APIs are in the mach-omap2 layer.
+
+config NOP_USB_XCEIV
+	tristate "NOP USB Transceiver Driver"
+	select USB_OTG_UTILS
+	help
+	  This driver is to be used by all the usb transceiver which are either
+	  built-in with usb ip or which are autonomous and doesn't require any
+	  phy programming such as ISP1x04 etc.
+
+config USB_MSM_OTG
+	tristate "OTG support for Qualcomm on-chip USB controller"
+	depends on (USB || USB_GADGET) && ARCH_MSM
+	select USB_OTG_UTILS
+	help
+	  Enable this to support the USB OTG transceiver on MSM chips. It
+	  handles PHY initialization, clock management, and workarounds
+	  required after resetting the hardware and power management.
+	  This driver is required even for peripheral only or host only
+	  mode configurations.
+	  This driver is not supported on boards like trout which
+	  has an external PHY.
+
+config AB8500_USB
+	tristate "AB8500 USB Transceiver Driver"
+	depends on AB8500_CORE
+	select USB_OTG_UTILS
+	help
+	  Enable this to support the USB OTG transceiver in AB8500 chip.
+	  This transceiver supports high and full speed devices plus,
+	  in host mode, low speed.
+
+config FSL_USB2_OTG
+	bool "Freescale USB OTG Transceiver Driver"
+	depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND
+	select USB_OTG
+	select USB_OTG_UTILS
+	help
+	  Enable this to support Freescale USB OTG transceiver.
+
+config USB_MV_OTG
+	tristate "Marvell USB OTG support"
+	depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND
+	select USB_OTG
+	select USB_OTG_UTILS
+	help
+	  Say Y here if you want to build Marvell USB OTG transciever
+	  driver in kernel (including PXA and MMP series). This driver
+	  implements role switch between EHCI host driver and gadget driver.
+
+	  To compile this driver as a module, choose M here.
+
+endif # USB || OTG
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/Makefile b/ap/os/linux/linux-3.4.x/drivers/usb/otg/Makefile
new file mode 100644
index 0000000..638d040
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/Makefile
@@ -0,0 +1,24 @@
+#
+# OTG infrastructure and transceiver drivers
+#
+
+ccflags-$(CONFIG_USB_DEBUG)		:= -DDEBUG
+ccflags-$(CONFIG_USB_GADGET_DEBUG)	+= -DDEBUG
+
+# infrastructure
+obj-$(CONFIG_USB_OTG_UTILS)	+= otg.o
+obj-$(CONFIG_USB_OTG_WAKELOCK)	+= otg-wakelock.o
+
+# transceiver drivers
+obj-$(CONFIG_USB_GPIO_VBUS)	+= gpio_vbus.o
+obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
+obj-$(CONFIG_TWL4030_USB)	+= twl4030-usb.o
+obj-$(CONFIG_TWL6030_USB)	+= twl6030-usb.o
+obj-$(CONFIG_NOP_USB_XCEIV)	+= nop-usb-xceiv.o
+obj-$(CONFIG_USB_ULPI)		+= ulpi.o
+obj-$(CONFIG_USB_ULPI_VIEWPORT)	+= ulpi_viewport.o
+obj-$(CONFIG_USB_MSM_OTG)	+= msm_otg.o
+obj-$(CONFIG_AB8500_USB)	+= ab8500-usb.o
+fsl_usb2_otg-objs		:= fsl_otg.o otg_fsm.o
+obj-$(CONFIG_FSL_USB2_OTG)	+= fsl_usb2_otg.o
+obj-$(CONFIG_USB_MV_OTG)	+= mv_otg.o
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/ab8500-usb.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/ab8500-usb.c
new file mode 100644
index 0000000..a84af67
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/ab8500-usb.c
@@ -0,0 +1,596 @@
+/*
+ * drivers/usb/otg/ab8500_usb.c
+ *
+ * USB transceiver driver for AB8500 chip
+ *
+ * Copyright (C) 2010 ST-Ericsson AB
+ * Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+
+#define AB8500_MAIN_WD_CTRL_REG 0x01
+#define AB8500_USB_LINE_STAT_REG 0x80
+#define AB8500_USB_PHY_CTRL_REG 0x8A
+
+#define AB8500_BIT_OTG_STAT_ID (1 << 0)
+#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0)
+#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1)
+#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
+#define AB8500_BIT_WD_CTRL_KICK (1 << 1)
+
+#define AB8500_V1x_LINK_STAT_WAIT (HZ/10)
+#define AB8500_WD_KICK_DELAY_US 100 /* usec */
+#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
+#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */
+
+/* Usb line status register */
+enum ab8500_usb_link_status {
+	USB_LINK_NOT_CONFIGURED = 0,
+	USB_LINK_STD_HOST_NC,
+	USB_LINK_STD_HOST_C_NS,
+	USB_LINK_STD_HOST_C_S,
+	USB_LINK_HOST_CHG_NM,
+	USB_LINK_HOST_CHG_HS,
+	USB_LINK_HOST_CHG_HS_CHIRP,
+	USB_LINK_DEDICATED_CHG,
+	USB_LINK_ACA_RID_A,
+	USB_LINK_ACA_RID_B,
+	USB_LINK_ACA_RID_C_NM,
+	USB_LINK_ACA_RID_C_HS,
+	USB_LINK_ACA_RID_C_HS_CHIRP,
+	USB_LINK_HM_IDGND,
+	USB_LINK_RESERVED,
+	USB_LINK_NOT_VALID_LINK
+};
+
+struct ab8500_usb {
+	struct usb_phy phy;
+	struct device *dev;
+	int irq_num_id_rise;
+	int irq_num_id_fall;
+	int irq_num_vbus_rise;
+	int irq_num_vbus_fall;
+	int irq_num_link_status;
+	unsigned vbus_draw;
+	struct delayed_work dwork;
+	struct work_struct phy_dis_work;
+	unsigned long link_status_wait;
+	int rev;
+};
+
+static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x)
+{
+	return container_of(x, struct ab8500_usb, phy);
+}
+
+static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
+{
+	abx500_set_register_interruptible(ab->dev,
+		AB8500_SYS_CTRL2_BLOCK,
+		AB8500_MAIN_WD_CTRL_REG,
+		AB8500_BIT_WD_CTRL_ENABLE);
+
+	udelay(AB8500_WD_KICK_DELAY_US);
+
+	abx500_set_register_interruptible(ab->dev,
+		AB8500_SYS_CTRL2_BLOCK,
+		AB8500_MAIN_WD_CTRL_REG,
+		(AB8500_BIT_WD_CTRL_ENABLE
+		| AB8500_BIT_WD_CTRL_KICK));
+
+	if (ab->rev > 0x10) /* v1.1 v2.0 */
+		udelay(AB8500_WD_V11_DISABLE_DELAY_US);
+	else /* v1.0 */
+		msleep(AB8500_WD_V10_DISABLE_DELAY_MS);
+
+	abx500_set_register_interruptible(ab->dev,
+		AB8500_SYS_CTRL2_BLOCK,
+		AB8500_MAIN_WD_CTRL_REG,
+		0);
+}
+
+static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host,
+					bool enable)
+{
+	u8 ctrl_reg;
+	abx500_get_register_interruptible(ab->dev,
+				AB8500_USB,
+				AB8500_USB_PHY_CTRL_REG,
+				&ctrl_reg);
+	if (sel_host) {
+		if (enable)
+			ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN;
+		else
+			ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN;
+	} else {
+		if (enable)
+			ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN;
+		else
+			ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN;
+	}
+
+	abx500_set_register_interruptible(ab->dev,
+				AB8500_USB,
+				AB8500_USB_PHY_CTRL_REG,
+				ctrl_reg);
+
+	/* Needed to enable the phy.*/
+	if (enable)
+		ab8500_usb_wd_workaround(ab);
+}
+
+#define ab8500_usb_host_phy_en(ab)	ab8500_usb_phy_ctrl(ab, true, true)
+#define ab8500_usb_host_phy_dis(ab)	ab8500_usb_phy_ctrl(ab, true, false)
+#define ab8500_usb_peri_phy_en(ab)	ab8500_usb_phy_ctrl(ab, false, true)
+#define ab8500_usb_peri_phy_dis(ab)	ab8500_usb_phy_ctrl(ab, false, false)
+
+static int ab8500_usb_link_status_update(struct ab8500_usb *ab)
+{
+	u8 reg;
+	enum ab8500_usb_link_status lsts;
+	void *v = NULL;
+	enum usb_phy_events event;
+
+	abx500_get_register_interruptible(ab->dev,
+			AB8500_USB,
+			AB8500_USB_LINE_STAT_REG,
+			&reg);
+
+	lsts = (reg >> 3) & 0x0F;
+
+	switch (lsts) {
+	case USB_LINK_NOT_CONFIGURED:
+	case USB_LINK_RESERVED:
+	case USB_LINK_NOT_VALID_LINK:
+		/* TODO: Disable regulators. */
+		ab8500_usb_host_phy_dis(ab);
+		ab8500_usb_peri_phy_dis(ab);
+		ab->phy.state = OTG_STATE_B_IDLE;
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+		event = USB_EVENT_NONE;
+		break;
+
+	case USB_LINK_STD_HOST_NC:
+	case USB_LINK_STD_HOST_C_NS:
+	case USB_LINK_STD_HOST_C_S:
+	case USB_LINK_HOST_CHG_NM:
+	case USB_LINK_HOST_CHG_HS:
+	case USB_LINK_HOST_CHG_HS_CHIRP:
+		if (ab->phy.otg->gadget) {
+			/* TODO: Enable regulators. */
+			ab8500_usb_peri_phy_en(ab);
+			v = ab->phy.otg->gadget;
+		}
+		event = USB_EVENT_VBUS;
+		break;
+
+	case USB_LINK_HM_IDGND:
+		if (ab->phy.otg->host) {
+			/* TODO: Enable regulators. */
+			ab8500_usb_host_phy_en(ab);
+			v = ab->phy.otg->host;
+		}
+		ab->phy.state = OTG_STATE_A_IDLE;
+		ab->phy.otg->default_a = true;
+		event = USB_EVENT_ID;
+		break;
+
+	case USB_LINK_ACA_RID_A:
+	case USB_LINK_ACA_RID_B:
+		/* TODO */
+	case USB_LINK_ACA_RID_C_NM:
+	case USB_LINK_ACA_RID_C_HS:
+	case USB_LINK_ACA_RID_C_HS_CHIRP:
+	case USB_LINK_DEDICATED_CHG:
+		/* TODO: vbus_draw */
+		event = USB_EVENT_CHARGER;
+		break;
+	}
+
+	atomic_notifier_call_chain(&ab->phy.notifier, event, v);
+
+	return 0;
+}
+
+static void ab8500_usb_delayed_work(struct work_struct *work)
+{
+	struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
+						dwork.work);
+
+	ab8500_usb_link_status_update(ab);
+}
+
+static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data)
+{
+	struct ab8500_usb *ab = (struct ab8500_usb *) data;
+
+	/* Wait for link status to become stable. */
+	schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data)
+{
+	struct ab8500_usb *ab = (struct ab8500_usb *) data;
+
+	/* Link status will not be updated till phy is disabled. */
+	ab8500_usb_peri_phy_dis(ab);
+
+	/* Wait for link status to become stable. */
+	schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ab8500_usb_v20_irq(int irq, void *data)
+{
+	struct ab8500_usb *ab = (struct ab8500_usb *) data;
+
+	ab8500_usb_link_status_update(ab);
+
+	return IRQ_HANDLED;
+}
+
+static void ab8500_usb_phy_disable_work(struct work_struct *work)
+{
+	struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
+						phy_dis_work);
+
+	if (!ab->phy.otg->host)
+		ab8500_usb_host_phy_dis(ab);
+
+	if (!ab->phy.otg->gadget)
+		ab8500_usb_peri_phy_dis(ab);
+}
+
+static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA)
+{
+	struct ab8500_usb *ab;
+
+	if (!phy)
+		return -ENODEV;
+
+	ab = phy_to_ab(phy);
+
+	ab->vbus_draw = mA;
+
+	if (mA)
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				USB_EVENT_ENUMERATED, ab->phy.otg->gadget);
+	return 0;
+}
+
+/* TODO: Implement some way for charging or other drivers to read
+ * ab->vbus_draw.
+ */
+
+static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend)
+{
+	/* TODO */
+	return 0;
+}
+
+static int ab8500_usb_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct ab8500_usb *ab;
+
+	if (!otg)
+		return -ENODEV;
+
+	ab = phy_to_ab(otg->phy);
+
+	/* Some drivers call this function in atomic context.
+	 * Do not update ab8500 registers directly till this
+	 * is fixed.
+	 */
+
+	if (!gadget) {
+		/* TODO: Disable regulators. */
+		otg->gadget = NULL;
+		schedule_work(&ab->phy_dis_work);
+	} else {
+		otg->gadget = gadget;
+		otg->phy->state = OTG_STATE_B_IDLE;
+
+		/* Phy will not be enabled if cable is already
+		 * plugged-in. Schedule to enable phy.
+		 * Use same delay to avoid any race condition.
+		 */
+		schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+	}
+
+	return 0;
+}
+
+static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct ab8500_usb *ab;
+
+	if (!otg)
+		return -ENODEV;
+
+	ab = phy_to_ab(otg->phy);
+
+	/* Some drivers call this function in atomic context.
+	 * Do not update ab8500 registers directly till this
+	 * is fixed.
+	 */
+
+	if (!host) {
+		/* TODO: Disable regulators. */
+		otg->host = NULL;
+		schedule_work(&ab->phy_dis_work);
+	} else {
+		otg->host = host;
+		/* Phy will not be enabled if cable is already
+		 * plugged-in. Schedule to enable phy.
+		 * Use same delay to avoid any race condition.
+		 */
+		schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+	}
+
+	return 0;
+}
+
+static void ab8500_usb_irq_free(struct ab8500_usb *ab)
+{
+	if (ab->rev < 0x20) {
+		free_irq(ab->irq_num_id_rise, ab);
+		free_irq(ab->irq_num_id_fall, ab);
+		free_irq(ab->irq_num_vbus_rise, ab);
+		free_irq(ab->irq_num_vbus_fall, ab);
+	} else {
+		free_irq(ab->irq_num_link_status, ab);
+	}
+}
+
+static int ab8500_usb_v1x_res_setup(struct platform_device *pdev,
+				struct ab8500_usb *ab)
+{
+	int err;
+
+	ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R");
+	if (ab->irq_num_id_rise < 0) {
+		dev_err(&pdev->dev, "ID rise irq not found\n");
+		return ab->irq_num_id_rise;
+	}
+	err = request_threaded_irq(ab->irq_num_id_rise, NULL,
+		ab8500_usb_v1x_common_irq,
+		IRQF_NO_SUSPEND | IRQF_SHARED,
+		"usb-id-rise", ab);
+	if (err < 0) {
+		dev_err(ab->dev, "request_irq failed for ID rise irq\n");
+		goto fail0;
+	}
+
+	ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
+	if (ab->irq_num_id_fall < 0) {
+		dev_err(&pdev->dev, "ID fall irq not found\n");
+		return ab->irq_num_id_fall;
+	}
+	err = request_threaded_irq(ab->irq_num_id_fall, NULL,
+		ab8500_usb_v1x_common_irq,
+		IRQF_NO_SUSPEND | IRQF_SHARED,
+		"usb-id-fall", ab);
+	if (err < 0) {
+		dev_err(ab->dev, "request_irq failed for ID fall irq\n");
+		goto fail1;
+	}
+
+	ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R");
+	if (ab->irq_num_vbus_rise < 0) {
+		dev_err(&pdev->dev, "VBUS rise irq not found\n");
+		return ab->irq_num_vbus_rise;
+	}
+	err = request_threaded_irq(ab->irq_num_vbus_rise, NULL,
+		ab8500_usb_v1x_common_irq,
+		IRQF_NO_SUSPEND | IRQF_SHARED,
+		"usb-vbus-rise", ab);
+	if (err < 0) {
+		dev_err(ab->dev, "request_irq failed for Vbus rise irq\n");
+		goto fail2;
+	}
+
+	ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F");
+	if (ab->irq_num_vbus_fall < 0) {
+		dev_err(&pdev->dev, "VBUS fall irq not found\n");
+		return ab->irq_num_vbus_fall;
+	}
+	err = request_threaded_irq(ab->irq_num_vbus_fall, NULL,
+		ab8500_usb_v1x_vbus_fall_irq,
+		IRQF_NO_SUSPEND | IRQF_SHARED,
+		"usb-vbus-fall", ab);
+	if (err < 0) {
+		dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
+		goto fail3;
+	}
+
+	return 0;
+fail3:
+	free_irq(ab->irq_num_vbus_rise, ab);
+fail2:
+	free_irq(ab->irq_num_id_fall, ab);
+fail1:
+	free_irq(ab->irq_num_id_rise, ab);
+fail0:
+	return err;
+}
+
+static int ab8500_usb_v2_res_setup(struct platform_device *pdev,
+				struct ab8500_usb *ab)
+{
+	int err;
+
+	ab->irq_num_link_status = platform_get_irq_byname(pdev,
+						"USB_LINK_STATUS");
+	if (ab->irq_num_link_status < 0) {
+		dev_err(&pdev->dev, "Link status irq not found\n");
+		return ab->irq_num_link_status;
+	}
+
+	err = request_threaded_irq(ab->irq_num_link_status, NULL,
+		ab8500_usb_v20_irq,
+		IRQF_NO_SUSPEND | IRQF_SHARED,
+		"usb-link-status", ab);
+	if (err < 0) {
+		dev_err(ab->dev,
+			"request_irq failed for link status irq\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int __devinit ab8500_usb_probe(struct platform_device *pdev)
+{
+	struct ab8500_usb	*ab;
+	struct usb_otg		*otg;
+	int err;
+	int rev;
+
+	rev = abx500_get_chip_id(&pdev->dev);
+	if (rev < 0) {
+		dev_err(&pdev->dev, "Chip id read failed\n");
+		return rev;
+	} else if (rev < 0x10) {
+		dev_err(&pdev->dev, "Unsupported AB8500 chip\n");
+		return -ENODEV;
+	}
+
+	ab = kzalloc(sizeof *ab, GFP_KERNEL);
+	if (!ab)
+		return -ENOMEM;
+
+	otg = kzalloc(sizeof *otg, GFP_KERNEL);
+	if (!otg) {
+		kfree(ab);
+		return -ENOMEM;
+	}
+
+	ab->dev			= &pdev->dev;
+	ab->rev			= rev;
+	ab->phy.dev		= ab->dev;
+	ab->phy.otg		= otg;
+	ab->phy.label		= "ab8500";
+	ab->phy.set_suspend	= ab8500_usb_set_suspend;
+	ab->phy.set_power	= ab8500_usb_set_power;
+	ab->phy.state		= OTG_STATE_UNDEFINED;
+
+	otg->phy		= &ab->phy;
+	otg->set_host		= ab8500_usb_set_host;
+	otg->set_peripheral	= ab8500_usb_set_peripheral;
+
+	platform_set_drvdata(pdev, ab);
+
+	ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier);
+
+	/* v1: Wait for link status to become stable.
+	 * all: Updates form set_host and set_peripheral as they are atomic.
+	 */
+	INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work);
+
+	/* all: Disable phy when called from set_host and set_peripheral */
+	INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
+
+	if (ab->rev < 0x20) {
+		err = ab8500_usb_v1x_res_setup(pdev, ab);
+		ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT;
+	} else {
+		err = ab8500_usb_v2_res_setup(pdev, ab);
+	}
+
+	if (err < 0)
+		goto fail0;
+
+	err = usb_set_transceiver(&ab->phy);
+	if (err) {
+		dev_err(&pdev->dev, "Can't register transceiver\n");
+		goto fail1;
+	}
+
+	dev_info(&pdev->dev, "AB8500 usb driver initialized\n");
+
+	return 0;
+fail1:
+	ab8500_usb_irq_free(ab);
+fail0:
+	kfree(otg);
+	kfree(ab);
+	return err;
+}
+
+static int __devexit ab8500_usb_remove(struct platform_device *pdev)
+{
+	struct ab8500_usb *ab = platform_get_drvdata(pdev);
+
+	ab8500_usb_irq_free(ab);
+
+	cancel_delayed_work_sync(&ab->dwork);
+
+	cancel_work_sync(&ab->phy_dis_work);
+
+	usb_set_transceiver(NULL);
+
+	ab8500_usb_host_phy_dis(ab);
+	ab8500_usb_peri_phy_dis(ab);
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(ab->phy.otg);
+	kfree(ab);
+
+	return 0;
+}
+
+static struct platform_driver ab8500_usb_driver = {
+	.probe		= ab8500_usb_probe,
+	.remove		= __devexit_p(ab8500_usb_remove),
+	.driver		= {
+		.name	= "ab8500-usb",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ab8500_usb_init(void)
+{
+	return platform_driver_register(&ab8500_usb_driver);
+}
+subsys_initcall(ab8500_usb_init);
+
+static void __exit ab8500_usb_exit(void)
+{
+	platform_driver_unregister(&ab8500_usb_driver);
+}
+module_exit(ab8500_usb_exit);
+
+MODULE_ALIAS("platform:ab8500_usb");
+MODULE_AUTHOR("ST-Ericsson AB");
+MODULE_DESCRIPTION("AB8500 usb transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/fsl_otg.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/fsl_otg.c
new file mode 100644
index 0000000..be4a63e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/fsl_otg.c
@@ -0,0 +1,1169 @@
+/*
+ * Copyright (C) 2007,2008 Freescale semiconductor, Inc.
+ *
+ * Author: Li Yang <LeoLi@freescale.com>
+ *         Jerry Huang <Chang-Ming.Huang@freescale.com>
+ *
+ * Initialization based on code from Shlomi Gridish.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+#include <linux/time.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/unaligned.h>
+
+#include "fsl_otg.h"
+
+#define DRIVER_VERSION "Rev. 1.55"
+#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
+#define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
+#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION
+
+static const char driver_name[] = "fsl-usb2-otg";
+
+const pm_message_t otg_suspend_state = {
+	.event = 1,
+};
+
+#define HA_DATA_PULSE
+
+static struct usb_dr_mmap *usb_dr_regs;
+static struct fsl_otg *fsl_otg_dev;
+static int srp_wait_done;
+
+/* FSM timers */
+struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
+	*b_ase0_brst_tmr, *b_se0_srp_tmr;
+
+/* Driver specific timers */
+struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
+	*b_srp_wait_tmr, *a_wait_enum_tmr;
+
+static struct list_head active_timers;
+
+static struct fsl_otg_config fsl_otg_initdata = {
+	.otg_port = 1,
+};
+
+#ifdef CONFIG_PPC32
+static u32 _fsl_readl_be(const unsigned __iomem *p)
+{
+	return in_be32(p);
+}
+
+static u32 _fsl_readl_le(const unsigned __iomem *p)
+{
+	return in_le32(p);
+}
+
+static void _fsl_writel_be(u32 v, unsigned __iomem *p)
+{
+	out_be32(p, v);
+}
+
+static void _fsl_writel_le(u32 v, unsigned __iomem *p)
+{
+	out_le32(p, v);
+}
+
+static u32 (*_fsl_readl)(const unsigned __iomem *p);
+static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
+
+#define fsl_readl(p)		(*_fsl_readl)((p))
+#define fsl_writel(v, p)	(*_fsl_writel)((v), (p))
+
+#else
+#define fsl_readl(addr)		readl(addr)
+#define fsl_writel(val, addr)	writel(val, addr)
+#endif /* CONFIG_PPC32 */
+
+/* Routines to access transceiver ULPI registers */
+u8 view_ulpi(u8 addr)
+{
+	u32 temp;
+
+	temp = 0x40000000 | (addr << 16);
+	fsl_writel(temp, &usb_dr_regs->ulpiview);
+	udelay(1000);
+	while (temp & 0x40)
+		temp = fsl_readl(&usb_dr_regs->ulpiview);
+	return (le32_to_cpu(temp) & 0x0000ff00) >> 8;
+}
+
+int write_ulpi(u8 addr, u8 data)
+{
+	u32 temp;
+
+	temp = 0x60000000 | (addr << 16) | data;
+	fsl_writel(temp, &usb_dr_regs->ulpiview);
+	return 0;
+}
+
+/* -------------------------------------------------------------*/
+/* Operations that will be called from OTG Finite State Machine */
+
+/* Charge vbus for vbus pulsing in SRP */
+void fsl_otg_chrg_vbus(int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+
+	if (on)
+		/* stop discharging, start charging */
+		tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) |
+			OTGSC_CTRL_VBUS_CHARGE;
+	else
+		/* stop charging */
+		tmp &= ~OTGSC_CTRL_VBUS_CHARGE;
+
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+}
+
+/* Discharge vbus through a resistor to ground */
+void fsl_otg_dischrg_vbus(int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+
+	if (on)
+		/* stop charging, start discharging */
+		tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) |
+			OTGSC_CTRL_VBUS_DISCHARGE;
+	else
+		/* stop discharging */
+		tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE;
+
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+}
+
+/* A-device driver vbus, controlled through PP bit in PORTSC */
+void fsl_otg_drv_vbus(int on)
+{
+	u32 tmp;
+
+	if (on) {
+		tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS;
+		fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc);
+	} else {
+		tmp = fsl_readl(&usb_dr_regs->portsc) &
+		      ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER;
+		fsl_writel(tmp, &usb_dr_regs->portsc);
+	}
+}
+
+/*
+ * Pull-up D+, signalling connect by periperal. Also used in
+ * data-line pulsing in SRP
+ */
+void fsl_otg_loc_conn(int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+
+	if (on)
+		tmp |= OTGSC_CTRL_DATA_PULSING;
+	else
+		tmp &= ~OTGSC_CTRL_DATA_PULSING;
+
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+}
+
+/*
+ * Generate SOF by host.  This is controlled through suspend/resume the
+ * port.  In host mode, controller will automatically send SOF.
+ * Suspend will block the data on the port.
+ */
+void fsl_otg_loc_sof(int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS;
+	if (on)
+		tmp |= PORTSC_PORT_FORCE_RESUME;
+	else
+		tmp |= PORTSC_PORT_SUSPEND;
+
+	fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc);
+
+}
+
+/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
+void fsl_otg_start_pulse(void)
+{
+	u32 tmp;
+
+	srp_wait_done = 0;
+#ifdef HA_DATA_PULSE
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+	tmp |= OTGSC_HA_DATA_PULSE;
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+#else
+	fsl_otg_loc_conn(1);
+#endif
+
+	fsl_otg_add_timer(b_data_pulse_tmr);
+}
+
+void b_data_pulse_end(unsigned long foo)
+{
+#ifdef HA_DATA_PULSE
+#else
+	fsl_otg_loc_conn(0);
+#endif
+
+	/* Do VBUS pulse after data pulse */
+	fsl_otg_pulse_vbus();
+}
+
+void fsl_otg_pulse_vbus(void)
+{
+	srp_wait_done = 0;
+	fsl_otg_chrg_vbus(1);
+	/* start the timer to end vbus charge */
+	fsl_otg_add_timer(b_vbus_pulse_tmr);
+}
+
+void b_vbus_pulse_end(unsigned long foo)
+{
+	fsl_otg_chrg_vbus(0);
+
+	/*
+	 * As USB3300 using the same a_sess_vld and b_sess_vld voltage
+	 * we need to discharge the bus for a while to distinguish
+	 * residual voltage of vbus pulsing and A device pull up
+	 */
+	fsl_otg_dischrg_vbus(1);
+	fsl_otg_add_timer(b_srp_wait_tmr);
+}
+
+void b_srp_end(unsigned long foo)
+{
+	fsl_otg_dischrg_vbus(0);
+	srp_wait_done = 1;
+
+	if ((fsl_otg_dev->phy.state == OTG_STATE_B_SRP_INIT) &&
+	    fsl_otg_dev->fsm.b_sess_vld)
+		fsl_otg_dev->fsm.b_srp_done = 1;
+}
+
+/*
+ * Workaround for a_host suspending too fast.  When a_bus_req=0,
+ * a_host will start by SRP.  It needs to set b_hnp_enable before
+ * actually suspending to start HNP
+ */
+void a_wait_enum(unsigned long foo)
+{
+	VDBG("a_wait_enum timeout\n");
+	if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
+		fsl_otg_add_timer(a_wait_enum_tmr);
+	else
+		otg_statemachine(&fsl_otg_dev->fsm);
+}
+
+/* The timeout callback function to set time out bit */
+void set_tmout(unsigned long indicator)
+{
+	*(int *)indicator = 1;
+}
+
+/* Initialize timers */
+int fsl_otg_init_timers(struct otg_fsm *fsm)
+{
+	/* FSM used timers */
+	a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
+				(unsigned long)&fsm->a_wait_vrise_tmout);
+	if (!a_wait_vrise_tmr)
+		return -ENOMEM;
+
+	a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
+				(unsigned long)&fsm->a_wait_bcon_tmout);
+	if (!a_wait_bcon_tmr)
+		return -ENOMEM;
+
+	a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
+				(unsigned long)&fsm->a_aidl_bdis_tmout);
+	if (!a_aidl_bdis_tmr)
+		return -ENOMEM;
+
+	b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
+				(unsigned long)&fsm->b_ase0_brst_tmout);
+	if (!b_ase0_brst_tmr)
+		return -ENOMEM;
+
+	b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
+				(unsigned long)&fsm->b_se0_srp);
+	if (!b_se0_srp_tmr)
+		return -ENOMEM;
+
+	b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL,
+				(unsigned long)&fsm->b_srp_done);
+	if (!b_srp_fail_tmr)
+		return -ENOMEM;
+
+	a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10,
+				(unsigned long)&fsm);
+	if (!a_wait_enum_tmr)
+		return -ENOMEM;
+
+	/* device driver used timers */
+	b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0);
+	if (!b_srp_wait_tmr)
+		return -ENOMEM;
+
+	b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end,
+				TB_DATA_PLS, 0);
+	if (!b_data_pulse_tmr)
+		return -ENOMEM;
+
+	b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end,
+				TB_VBUS_PLS, 0);
+	if (!b_vbus_pulse_tmr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/* Uninitialize timers */
+void fsl_otg_uninit_timers(void)
+{
+	/* FSM used timers */
+	if (a_wait_vrise_tmr != NULL)
+		kfree(a_wait_vrise_tmr);
+	if (a_wait_bcon_tmr != NULL)
+		kfree(a_wait_bcon_tmr);
+	if (a_aidl_bdis_tmr != NULL)
+		kfree(a_aidl_bdis_tmr);
+	if (b_ase0_brst_tmr != NULL)
+		kfree(b_ase0_brst_tmr);
+	if (b_se0_srp_tmr != NULL)
+		kfree(b_se0_srp_tmr);
+	if (b_srp_fail_tmr != NULL)
+		kfree(b_srp_fail_tmr);
+	if (a_wait_enum_tmr != NULL)
+		kfree(a_wait_enum_tmr);
+
+	/* device driver used timers */
+	if (b_srp_wait_tmr != NULL)
+		kfree(b_srp_wait_tmr);
+	if (b_data_pulse_tmr != NULL)
+		kfree(b_data_pulse_tmr);
+	if (b_vbus_pulse_tmr != NULL)
+		kfree(b_vbus_pulse_tmr);
+}
+
+/* Add timer to timer list */
+void fsl_otg_add_timer(void *gtimer)
+{
+	struct fsl_otg_timer *timer = gtimer;
+	struct fsl_otg_timer *tmp_timer;
+
+	/*
+	 * Check if the timer is already in the active list,
+	 * if so update timer count
+	 */
+	list_for_each_entry(tmp_timer, &active_timers, list)
+	    if (tmp_timer == timer) {
+		timer->count = timer->expires;
+		return;
+	}
+	timer->count = timer->expires;
+	list_add_tail(&timer->list, &active_timers);
+}
+
+/* Remove timer from the timer list; clear timeout status */
+void fsl_otg_del_timer(void *gtimer)
+{
+	struct fsl_otg_timer *timer = gtimer;
+	struct fsl_otg_timer *tmp_timer, *del_tmp;
+
+	list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
+		if (tmp_timer == timer)
+			list_del(&timer->list);
+}
+
+/*
+ * Reduce timer count by 1, and find timeout conditions.
+ * Called by fsl_otg 1ms timer interrupt
+ */
+int fsl_otg_tick_timer(void)
+{
+	struct fsl_otg_timer *tmp_timer, *del_tmp;
+	int expired = 0;
+
+	list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
+		tmp_timer->count--;
+		/* check if timer expires */
+		if (!tmp_timer->count) {
+			list_del(&tmp_timer->list);
+			tmp_timer->function(tmp_timer->data);
+			expired = 1;
+		}
+	}
+
+	return expired;
+}
+
+/* Reset controller, not reset the bus */
+void otg_reset_controller(void)
+{
+	u32 command;
+
+	command = fsl_readl(&usb_dr_regs->usbcmd);
+	command |= (1 << 1);
+	fsl_writel(command, &usb_dr_regs->usbcmd);
+	while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1))
+		;
+}
+
+/* Call suspend/resume routines in host driver */
+int fsl_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otg = fsm->otg;
+	struct device *dev;
+	struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+	u32 retval = 0;
+
+	if (!otg->host)
+		return -ENODEV;
+	dev = otg->host->controller;
+
+	/*
+	 * Update a_vbus_vld state as a_vbus_vld int is disabled
+	 * in device mode
+	 */
+	fsm->a_vbus_vld =
+		!!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID);
+	if (on) {
+		/* start fsl usb host controller */
+		if (otg_dev->host_working)
+			goto end;
+		else {
+			otg_reset_controller();
+			VDBG("host on......\n");
+			if (dev->driver->pm && dev->driver->pm->resume) {
+				retval = dev->driver->pm->resume(dev);
+				if (fsm->id) {
+					/* default-b */
+					fsl_otg_drv_vbus(1);
+					/*
+					 * Workaround: b_host can't driver
+					 * vbus, but PP in PORTSC needs to
+					 * be 1 for host to work.
+					 * So we set drv_vbus bit in
+					 * transceiver to 0 thru ULPI.
+					 */
+					write_ulpi(0x0c, 0x20);
+				}
+			}
+
+			otg_dev->host_working = 1;
+		}
+	} else {
+		/* stop fsl usb host controller */
+		if (!otg_dev->host_working)
+			goto end;
+		else {
+			VDBG("host off......\n");
+			if (dev && dev->driver) {
+				if (dev->driver->pm && dev->driver->pm->suspend)
+					retval = dev->driver->pm->suspend(dev);
+				if (fsm->id)
+					/* default-b */
+					fsl_otg_drv_vbus(0);
+			}
+			otg_dev->host_working = 0;
+		}
+	}
+end:
+	return retval;
+}
+
+/*
+ * Call suspend and resume function in udc driver
+ * to stop and start udc driver.
+ */
+int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otg = fsm->otg;
+	struct device *dev;
+
+	if (!otg->gadget || !otg->gadget->dev.parent)
+		return -ENODEV;
+
+	VDBG("gadget %s\n", on ? "on" : "off");
+	dev = otg->gadget->dev.parent;
+
+	if (on) {
+		if (dev->driver->resume)
+			dev->driver->resume(dev);
+	} else {
+		if (dev->driver->suspend)
+			dev->driver->suspend(dev, otg_suspend_state);
+	}
+
+	return 0;
+}
+
+/*
+ * Called by initialization code of host driver.  Register host controller
+ * to the OTG.  Suspend host for OTG role detection.
+ */
+static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+
+	if (!otg || otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	otg->host = host;
+
+	otg_dev->fsm.a_bus_drop = 0;
+	otg_dev->fsm.a_bus_req = 1;
+
+	if (host) {
+		VDBG("host off......\n");
+
+		otg->host->otg_port = fsl_otg_initdata.otg_port;
+		otg->host->is_b_host = otg_dev->fsm.id;
+		/*
+		 * must leave time for khubd to finish its thing
+		 * before yanking the host driver out from under it,
+		 * so suspend the host after a short delay.
+		 */
+		otg_dev->host_working = 1;
+		schedule_delayed_work(&otg_dev->otg_event, 100);
+		return 0;
+	} else {
+		/* host driver going away */
+		if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) &
+		      OTGSC_STS_USB_ID)) {
+			/* Mini-A cable connected */
+			struct otg_fsm *fsm = &otg_dev->fsm;
+
+			otg->phy->state = OTG_STATE_UNDEFINED;
+			fsm->protocol = PROTO_UNDEF;
+		}
+	}
+
+	otg_dev->host_working = 0;
+
+	otg_statemachine(&otg_dev->fsm);
+
+	return 0;
+}
+
+/* Called by initialization code of udc.  Register udc to OTG. */
+static int fsl_otg_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+
+	VDBG("otg_dev 0x%x\n", (int)otg_dev);
+	VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
+
+	if (!otg || otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	if (!gadget) {
+		if (!otg->default_a)
+			otg->gadget->ops->vbus_draw(otg->gadget, 0);
+		usb_gadget_vbus_disconnect(otg->gadget);
+		otg->gadget = 0;
+		otg_dev->fsm.b_bus_req = 0;
+		otg_statemachine(&otg_dev->fsm);
+		return 0;
+	}
+
+	otg->gadget = gadget;
+	otg->gadget->is_a_peripheral = !otg_dev->fsm.id;
+
+	otg_dev->fsm.b_bus_req = 1;
+
+	/* start the gadget right away if the ID pin says Mini-B */
+	DBG("ID pin=%d\n", otg_dev->fsm.id);
+	if (otg_dev->fsm.id == 1) {
+		fsl_otg_start_host(&otg_dev->fsm, 0);
+		otg_drv_vbus(&otg_dev->fsm, 0);
+		fsl_otg_start_gadget(&otg_dev->fsm, 1);
+	}
+
+	return 0;
+}
+
+/* Set OTG port power, only for B-device */
+static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA)
+{
+	if (!fsl_otg_dev)
+		return -ENODEV;
+	if (phy->state == OTG_STATE_B_PERIPHERAL)
+		pr_info("FSL OTG: Draw %d mA\n", mA);
+
+	return 0;
+}
+
+/*
+ * Delayed pin detect interrupt processing.
+ *
+ * When the Mini-A cable is disconnected from the board,
+ * the pin-detect interrupt happens before the disconnect
+ * interrupts for the connected device(s).  In order to
+ * process the disconnect interrupt(s) prior to switching
+ * roles, the pin-detect interrupts are delayed, and handled
+ * by this routine.
+ */
+static void fsl_otg_event(struct work_struct *work)
+{
+	struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
+	struct otg_fsm *fsm = &og->fsm;
+
+	if (fsm->id) {		/* switch to gadget */
+		fsl_otg_start_host(fsm, 0);
+		otg_drv_vbus(fsm, 0);
+		fsl_otg_start_gadget(fsm, 1);
+	}
+}
+
+/* B-device start SRP */
+static int fsl_otg_start_srp(struct usb_otg *otg)
+{
+	struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+
+	if (!otg || otg_dev != fsl_otg_dev
+	    || otg->phy->state != OTG_STATE_B_IDLE)
+		return -ENODEV;
+
+	otg_dev->fsm.b_bus_req = 1;
+	otg_statemachine(&otg_dev->fsm);
+
+	return 0;
+}
+
+/* A_host suspend will call this function to start hnp */
+static int fsl_otg_start_hnp(struct usb_otg *otg)
+{
+	struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+
+	if (!otg || otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	DBG("start_hnp...n");
+
+	/* clear a_bus_req to enter a_suspend state */
+	otg_dev->fsm.a_bus_req = 0;
+	otg_statemachine(&otg_dev->fsm);
+
+	return 0;
+}
+
+/*
+ * Interrupt handler.  OTG/host/peripheral share the same int line.
+ * OTG driver clears OTGSC interrupts and leaves USB interrupts
+ * intact.  It needs to have knowledge of some USB interrupts
+ * such as port change.
+ */
+irqreturn_t fsl_otg_isr(int irq, void *dev_id)
+{
+	struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
+	struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
+	u32 otg_int_src, otg_sc;
+
+	otg_sc = fsl_readl(&usb_dr_regs->otgsc);
+	otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8);
+
+	/* Only clear otg interrupts */
+	fsl_writel(otg_sc, &usb_dr_regs->otgsc);
+
+	/*FIXME: ID change not generate when init to 0 */
+	fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+	otg->default_a = (fsm->id == 0);
+
+	/* process OTG interrupts */
+	if (otg_int_src) {
+		if (otg_int_src & OTGSC_INTSTS_USB_ID) {
+			fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+			otg->default_a = (fsm->id == 0);
+			/* clear conn information */
+			if (fsm->id)
+				fsm->b_conn = 0;
+			else
+				fsm->a_conn = 0;
+
+			if (otg->host)
+				otg->host->is_b_host = fsm->id;
+			if (otg->gadget)
+				otg->gadget->is_a_peripheral = !fsm->id;
+			VDBG("ID int (ID is %d)\n", fsm->id);
+
+			if (fsm->id) {	/* switch to gadget */
+				schedule_delayed_work(
+					&((struct fsl_otg *)dev_id)->otg_event,
+					100);
+			} else {	/* switch to host */
+				cancel_delayed_work(&
+						    ((struct fsl_otg *)dev_id)->
+						    otg_event);
+				fsl_otg_start_gadget(fsm, 0);
+				otg_drv_vbus(fsm, 1);
+				fsl_otg_start_host(fsm, 1);
+			}
+			return IRQ_HANDLED;
+		}
+	}
+	return IRQ_NONE;
+}
+
+static struct otg_fsm_ops fsl_otg_ops = {
+	.chrg_vbus = fsl_otg_chrg_vbus,
+	.drv_vbus = fsl_otg_drv_vbus,
+	.loc_conn = fsl_otg_loc_conn,
+	.loc_sof = fsl_otg_loc_sof,
+	.start_pulse = fsl_otg_start_pulse,
+
+	.add_timer = fsl_otg_add_timer,
+	.del_timer = fsl_otg_del_timer,
+
+	.start_host = fsl_otg_start_host,
+	.start_gadget = fsl_otg_start_gadget,
+};
+
+/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
+static int fsl_otg_conf(struct platform_device *pdev)
+{
+	struct fsl_otg *fsl_otg_tc;
+	int status;
+
+	if (fsl_otg_dev)
+		return 0;
+
+	/* allocate space to fsl otg device */
+	fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL);
+	if (!fsl_otg_tc)
+		return -ENOMEM;
+
+	fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+	if (!fsl_otg_tc->phy.otg) {
+		kfree(fsl_otg_tc);
+		return -ENOMEM;
+	}
+
+	INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);
+
+	INIT_LIST_HEAD(&active_timers);
+	status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
+	if (status) {
+		pr_info("Couldn't init OTG timers\n");
+		goto err;
+	}
+	spin_lock_init(&fsl_otg_tc->fsm.lock);
+
+	/* Set OTG state machine operations */
+	fsl_otg_tc->fsm.ops = &fsl_otg_ops;
+
+	/* initialize the otg structure */
+	fsl_otg_tc->phy.label = DRIVER_DESC;
+	fsl_otg_tc->phy.set_power = fsl_otg_set_power;
+
+	fsl_otg_tc->phy.otg->phy = &fsl_otg_tc->phy;
+	fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host;
+	fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral;
+	fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp;
+	fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp;
+
+	fsl_otg_dev = fsl_otg_tc;
+
+	/* Store the otg transceiver */
+	status = usb_set_transceiver(&fsl_otg_tc->phy);
+	if (status) {
+		pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n");
+		goto err;
+	}
+
+	return 0;
+err:
+	fsl_otg_uninit_timers();
+	kfree(fsl_otg_tc->phy.otg);
+	kfree(fsl_otg_tc);
+	return status;
+}
+
+/* OTG Initialization */
+int usb_otg_start(struct platform_device *pdev)
+{
+	struct fsl_otg *p_otg;
+	struct usb_phy *otg_trans = usb_get_transceiver();
+	struct otg_fsm *fsm;
+	int status;
+	struct resource *res;
+	u32 temp;
+	struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+	p_otg = container_of(otg_trans, struct fsl_otg, phy);
+	fsm = &p_otg->fsm;
+
+	/* Initialize the state machine structure with default values */
+	SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
+	fsm->otg = p_otg->phy.otg;
+
+	/* We don't require predefined MEM/IRQ resource index */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	/* We don't request_mem_region here to enable resource sharing
+	 * with host/device */
+
+	usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap));
+	p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs;
+	pdata->regs = (void *)usb_dr_regs;
+
+	if (pdata->init && pdata->init(pdev) != 0)
+		return -EINVAL;
+
+	if (pdata->big_endian_mmio) {
+		_fsl_readl = _fsl_readl_be;
+		_fsl_writel = _fsl_writel_be;
+	} else {
+		_fsl_readl = _fsl_readl_le;
+		_fsl_writel = _fsl_writel_le;
+	}
+
+	/* request irq */
+	p_otg->irq = platform_get_irq(pdev, 0);
+	status = request_irq(p_otg->irq, fsl_otg_isr,
+				IRQF_SHARED, driver_name, p_otg);
+	if (status) {
+		dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n",
+			p_otg->irq, status);
+		iounmap(p_otg->dr_mem_map);
+		kfree(p_otg->phy.otg);
+		kfree(p_otg);
+		return status;
+	}
+
+	/* stop the controller */
+	temp = fsl_readl(&p_otg->dr_mem_map->usbcmd);
+	temp &= ~USB_CMD_RUN_STOP;
+	fsl_writel(temp, &p_otg->dr_mem_map->usbcmd);
+
+	/* reset the controller */
+	temp = fsl_readl(&p_otg->dr_mem_map->usbcmd);
+	temp |= USB_CMD_CTRL_RESET;
+	fsl_writel(temp, &p_otg->dr_mem_map->usbcmd);
+
+	/* wait reset completed */
+	while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET)
+		;
+
+	/* configure the VBUSHS as IDLE(both host and device) */
+	temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0);
+	fsl_writel(temp, &p_otg->dr_mem_map->usbmode);
+
+	/* configure PHY interface */
+	temp = fsl_readl(&p_otg->dr_mem_map->portsc);
+	temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW);
+	switch (pdata->phy_mode) {
+	case FSL_USB2_PHY_ULPI:
+		temp |= PORTSC_PTS_ULPI;
+		break;
+	case FSL_USB2_PHY_UTMI_WIDE:
+		temp |= PORTSC_PTW_16BIT;
+		/* fall through */
+	case FSL_USB2_PHY_UTMI:
+		temp |= PORTSC_PTS_UTMI;
+		/* fall through */
+	default:
+		break;
+	}
+	fsl_writel(temp, &p_otg->dr_mem_map->portsc);
+
+	if (pdata->have_sysif_regs) {
+		/* configure control enable IO output, big endian register */
+		temp = __raw_readl(&p_otg->dr_mem_map->control);
+		temp |= USB_CTRL_IOENB;
+		__raw_writel(temp, &p_otg->dr_mem_map->control);
+	}
+
+	/* disable all interrupt and clear all OTGSC status */
+	temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
+	temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
+	temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE;
+	fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
+
+	/*
+	 * The identification (id) input is FALSE when a Mini-A plug is inserted
+	 * in the devices Mini-AB receptacle. Otherwise, this input is TRUE.
+	 * Also: record initial state of ID pin
+	 */
+	if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
+		p_otg->phy.state = OTG_STATE_UNDEFINED;
+		p_otg->fsm.id = 1;
+	} else {
+		p_otg->phy.state = OTG_STATE_A_IDLE;
+		p_otg->fsm.id = 0;
+	}
+
+	DBG("initial ID pin=%d\n", p_otg->fsm.id);
+
+	/* enable OTG ID pin interrupt */
+	temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
+	temp |= OTGSC_INTR_USB_ID_EN;
+	temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN);
+	fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
+
+	return 0;
+}
+
+/*
+ * state file in sysfs
+ */
+static int show_fsl_usb2_otg_state(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct otg_fsm *fsm = &fsl_otg_dev->fsm;
+	char *next = buf;
+	unsigned size = PAGE_SIZE;
+	unsigned long flags;
+	int t;
+
+	spin_lock_irqsave(&fsm->lock, flags);
+
+	/* basic driver infomation */
+	t = scnprintf(next, size,
+			DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n",
+			DRIVER_VERSION);
+	size -= t;
+	next += t;
+
+	/* Registers */
+	t = scnprintf(next, size,
+			"OTGSC:   0x%08x\n"
+			"PORTSC:  0x%08x\n"
+			"USBMODE: 0x%08x\n"
+			"USBCMD:  0x%08x\n"
+			"USBSTS:  0x%08x\n"
+			"USBINTR: 0x%08x\n",
+			fsl_readl(&usb_dr_regs->otgsc),
+			fsl_readl(&usb_dr_regs->portsc),
+			fsl_readl(&usb_dr_regs->usbmode),
+			fsl_readl(&usb_dr_regs->usbcmd),
+			fsl_readl(&usb_dr_regs->usbsts),
+			fsl_readl(&usb_dr_regs->usbintr));
+	size -= t;
+	next += t;
+
+	/* State */
+	t = scnprintf(next, size,
+		      "OTG state: %s\n\n",
+		      otg_state_string(fsl_otg_dev->phy.state));
+	size -= t;
+	next += t;
+
+	/* State Machine Variables */
+	t = scnprintf(next, size,
+			"a_bus_req: %d\n"
+			"b_bus_req: %d\n"
+			"a_bus_resume: %d\n"
+			"a_bus_suspend: %d\n"
+			"a_conn: %d\n"
+			"a_sess_vld: %d\n"
+			"a_srp_det: %d\n"
+			"a_vbus_vld: %d\n"
+			"b_bus_resume: %d\n"
+			"b_bus_suspend: %d\n"
+			"b_conn: %d\n"
+			"b_se0_srp: %d\n"
+			"b_sess_end: %d\n"
+			"b_sess_vld: %d\n"
+			"id: %d\n",
+			fsm->a_bus_req,
+			fsm->b_bus_req,
+			fsm->a_bus_resume,
+			fsm->a_bus_suspend,
+			fsm->a_conn,
+			fsm->a_sess_vld,
+			fsm->a_srp_det,
+			fsm->a_vbus_vld,
+			fsm->b_bus_resume,
+			fsm->b_bus_suspend,
+			fsm->b_conn,
+			fsm->b_se0_srp,
+			fsm->b_sess_end,
+			fsm->b_sess_vld,
+			fsm->id);
+	size -= t;
+	next += t;
+
+	spin_unlock_irqrestore(&fsm->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+
+static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL);
+
+
+/* Char driver interface to control some OTG input */
+
+/*
+ * Handle some ioctl command, such as get otg
+ * status and set host suspend
+ */
+static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	u32 retval = 0;
+
+	switch (cmd) {
+	case GET_OTG_STATUS:
+		retval = fsl_otg_dev->host_working;
+		break;
+
+	case SET_A_SUSPEND_REQ:
+		fsl_otg_dev->fsm.a_suspend_req = arg;
+		break;
+
+	case SET_A_BUS_DROP:
+		fsl_otg_dev->fsm.a_bus_drop = arg;
+		break;
+
+	case SET_A_BUS_REQ:
+		fsl_otg_dev->fsm.a_bus_req = arg;
+		break;
+
+	case SET_B_BUS_REQ:
+		fsl_otg_dev->fsm.b_bus_req = arg;
+		break;
+
+	default:
+		break;
+	}
+
+	otg_statemachine(&fsl_otg_dev->fsm);
+
+	return retval;
+}
+
+static int fsl_otg_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int fsl_otg_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations otg_fops = {
+	.owner = THIS_MODULE,
+	.llseek = NULL,
+	.read = NULL,
+	.write = NULL,
+	.unlocked_ioctl = fsl_otg_ioctl,
+	.open = fsl_otg_open,
+	.release = fsl_otg_release,
+};
+
+static int __devinit fsl_otg_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	if (!pdev->dev.platform_data)
+		return -ENODEV;
+
+	/* configure the OTG */
+	ret = fsl_otg_conf(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't configure OTG module\n");
+		return ret;
+	}
+
+	/* start OTG */
+	ret = usb_otg_start(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't init FSL OTG device\n");
+		return ret;
+	}
+
+	ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register FSL OTG device\n");
+		return ret;
+	}
+
+	ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state);
+	if (ret)
+		dev_warn(&pdev->dev, "Can't register sysfs attribute\n");
+
+	return ret;
+}
+
+static int __devexit fsl_otg_remove(struct platform_device *pdev)
+{
+	struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+	usb_set_transceiver(NULL);
+	free_irq(fsl_otg_dev->irq, fsl_otg_dev);
+
+	iounmap((void *)usb_dr_regs);
+
+	fsl_otg_uninit_timers();
+	kfree(fsl_otg_dev->phy.otg);
+	kfree(fsl_otg_dev);
+
+	device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state);
+
+	unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME);
+
+	if (pdata->exit)
+		pdata->exit(pdev);
+
+	return 0;
+}
+
+struct platform_driver fsl_otg_driver = {
+	.probe = fsl_otg_probe,
+	.remove = __devexit_p(fsl_otg_remove),
+	.driver = {
+		.name = driver_name,
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(fsl_otg_driver);
+
+MODULE_DESCRIPTION(DRIVER_INFO);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/fsl_otg.h b/ap/os/linux/linux-3.4.x/drivers/usb/otg/fsl_otg.h
new file mode 100644
index 0000000..ca26628
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/fsl_otg.h
@@ -0,0 +1,406 @@
+/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "otg_fsm.h"
+#include <linux/usb/otg.h>
+#include <linux/ioctl.h>
+
+/* USB Command Register Bit Masks */
+#define USB_CMD_RUN_STOP		(0x1<<0)
+#define USB_CMD_CTRL_RESET		(0x1<<1)
+#define USB_CMD_PERIODIC_SCHEDULE_EN	(0x1<<4)
+#define USB_CMD_ASYNC_SCHEDULE_EN	(0x1<<5)
+#define USB_CMD_INT_AA_DOORBELL		(0x1<<6)
+#define USB_CMD_ASP			(0x3<<8)
+#define USB_CMD_ASYNC_SCH_PARK_EN	(0x1<<11)
+#define USB_CMD_SUTW			(0x1<<13)
+#define USB_CMD_ATDTW			(0x1<<14)
+#define USB_CMD_ITC			(0xFF<<16)
+
+/* bit 15,3,2 are frame list size */
+#define USB_CMD_FRAME_SIZE_1024		(0x0<<15 | 0x0<<2)
+#define USB_CMD_FRAME_SIZE_512		(0x0<<15 | 0x1<<2)
+#define USB_CMD_FRAME_SIZE_256		(0x0<<15 | 0x2<<2)
+#define USB_CMD_FRAME_SIZE_128		(0x0<<15 | 0x3<<2)
+#define USB_CMD_FRAME_SIZE_64		(0x1<<15 | 0x0<<2)
+#define USB_CMD_FRAME_SIZE_32		(0x1<<15 | 0x1<<2)
+#define USB_CMD_FRAME_SIZE_16		(0x1<<15 | 0x2<<2)
+#define USB_CMD_FRAME_SIZE_8		(0x1<<15 | 0x3<<2)
+
+/* bit 9-8 are async schedule park mode count */
+#define USB_CMD_ASP_00			(0x0<<8)
+#define USB_CMD_ASP_01			(0x1<<8)
+#define USB_CMD_ASP_10			(0x2<<8)
+#define USB_CMD_ASP_11			(0x3<<8)
+#define USB_CMD_ASP_BIT_POS		(8)
+
+/* bit 23-16 are interrupt threshold control */
+#define USB_CMD_ITC_NO_THRESHOLD	(0x00<<16)
+#define USB_CMD_ITC_1_MICRO_FRM		(0x01<<16)
+#define USB_CMD_ITC_2_MICRO_FRM		(0x02<<16)
+#define USB_CMD_ITC_4_MICRO_FRM		(0x04<<16)
+#define USB_CMD_ITC_8_MICRO_FRM		(0x08<<16)
+#define USB_CMD_ITC_16_MICRO_FRM	(0x10<<16)
+#define USB_CMD_ITC_32_MICRO_FRM	(0x20<<16)
+#define USB_CMD_ITC_64_MICRO_FRM	(0x40<<16)
+#define USB_CMD_ITC_BIT_POS		(16)
+
+/* USB Status Register Bit Masks */
+#define USB_STS_INT			(0x1<<0)
+#define USB_STS_ERR			(0x1<<1)
+#define USB_STS_PORT_CHANGE		(0x1<<2)
+#define USB_STS_FRM_LST_ROLL		(0x1<<3)
+#define USB_STS_SYS_ERR			(0x1<<4)
+#define USB_STS_IAA			(0x1<<5)
+#define USB_STS_RESET_RECEIVED		(0x1<<6)
+#define USB_STS_SOF			(0x1<<7)
+#define USB_STS_DCSUSPEND		(0x1<<8)
+#define USB_STS_HC_HALTED		(0x1<<12)
+#define USB_STS_RCL			(0x1<<13)
+#define USB_STS_PERIODIC_SCHEDULE	(0x1<<14)
+#define USB_STS_ASYNC_SCHEDULE		(0x1<<15)
+
+/* USB Interrupt Enable Register Bit Masks */
+#define USB_INTR_INT_EN			(0x1<<0)
+#define USB_INTR_ERR_INT_EN		(0x1<<1)
+#define USB_INTR_PC_DETECT_EN		(0x1<<2)
+#define USB_INTR_FRM_LST_ROLL_EN	(0x1<<3)
+#define USB_INTR_SYS_ERR_EN		(0x1<<4)
+#define USB_INTR_ASYN_ADV_EN		(0x1<<5)
+#define USB_INTR_RESET_EN		(0x1<<6)
+#define USB_INTR_SOF_EN			(0x1<<7)
+#define USB_INTR_DEVICE_SUSPEND		(0x1<<8)
+
+/* Device Address bit masks */
+#define USB_DEVICE_ADDRESS_MASK		(0x7F<<25)
+#define USB_DEVICE_ADDRESS_BIT_POS	(25)
+/* PORTSC  Register Bit Masks,Only one PORT in OTG mode*/
+#define PORTSC_CURRENT_CONNECT_STATUS	(0x1<<0)
+#define PORTSC_CONNECT_STATUS_CHANGE	(0x1<<1)
+#define PORTSC_PORT_ENABLE		(0x1<<2)
+#define PORTSC_PORT_EN_DIS_CHANGE	(0x1<<3)
+#define PORTSC_OVER_CURRENT_ACT		(0x1<<4)
+#define PORTSC_OVER_CUURENT_CHG		(0x1<<5)
+#define PORTSC_PORT_FORCE_RESUME	(0x1<<6)
+#define PORTSC_PORT_SUSPEND		(0x1<<7)
+#define PORTSC_PORT_RESET		(0x1<<8)
+#define PORTSC_LINE_STATUS_BITS		(0x3<<10)
+#define PORTSC_PORT_POWER		(0x1<<12)
+#define PORTSC_PORT_INDICTOR_CTRL	(0x3<<14)
+#define PORTSC_PORT_TEST_CTRL		(0xF<<16)
+#define PORTSC_WAKE_ON_CONNECT_EN	(0x1<<20)
+#define PORTSC_WAKE_ON_CONNECT_DIS	(0x1<<21)
+#define PORTSC_WAKE_ON_OVER_CURRENT	(0x1<<22)
+#define PORTSC_PHY_LOW_POWER_SPD	(0x1<<23)
+#define PORTSC_PORT_FORCE_FULL_SPEED	(0x1<<24)
+#define PORTSC_PORT_SPEED_MASK		(0x3<<26)
+#define PORTSC_TRANSCEIVER_WIDTH	(0x1<<28)
+#define PORTSC_PHY_TYPE_SEL		(0x3<<30)
+/* bit 11-10 are line status */
+#define PORTSC_LINE_STATUS_SE0		(0x0<<10)
+#define PORTSC_LINE_STATUS_JSTATE	(0x1<<10)
+#define PORTSC_LINE_STATUS_KSTATE	(0x2<<10)
+#define PORTSC_LINE_STATUS_UNDEF	(0x3<<10)
+#define PORTSC_LINE_STATUS_BIT_POS	(10)
+
+/* bit 15-14 are port indicator control */
+#define PORTSC_PIC_OFF			(0x0<<14)
+#define PORTSC_PIC_AMBER		(0x1<<14)
+#define PORTSC_PIC_GREEN		(0x2<<14)
+#define PORTSC_PIC_UNDEF		(0x3<<14)
+#define PORTSC_PIC_BIT_POS		(14)
+
+/* bit 19-16 are port test control */
+#define PORTSC_PTC_DISABLE		(0x0<<16)
+#define PORTSC_PTC_JSTATE		(0x1<<16)
+#define PORTSC_PTC_KSTATE		(0x2<<16)
+#define PORTSC_PTC_SEQNAK		(0x3<<16)
+#define PORTSC_PTC_PACKET		(0x4<<16)
+#define PORTSC_PTC_FORCE_EN		(0x5<<16)
+#define PORTSC_PTC_BIT_POS		(16)
+
+/* bit 27-26 are port speed */
+#define PORTSC_PORT_SPEED_FULL		(0x0<<26)
+#define PORTSC_PORT_SPEED_LOW		(0x1<<26)
+#define PORTSC_PORT_SPEED_HIGH		(0x2<<26)
+#define PORTSC_PORT_SPEED_UNDEF		(0x3<<26)
+#define PORTSC_SPEED_BIT_POS		(26)
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define PORTSC_PTW			(0x1<<28)
+#define PORTSC_PTW_8BIT			(0x0<<28)
+#define PORTSC_PTW_16BIT		(0x1<<28)
+
+/* bit 31-30 are port transceiver select */
+#define PORTSC_PTS_UTMI			(0x0<<30)
+#define PORTSC_PTS_ULPI			(0x2<<30)
+#define PORTSC_PTS_FSLS_SERIAL		(0x3<<30)
+#define PORTSC_PTS_BIT_POS		(30)
+
+#define PORTSC_W1C_BITS			\
+	(PORTSC_CONNECT_STATUS_CHANGE |	\
+	 PORTSC_PORT_EN_DIS_CHANGE    |	\
+	 PORTSC_OVER_CUURENT_CHG)
+
+/* OTG Status Control Register Bit Masks */
+#define OTGSC_CTRL_VBUS_DISCHARGE	(0x1<<0)
+#define OTGSC_CTRL_VBUS_CHARGE		(0x1<<1)
+#define OTGSC_CTRL_OTG_TERMINATION	(0x1<<3)
+#define OTGSC_CTRL_DATA_PULSING		(0x1<<4)
+#define OTGSC_CTRL_ID_PULL_EN		(0x1<<5)
+#define OTGSC_HA_DATA_PULSE		(0x1<<6)
+#define OTGSC_HA_BA			(0x1<<7)
+#define OTGSC_STS_USB_ID		(0x1<<8)
+#define OTGSC_STS_A_VBUS_VALID		(0x1<<9)
+#define OTGSC_STS_A_SESSION_VALID	(0x1<<10)
+#define OTGSC_STS_B_SESSION_VALID	(0x1<<11)
+#define OTGSC_STS_B_SESSION_END		(0x1<<12)
+#define OTGSC_STS_1MS_TOGGLE		(0x1<<13)
+#define OTGSC_STS_DATA_PULSING		(0x1<<14)
+#define OTGSC_INTSTS_USB_ID		(0x1<<16)
+#define OTGSC_INTSTS_A_VBUS_VALID	(0x1<<17)
+#define OTGSC_INTSTS_A_SESSION_VALID	(0x1<<18)
+#define OTGSC_INTSTS_B_SESSION_VALID	(0x1<<19)
+#define OTGSC_INTSTS_B_SESSION_END	(0x1<<20)
+#define OTGSC_INTSTS_1MS		(0x1<<21)
+#define OTGSC_INTSTS_DATA_PULSING	(0x1<<22)
+#define OTGSC_INTR_USB_ID_EN		(0x1<<24)
+#define OTGSC_INTR_A_VBUS_VALID_EN	(0x1<<25)
+#define OTGSC_INTR_A_SESSION_VALID_EN	(0x1<<26)
+#define OTGSC_INTR_B_SESSION_VALID_EN	(0x1<<27)
+#define OTGSC_INTR_B_SESSION_END_EN	(0x1<<28)
+#define OTGSC_INTR_1MS_TIMER_EN		(0x1<<29)
+#define OTGSC_INTR_DATA_PULSING_EN	(0x1<<30)
+#define OTGSC_INTSTS_MASK		(0x00ff0000)
+
+/* USB MODE Register Bit Masks */
+#define  USB_MODE_CTRL_MODE_IDLE	(0x0<<0)
+#define  USB_MODE_CTRL_MODE_DEVICE	(0x2<<0)
+#define  USB_MODE_CTRL_MODE_HOST	(0x3<<0)
+#define  USB_MODE_CTRL_MODE_RSV		(0x1<<0)
+#define  USB_MODE_SETUP_LOCK_OFF	(0x1<<3)
+#define  USB_MODE_STREAM_DISABLE	(0x1<<4)
+#define  USB_MODE_ES			(0x1<<2) /* Endian Select */
+
+/* control Register Bit Masks */
+#define  USB_CTRL_IOENB			(0x1<<2)
+#define  USB_CTRL_ULPI_INT0EN		(0x1<<0)
+
+/* BCSR5 */
+#define BCSR5_INT_USB			(0x02)
+
+/* USB module clk cfg */
+#define SCCR_OFFS			(0xA08)
+#define SCCR_USB_CLK_DISABLE		(0x00000000)	/* USB clk disable */
+#define SCCR_USB_MPHCM_11		(0x00c00000)
+#define SCCR_USB_MPHCM_01		(0x00400000)
+#define SCCR_USB_MPHCM_10		(0x00800000)
+#define SCCR_USB_DRCM_11		(0x00300000)
+#define SCCR_USB_DRCM_01		(0x00100000)
+#define SCCR_USB_DRCM_10		(0x00200000)
+
+#define SICRL_OFFS			(0x114)
+#define SICRL_USB0			(0x40000000)
+#define SICRL_USB1			(0x20000000)
+
+#define SICRH_OFFS			(0x118)
+#define SICRH_USB_UTMI			(0x00020000)
+
+/* OTG interrupt enable bit masks */
+#define  OTGSC_INTERRUPT_ENABLE_BITS_MASK  \
+	(OTGSC_INTR_USB_ID_EN            | \
+	OTGSC_INTR_1MS_TIMER_EN		 | \
+	OTGSC_INTR_A_VBUS_VALID_EN       | \
+	OTGSC_INTR_A_SESSION_VALID_EN    | \
+	OTGSC_INTR_B_SESSION_VALID_EN    | \
+	OTGSC_INTR_B_SESSION_END_EN      | \
+	OTGSC_INTR_DATA_PULSING_EN)
+
+/* OTG interrupt status bit masks */
+#define  OTGSC_INTERRUPT_STATUS_BITS_MASK  \
+	(OTGSC_INTSTS_USB_ID          |    \
+	OTGSC_INTR_1MS_TIMER_EN       |    \
+	OTGSC_INTSTS_A_VBUS_VALID     |    \
+	OTGSC_INTSTS_A_SESSION_VALID  |    \
+	OTGSC_INTSTS_B_SESSION_VALID  |    \
+	OTGSC_INTSTS_B_SESSION_END    |    \
+	OTGSC_INTSTS_DATA_PULSING)
+
+/*
+ *  A-DEVICE timing  constants
+ */
+
+/* Wait for VBUS Rise  */
+#define TA_WAIT_VRISE	(100)	/* a_wait_vrise 100 ms, section: 6.6.5.1 */
+
+/* Wait for B-Connect */
+#define TA_WAIT_BCON	(10000)  /* a_wait_bcon > 1 sec, section: 6.6.5.2
+				  * This is only used to get out of
+				  * OTG_STATE_A_WAIT_BCON state if there was
+				  * no connection for these many milliseconds
+				  */
+
+/* A-Idle to B-Disconnect */
+/* It is necessary for this timer to be more than 750 ms because of a bug in OPT
+ * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated
+ * in the test description
+ */
+#define TA_AIDL_BDIS	(5000)	/* a_suspend minimum 200 ms, section: 6.6.5.3 */
+
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS	(12)	/* 3 to 200 ms */
+
+/* B-device timing constants */
+
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS	(10)	/* b_srp_init,continue 5~10ms, section:5.3.3 */
+#define TB_DATA_PLS_MIN	(5)	/* minimum 5 ms */
+#define TB_DATA_PLS_MAX	(10)	/* maximum 10 ms */
+
+/* SRP Initiate Time  */
+#define TB_SRP_INIT	(100)	/* b_srp_init,maximum 100 ms, section:5.3.8 */
+
+/* SRP Fail Time  */
+#define TB_SRP_FAIL	(7000)	/* b_srp_init,Fail time 5~30s, section:6.8.2.2*/
+
+/* SRP result wait time */
+#define TB_SRP_WAIT	(60)
+
+/* VBus time */
+#define TB_VBUS_PLS	(30)	/* time to keep vbus pulsing asserted */
+
+/* Discharge time */
+/* This time should be less than 10ms. It varies from system to system. */
+#define TB_VBUS_DSCHRG	(8)
+
+/* A-SE0 to B-Reset  */
+#define TB_ASE0_BRST	(20)	/* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */
+
+/* A bus suspend timer before we can switch to b_wait_aconn */
+#define TB_A_SUSPEND	(7)
+#define TB_BUS_RESUME	(12)
+
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP	(2)	/* b_idle,minimum 2 ms, section:5.3.2 */
+
+#define SET_OTG_STATE(otg_ptr, newstate)	((otg_ptr)->state = newstate)
+
+struct usb_dr_mmap {
+	/* Capability register */
+	u8 res1[256];
+	u16 caplength;		/* Capability Register Length */
+	u16 hciversion;		/* Host Controller Interface Version */
+	u32 hcsparams;		/* Host Controller Structual Parameters */
+	u32 hccparams;		/* Host Controller Capability Parameters */
+	u8 res2[20];
+	u32 dciversion;		/* Device Controller Interface Version */
+	u32 dccparams;		/* Device Controller Capability Parameters */
+	u8 res3[24];
+	/* Operation register */
+	u32 usbcmd;		/* USB Command Register */
+	u32 usbsts;		/* USB Status Register */
+	u32 usbintr;		/* USB Interrupt Enable Register */
+	u32 frindex;		/* Frame Index Register */
+	u8 res4[4];
+	u32 deviceaddr;		/* Device Address */
+	u32 endpointlistaddr;	/* Endpoint List Address Register */
+	u8 res5[4];
+	u32 burstsize;		/* Master Interface Data Burst Size Register */
+	u32 txttfilltuning;	/* Transmit FIFO Tuning Controls Register */
+	u8 res6[8];
+	u32 ulpiview;		/* ULPI register access */
+	u8 res7[12];
+	u32 configflag;		/* Configure Flag Register */
+	u32 portsc;		/* Port 1 Status and Control Register */
+	u8 res8[28];
+	u32 otgsc;		/* On-The-Go Status and Control */
+	u32 usbmode;		/* USB Mode Register */
+	u32 endptsetupstat;	/* Endpoint Setup Status Register */
+	u32 endpointprime;	/* Endpoint Initialization Register */
+	u32 endptflush;		/* Endpoint Flush Register */
+	u32 endptstatus;	/* Endpoint Status Register */
+	u32 endptcomplete;	/* Endpoint Complete Register */
+	u32 endptctrl[6];	/* Endpoint Control Registers */
+	u8 res9[552];
+	u32 snoop1;
+	u32 snoop2;
+	u32 age_cnt_thresh;	/* Age Count Threshold Register */
+	u32 pri_ctrl;		/* Priority Control Register */
+	u32 si_ctrl;		/* System Interface Control Register */
+	u8 res10[236];
+	u32 control;		/* General Purpose Control Register */
+};
+
+struct fsl_otg_timer {
+	unsigned long expires;	/* Number of count increase to timeout */
+	unsigned long count;	/* Tick counter */
+	void (*function)(unsigned long);	/* Timeout function */
+	unsigned long data;	/* Data passed to function */
+	struct list_head list;
+};
+
+inline struct fsl_otg_timer *otg_timer_initializer
+(void (*function)(unsigned long), unsigned long expires, unsigned long data)
+{
+	struct fsl_otg_timer *timer;
+
+	timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL);
+	if (!timer)
+		return NULL;
+	timer->function = function;
+	timer->expires = expires;
+	timer->data = data;
+	return timer;
+}
+
+struct fsl_otg {
+	struct usb_phy phy;
+	struct otg_fsm fsm;
+	struct usb_dr_mmap *dr_mem_map;
+	struct delayed_work otg_event;
+
+	/* used for usb host */
+	struct work_struct work_wq;
+	u8	host_working;
+
+	int irq;
+};
+
+struct fsl_otg_config {
+	u8 otg_port;
+};
+
+/* For SRP and HNP handle */
+#define FSL_OTG_MAJOR		240
+#define FSL_OTG_NAME		"fsl-usb2-otg"
+/* Command to OTG driver ioctl */
+#define OTG_IOCTL_MAGIC		FSL_OTG_MAJOR
+/* if otg work as host, it should return 1, otherwise return 0 */
+#define GET_OTG_STATUS		_IOR(OTG_IOCTL_MAGIC, 1, int)
+#define SET_A_SUSPEND_REQ	_IOW(OTG_IOCTL_MAGIC, 2, int)
+#define SET_A_BUS_DROP		_IOW(OTG_IOCTL_MAGIC, 3, int)
+#define SET_A_BUS_REQ		_IOW(OTG_IOCTL_MAGIC, 4, int)
+#define SET_B_BUS_REQ		_IOW(OTG_IOCTL_MAGIC, 5, int)
+#define GET_A_SUSPEND_REQ	_IOR(OTG_IOCTL_MAGIC, 6, int)
+#define GET_A_BUS_DROP		_IOR(OTG_IOCTL_MAGIC, 7, int)
+#define GET_A_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 8, int)
+#define GET_B_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 9, int)
+
+void fsl_otg_add_timer(void *timer);
+void fsl_otg_del_timer(void *timer);
+void fsl_otg_pulse_vbus(void);
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/gpio_vbus.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/gpio_vbus.c
new file mode 100644
index 0000000..fe20864
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/gpio_vbus.c
@@ -0,0 +1,381 @@
+/*
+ * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices
+ *
+ * Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/gpio_vbus.h>
+#include <linux/usb/otg.h>
+
+
+/*
+ * A simple GPIO VBUS sensing driver for B peripheral only devices
+ * with internal transceivers. It can control a D+ pullup GPIO and
+ * a regulator to limit the current drawn from VBUS.
+ *
+ * Needs to be loaded before the UDC driver that will use it.
+ */
+struct gpio_vbus_data {
+	struct usb_phy		phy;
+	struct device          *dev;
+	struct regulator       *vbus_draw;
+	int			vbus_draw_enabled;
+	unsigned		mA;
+	struct delayed_work	work;
+};
+
+
+/*
+ * This driver relies on "both edges" triggering.  VBUS has 100 msec to
+ * stabilize, so the peripheral controller driver may need to cope with
+ * some bouncing due to current surges (e.g. charging local capacitance)
+ * and contact chatter.
+ *
+ * REVISIT in desperate straits, toggling between rising and falling
+ * edges might be workable.
+ */
+#define VBUS_IRQ_FLAGS \
+	( IRQF_SAMPLE_RANDOM | IRQF_SHARED \
+	| IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING )
+
+
+/* interface to regulator framework */
+static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
+{
+	struct regulator *vbus_draw = gpio_vbus->vbus_draw;
+	int enabled;
+
+	if (!vbus_draw)
+		return;
+
+	enabled = gpio_vbus->vbus_draw_enabled;
+	if (mA) {
+		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
+		if (!enabled) {
+			regulator_enable(vbus_draw);
+			gpio_vbus->vbus_draw_enabled = 1;
+		}
+	} else {
+		if (enabled) {
+			regulator_disable(vbus_draw);
+			gpio_vbus->vbus_draw_enabled = 0;
+		}
+	}
+	gpio_vbus->mA = mA;
+}
+
+static int is_vbus_powered(struct gpio_vbus_mach_info *pdata)
+{
+	int vbus;
+
+	vbus = gpio_get_value(pdata->gpio_vbus);
+	if (pdata->gpio_vbus_inverted)
+		vbus = !vbus;
+
+	return vbus;
+}
+
+static void gpio_vbus_work(struct work_struct *work)
+{
+	struct gpio_vbus_data *gpio_vbus =
+		container_of(work, struct gpio_vbus_data, work.work);
+	struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data;
+	int gpio, status;
+
+	if (!gpio_vbus->phy.otg->gadget)
+		return;
+
+	/* Peripheral controllers which manage the pullup themselves won't have
+	 * gpio_pullup configured here.  If it's configured here, we'll do what
+	 * isp1301_omap::b_peripheral() does and enable the pullup here... although
+	 * that may complicate usb_gadget_{,dis}connect() support.
+	 */
+	gpio = pdata->gpio_pullup;
+	if (is_vbus_powered(pdata)) {
+		status = USB_EVENT_VBUS;
+		gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL;
+		gpio_vbus->phy.last_event = status;
+		usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget);
+
+		/* drawing a "unit load" is *always* OK, except for OTG */
+		set_vbus_draw(gpio_vbus, 100);
+
+		/* optionally enable D+ pullup */
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, !pdata->gpio_pullup_inverted);
+
+		atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
+					   status, gpio_vbus->phy.otg->gadget);
+	} else {
+		/* optionally disable D+ pullup */
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, pdata->gpio_pullup_inverted);
+
+		set_vbus_draw(gpio_vbus, 0);
+
+		usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget);
+		status = USB_EVENT_NONE;
+		gpio_vbus->phy.state = OTG_STATE_B_IDLE;
+		gpio_vbus->phy.last_event = status;
+
+		atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
+					   status, gpio_vbus->phy.otg->gadget);
+	}
+}
+
+/* VBUS change IRQ handler */
+static irqreturn_t gpio_vbus_irq(int irq, void *data)
+{
+	struct platform_device *pdev = data;
+	struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
+	struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
+	struct usb_otg *otg = gpio_vbus->phy.otg;
+
+	dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
+		is_vbus_powered(pdata) ? "supplied" : "inactive",
+		otg->gadget ? otg->gadget->name : "none");
+
+	if (otg->gadget)
+		schedule_delayed_work(&gpio_vbus->work, msecs_to_jiffies(100));
+
+	return IRQ_HANDLED;
+}
+
+/* OTG transceiver interface */
+
+/* bind/unbind the peripheral controller */
+static int gpio_vbus_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct gpio_vbus_data *gpio_vbus;
+	struct gpio_vbus_mach_info *pdata;
+	struct platform_device *pdev;
+	int gpio, irq;
+
+	gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy);
+	pdev = to_platform_device(gpio_vbus->dev);
+	pdata = gpio_vbus->dev->platform_data;
+	irq = gpio_to_irq(pdata->gpio_vbus);
+	gpio = pdata->gpio_pullup;
+
+	if (!gadget) {
+		dev_dbg(&pdev->dev, "unregistering gadget '%s'\n",
+			otg->gadget->name);
+
+		/* optionally disable D+ pullup */
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, pdata->gpio_pullup_inverted);
+
+		set_vbus_draw(gpio_vbus, 0);
+
+		usb_gadget_vbus_disconnect(otg->gadget);
+		otg->phy->state = OTG_STATE_UNDEFINED;
+
+		otg->gadget = NULL;
+		return 0;
+	}
+
+	otg->gadget = gadget;
+	dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name);
+
+	/* initialize connection state */
+	gpio_vbus_irq(irq, pdev);
+	return 0;
+}
+
+/* effective for B devices, ignored for A-peripheral */
+static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA)
+{
+	struct gpio_vbus_data *gpio_vbus;
+
+	gpio_vbus = container_of(phy, struct gpio_vbus_data, phy);
+
+	if (phy->state == OTG_STATE_B_PERIPHERAL)
+		set_vbus_draw(gpio_vbus, mA);
+	return 0;
+}
+
+/* for non-OTG B devices: set/clear transceiver suspend mode */
+static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend)
+{
+	struct gpio_vbus_data *gpio_vbus;
+
+	gpio_vbus = container_of(phy, struct gpio_vbus_data, phy);
+
+	/* draw max 0 mA from vbus in suspend mode; or the previously
+	 * recorded amount of current if not suspended
+	 *
+	 * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA
+	 * if they're wake-enabled ... we don't handle that yet.
+	 */
+	return gpio_vbus_set_power(phy, suspend ? 0 : gpio_vbus->mA);
+}
+
+/* platform driver interface */
+
+static int __init gpio_vbus_probe(struct platform_device *pdev)
+{
+	struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
+	struct gpio_vbus_data *gpio_vbus;
+	struct resource *res;
+	int err, gpio, irq;
+
+	if (!pdata || !gpio_is_valid(pdata->gpio_vbus))
+		return -EINVAL;
+	gpio = pdata->gpio_vbus;
+
+	gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL);
+	if (!gpio_vbus)
+		return -ENOMEM;
+
+	gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+	if (!gpio_vbus->phy.otg) {
+		kfree(gpio_vbus);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, gpio_vbus);
+	gpio_vbus->dev = &pdev->dev;
+	gpio_vbus->phy.label = "gpio-vbus";
+	gpio_vbus->phy.set_power = gpio_vbus_set_power;
+	gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend;
+	gpio_vbus->phy.state = OTG_STATE_UNDEFINED;
+
+	gpio_vbus->phy.otg->phy = &gpio_vbus->phy;
+	gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral;
+
+	err = gpio_request(gpio, "vbus_detect");
+	if (err) {
+		dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
+			gpio, err);
+		goto err_gpio;
+	}
+	gpio_direction_input(gpio);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res) {
+		irq = res->start;
+		res->flags &= IRQF_TRIGGER_MASK;
+		res->flags |= IRQF_SAMPLE_RANDOM | IRQF_SHARED;
+	} else
+		irq = gpio_to_irq(gpio);
+
+	/* if data line pullup is in use, initialize it to "not pulling up" */
+	gpio = pdata->gpio_pullup;
+	if (gpio_is_valid(gpio)) {
+		err = gpio_request(gpio, "udc_pullup");
+		if (err) {
+			dev_err(&pdev->dev,
+				"can't request pullup gpio %d, err: %d\n",
+				gpio, err);
+			gpio_free(pdata->gpio_vbus);
+			goto err_gpio;
+		}
+		gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
+	}
+
+	err = request_irq(irq, gpio_vbus_irq, VBUS_IRQ_FLAGS,
+		"vbus_detect", pdev);
+	if (err) {
+		dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+			irq, err);
+		goto err_irq;
+	}
+
+	ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier);
+
+	INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work);
+
+	gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw");
+	if (IS_ERR(gpio_vbus->vbus_draw)) {
+		dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n",
+			PTR_ERR(gpio_vbus->vbus_draw));
+		gpio_vbus->vbus_draw = NULL;
+	}
+
+	/* only active when a gadget is registered */
+	err = usb_set_transceiver(&gpio_vbus->phy);
+	if (err) {
+		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
+			err);
+		goto err_otg;
+	}
+
+	return 0;
+err_otg:
+	free_irq(irq, &pdev->dev);
+err_irq:
+	if (gpio_is_valid(pdata->gpio_pullup))
+		gpio_free(pdata->gpio_pullup);
+	gpio_free(pdata->gpio_vbus);
+err_gpio:
+	platform_set_drvdata(pdev, NULL);
+	kfree(gpio_vbus->phy.otg);
+	kfree(gpio_vbus);
+	return err;
+}
+
+static int __exit gpio_vbus_remove(struct platform_device *pdev)
+{
+	struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
+	struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
+	int gpio = pdata->gpio_vbus;
+
+	regulator_put(gpio_vbus->vbus_draw);
+
+	usb_set_transceiver(NULL);
+
+	free_irq(gpio_to_irq(gpio), &pdev->dev);
+	if (gpio_is_valid(pdata->gpio_pullup))
+		gpio_free(pdata->gpio_pullup);
+	gpio_free(gpio);
+	platform_set_drvdata(pdev, NULL);
+	kfree(gpio_vbus->phy.otg);
+	kfree(gpio_vbus);
+
+	return 0;
+}
+
+/* NOTE:  the gpio-vbus device may *NOT* be hotplugged */
+
+MODULE_ALIAS("platform:gpio-vbus");
+
+static struct platform_driver gpio_vbus_driver = {
+	.driver = {
+		.name  = "gpio-vbus",
+		.owner = THIS_MODULE,
+	},
+	.remove  = __exit_p(gpio_vbus_remove),
+};
+
+static int __init gpio_vbus_init(void)
+{
+	return platform_driver_probe(&gpio_vbus_driver, gpio_vbus_probe);
+}
+module_init(gpio_vbus_init);
+
+static void __exit gpio_vbus_exit(void)
+{
+	platform_driver_unregister(&gpio_vbus_driver);
+}
+module_exit(gpio_vbus_exit);
+
+MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver");
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/isp1301_omap.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/isp1301_omap.c
new file mode 100644
index 0000000..70cf5d7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/isp1301_omap.c
@@ -0,0 +1,1656 @@
+/*
+ * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <plat/usb.h>
+#include <plat/mux.h>
+
+
+#ifndef	DEBUG
+#undef	VERBOSE
+#endif
+
+
+#define	DRIVER_VERSION	"24 August 2004"
+#define	DRIVER_NAME	(isp1301_driver.driver.name)
+
+MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver");
+MODULE_LICENSE("GPL");
+
+struct isp1301 {
+	struct usb_phy		phy;
+	struct i2c_client	*client;
+	void			(*i2c_release)(struct device *dev);
+
+	int			irq_type;
+
+	u32			last_otg_ctrl;
+	unsigned		working:1;
+
+	struct timer_list	timer;
+
+	/* use keventd context to change the state for us */
+	struct work_struct	work;
+
+	unsigned long		todo;
+#		define WORK_UPDATE_ISP	0	/* update ISP from OTG */
+#		define WORK_UPDATE_OTG	1	/* update OTG from ISP */
+#		define WORK_HOST_RESUME	4	/* resume host */
+#		define WORK_TIMER	6	/* timer fired */
+#		define WORK_STOP	7	/* don't resubmit */
+};
+
+
+/* bits in OTG_CTRL */
+
+#define	OTG_XCEIV_OUTPUTS \
+	(OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID)
+#define	OTG_XCEIV_INPUTS \
+	(OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID)
+#define	OTG_CTRL_BITS \
+	(OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP)
+	/* and OTG_PULLUP is sometimes written */
+
+#define	OTG_CTRL_MASK	(OTG_DRIVER_SEL| \
+	OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \
+	OTG_CTRL_BITS)
+
+
+/*-------------------------------------------------------------------------*/
+
+/* board-specific PM hooks */
+
+#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
+
+#if	defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE)
+
+#include <linux/i2c/tps65010.h>
+
+#else
+
+static inline int tps65010_set_vbus_draw(unsigned mA)
+{
+	pr_debug("tps65010: draw %d mA (STUB)\n", mA);
+	return 0;
+}
+
+#endif
+
+static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
+{
+	int status = tps65010_set_vbus_draw(mA);
+	if (status < 0)
+		pr_debug("  VBUS %d mA error %d\n", mA, status);
+}
+
+#else
+
+static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
+{
+	/* H4 controls this by DIP switch S2.4; no soft control.
+	 * ON means the charger is always enabled.  Leave it OFF
+	 * unless the OTG port is used only in B-peripheral mode.
+	 */
+}
+
+#endif
+
+static void enable_vbus_source(struct isp1301 *isp)
+{
+	/* this board won't supply more than 8mA vbus power.
+	 * some boards can switch a 100ma "unit load" (or more).
+	 */
+}
+
+
+/* products will deliver OTG messages with LEDs, GUI, etc */
+static inline void notresponding(struct isp1301 *isp)
+{
+	printk(KERN_NOTICE "OTG device not responding.\n");
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver isp1301_driver;
+
+/* smbus apis are used for portability */
+
+static inline u8
+isp1301_get_u8(struct isp1301 *isp, u8 reg)
+{
+	return i2c_smbus_read_byte_data(isp->client, reg + 0);
+}
+
+static inline int
+isp1301_get_u16(struct isp1301 *isp, u8 reg)
+{
+	return i2c_smbus_read_word_data(isp->client, reg);
+}
+
+static inline int
+isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits)
+{
+	return i2c_smbus_write_byte_data(isp->client, reg + 0, bits);
+}
+
+static inline int
+isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits)
+{
+	return i2c_smbus_write_byte_data(isp->client, reg + 1, bits);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* identification */
+#define	ISP1301_VENDOR_ID		0x00	/* u16 read */
+#define	ISP1301_PRODUCT_ID		0x02	/* u16 read */
+#define	ISP1301_BCD_DEVICE		0x14	/* u16 read */
+
+#define	I2C_VENDOR_ID_PHILIPS		0x04cc
+#define	I2C_PRODUCT_ID_PHILIPS_1301	0x1301
+
+/* operational registers */
+#define	ISP1301_MODE_CONTROL_1		0x04	/* u8 read, set, +1 clear */
+#	define	MC1_SPEED		(1 << 0)
+#	define	MC1_SUSPEND		(1 << 1)
+#	define	MC1_DAT_SE0		(1 << 2)
+#	define	MC1_TRANSPARENT		(1 << 3)
+#	define	MC1_BDIS_ACON_EN	(1 << 4)
+#	define	MC1_OE_INT_EN		(1 << 5)
+#	define	MC1_UART_EN		(1 << 6)
+#	define	MC1_MASK		0x7f
+#define	ISP1301_MODE_CONTROL_2		0x12	/* u8 read, set, +1 clear */
+#	define	MC2_GLOBAL_PWR_DN	(1 << 0)
+#	define	MC2_SPD_SUSP_CTRL	(1 << 1)
+#	define	MC2_BI_DI		(1 << 2)
+#	define	MC2_TRANSP_BDIR0	(1 << 3)
+#	define	MC2_TRANSP_BDIR1	(1 << 4)
+#	define	MC2_AUDIO_EN		(1 << 5)
+#	define	MC2_PSW_EN		(1 << 6)
+#	define	MC2_EN2V7		(1 << 7)
+#define	ISP1301_OTG_CONTROL_1		0x06	/* u8 read, set, +1 clear */
+#	define	OTG1_DP_PULLUP		(1 << 0)
+#	define	OTG1_DM_PULLUP		(1 << 1)
+#	define	OTG1_DP_PULLDOWN	(1 << 2)
+#	define	OTG1_DM_PULLDOWN	(1 << 3)
+#	define	OTG1_ID_PULLDOWN	(1 << 4)
+#	define	OTG1_VBUS_DRV		(1 << 5)
+#	define	OTG1_VBUS_DISCHRG	(1 << 6)
+#	define	OTG1_VBUS_CHRG		(1 << 7)
+#define	ISP1301_OTG_STATUS		0x10	/* u8 readonly */
+#	define	OTG_B_SESS_END		(1 << 6)
+#	define	OTG_B_SESS_VLD		(1 << 7)
+
+#define	ISP1301_INTERRUPT_SOURCE	0x08	/* u8 read */
+#define	ISP1301_INTERRUPT_LATCH		0x0A	/* u8 read, set, +1 clear */
+
+#define	ISP1301_INTERRUPT_FALLING	0x0C	/* u8 read, set, +1 clear */
+#define	ISP1301_INTERRUPT_RISING	0x0E	/* u8 read, set, +1 clear */
+
+/* same bitfields in all interrupt registers */
+#	define	INTR_VBUS_VLD		(1 << 0)
+#	define	INTR_SESS_VLD		(1 << 1)
+#	define	INTR_DP_HI		(1 << 2)
+#	define	INTR_ID_GND		(1 << 3)
+#	define	INTR_DM_HI		(1 << 4)
+#	define	INTR_ID_FLOAT		(1 << 5)
+#	define	INTR_BDIS_ACON		(1 << 6)
+#	define	INTR_CR_INT		(1 << 7)
+
+/*-------------------------------------------------------------------------*/
+
+static inline const char *state_name(struct isp1301 *isp)
+{
+	return otg_state_string(isp->phy.state);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NOTE:  some of this ISP1301 setup is specific to H2 boards;
+ * not everything is guarded by board-specific checks, or even using
+ * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI.
+ *
+ * ALSO:  this currently doesn't use ISP1301 low-power modes
+ * while OTG is running.
+ */
+
+static void power_down(struct isp1301 *isp)
+{
+	isp->phy.state = OTG_STATE_UNDEFINED;
+
+	// isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
+
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN);
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+}
+
+static void power_up(struct isp1301 *isp)
+{
+	// isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
+
+	/* do this only when cpu is driving transceiver,
+	 * so host won't see a low speed device...
+	 */
+	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+}
+
+#define	NO_HOST_SUSPEND
+
+static int host_suspend(struct isp1301 *isp)
+{
+#ifdef	NO_HOST_SUSPEND
+	return 0;
+#else
+	struct device	*dev;
+
+	if (!isp->phy.otg->host)
+		return -ENODEV;
+
+	/* Currently ASSUMES only the OTG port matters;
+	 * other ports could be active...
+	 */
+	dev = isp->phy.otg->host->controller;
+	return dev->driver->suspend(dev, 3, 0);
+#endif
+}
+
+static int host_resume(struct isp1301 *isp)
+{
+#ifdef	NO_HOST_SUSPEND
+	return 0;
+#else
+	struct device	*dev;
+
+	if (!isp->phy.otg->host)
+		return -ENODEV;
+
+	dev = isp->phy.otg->host->controller;
+	return dev->driver->resume(dev, 0);
+#endif
+}
+
+static int gadget_suspend(struct isp1301 *isp)
+{
+	isp->phy.otg->gadget->b_hnp_enable = 0;
+	isp->phy.otg->gadget->a_hnp_support = 0;
+	isp->phy.otg->gadget->a_alt_hnp_support = 0;
+	return usb_gadget_vbus_disconnect(isp->phy.otg->gadget);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define	TIMER_MINUTES	10
+#define	TIMER_JIFFIES	(TIMER_MINUTES * 60 * HZ)
+
+/* Almost all our I2C messaging comes from a work queue's task context.
+ * NOTE: guaranteeing certain response times might mean we shouldn't
+ * share keventd's work queue; a realtime task might be safest.
+ */
+static void isp1301_defer_work(struct isp1301 *isp, int work)
+{
+	int status;
+
+	if (isp && !test_and_set_bit(work, &isp->todo)) {
+		(void) get_device(&isp->client->dev);
+		status = schedule_work(&isp->work);
+		if (!status && !isp->working)
+			dev_vdbg(&isp->client->dev,
+				"work item %d may be lost\n", work);
+	}
+}
+
+/* called from irq handlers */
+static void a_idle(struct isp1301 *isp, const char *tag)
+{
+	u32 l;
+
+	if (isp->phy.state == OTG_STATE_A_IDLE)
+		return;
+
+	isp->phy.otg->default_a = 1;
+	if (isp->phy.otg->host) {
+		isp->phy.otg->host->is_b_host = 0;
+		host_suspend(isp);
+	}
+	if (isp->phy.otg->gadget) {
+		isp->phy.otg->gadget->is_a_peripheral = 1;
+		gadget_suspend(isp);
+	}
+	isp->phy.state = OTG_STATE_A_IDLE;
+	l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+	omap_writel(l, OTG_CTRL);
+	isp->last_otg_ctrl = l;
+	pr_debug("  --> %s/%s\n", state_name(isp), tag);
+}
+
+/* called from irq handlers */
+static void b_idle(struct isp1301 *isp, const char *tag)
+{
+	u32 l;
+
+	if (isp->phy.state == OTG_STATE_B_IDLE)
+		return;
+
+	isp->phy.otg->default_a = 0;
+	if (isp->phy.otg->host) {
+		isp->phy.otg->host->is_b_host = 1;
+		host_suspend(isp);
+	}
+	if (isp->phy.otg->gadget) {
+		isp->phy.otg->gadget->is_a_peripheral = 0;
+		gadget_suspend(isp);
+	}
+	isp->phy.state = OTG_STATE_B_IDLE;
+	l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+	omap_writel(l, OTG_CTRL);
+	isp->last_otg_ctrl = l;
+	pr_debug("  --> %s/%s\n", state_name(isp), tag);
+}
+
+static void
+dump_regs(struct isp1301 *isp, const char *label)
+{
+#ifdef	DEBUG
+	u8	ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1);
+	u8	status = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+	u8	src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
+
+	pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n",
+		omap_readl(OTG_CTRL), label, state_name(isp),
+		ctrl, status, src);
+	/* mode control and irq enables don't change much */
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_USB_OTG
+
+/*
+ * The OMAP OTG controller handles most of the OTG state transitions.
+ *
+ * We translate isp1301 outputs (mostly voltage comparator status) into
+ * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state
+ * flags into isp1301 inputs ... and infer state transitions.
+ */
+
+#ifdef	VERBOSE
+
+static void check_state(struct isp1301 *isp, const char *tag)
+{
+	enum usb_otg_state	state = OTG_STATE_UNDEFINED;
+	u8			fsm = omap_readw(OTG_TEST) & 0x0ff;
+	unsigned		extra = 0;
+
+	switch (fsm) {
+
+	/* default-b */
+	case 0x0:
+		state = OTG_STATE_B_IDLE;
+		break;
+	case 0x3:
+	case 0x7:
+		extra = 1;
+	case 0x1:
+		state = OTG_STATE_B_PERIPHERAL;
+		break;
+	case 0x11:
+		state = OTG_STATE_B_SRP_INIT;
+		break;
+
+	/* extra dual-role default-b states */
+	case 0x12:
+	case 0x13:
+	case 0x16:
+		extra = 1;
+	case 0x17:
+		state = OTG_STATE_B_WAIT_ACON;
+		break;
+	case 0x34:
+		state = OTG_STATE_B_HOST;
+		break;
+
+	/* default-a */
+	case 0x36:
+		state = OTG_STATE_A_IDLE;
+		break;
+	case 0x3c:
+		state = OTG_STATE_A_WAIT_VFALL;
+		break;
+	case 0x7d:
+		state = OTG_STATE_A_VBUS_ERR;
+		break;
+	case 0x9e:
+	case 0x9f:
+		extra = 1;
+	case 0x89:
+		state = OTG_STATE_A_PERIPHERAL;
+		break;
+	case 0xb7:
+		state = OTG_STATE_A_WAIT_VRISE;
+		break;
+	case 0xb8:
+		state = OTG_STATE_A_WAIT_BCON;
+		break;
+	case 0xb9:
+		state = OTG_STATE_A_HOST;
+		break;
+	case 0xba:
+		state = OTG_STATE_A_SUSPEND;
+		break;
+	default:
+		break;
+	}
+	if (isp->phy.state == state && !extra)
+		return;
+	pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag,
+		otg_state_string(state), fsm, state_name(isp),
+		omap_readl(OTG_CTRL));
+}
+
+#else
+
+static inline void check_state(struct isp1301 *isp, const char *tag) { }
+
+#endif
+
+/* outputs from ISP1301_INTERRUPT_SOURCE */
+static void update_otg1(struct isp1301 *isp, u8 int_src)
+{
+	u32	otg_ctrl;
+
+	otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+	otg_ctrl &= ~OTG_XCEIV_INPUTS;
+	otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD);
+
+	if (int_src & INTR_SESS_VLD)
+		otg_ctrl |= OTG_ASESSVLD;
+	else if (isp->phy.state == OTG_STATE_A_WAIT_VFALL) {
+		a_idle(isp, "vfall");
+		otg_ctrl &= ~OTG_CTRL_BITS;
+	}
+	if (int_src & INTR_VBUS_VLD)
+		otg_ctrl |= OTG_VBUSVLD;
+	if (int_src & INTR_ID_GND) {		/* default-A */
+		if (isp->phy.state == OTG_STATE_B_IDLE
+				|| isp->phy.state
+					== OTG_STATE_UNDEFINED) {
+			a_idle(isp, "init");
+			return;
+		}
+	} else {				/* default-B */
+		otg_ctrl |= OTG_ID;
+		if (isp->phy.state == OTG_STATE_A_IDLE
+			|| isp->phy.state == OTG_STATE_UNDEFINED) {
+			b_idle(isp, "init");
+			return;
+		}
+	}
+	omap_writel(otg_ctrl, OTG_CTRL);
+}
+
+/* outputs from ISP1301_OTG_STATUS */
+static void update_otg2(struct isp1301 *isp, u8 otg_status)
+{
+	u32	otg_ctrl;
+
+	otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+	otg_ctrl &= ~OTG_XCEIV_INPUTS;
+	otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND);
+	if (otg_status & OTG_B_SESS_VLD)
+		otg_ctrl |= OTG_BSESSVLD;
+	else if (otg_status & OTG_B_SESS_END)
+		otg_ctrl |= OTG_BSESSEND;
+	omap_writel(otg_ctrl, OTG_CTRL);
+}
+
+/* inputs going to ISP1301 */
+static void otg_update_isp(struct isp1301 *isp)
+{
+	u32	otg_ctrl, otg_change;
+	u8	set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP;
+
+	otg_ctrl = omap_readl(OTG_CTRL);
+	otg_change = otg_ctrl ^ isp->last_otg_ctrl;
+	isp->last_otg_ctrl = otg_ctrl;
+	otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS;
+
+	switch (isp->phy.state) {
+	case OTG_STATE_B_IDLE:
+	case OTG_STATE_B_PERIPHERAL:
+	case OTG_STATE_B_SRP_INIT:
+		if (!(otg_ctrl & OTG_PULLUP)) {
+			// if (otg_ctrl & OTG_B_HNPEN) {
+			if (isp->phy.otg->gadget->b_hnp_enable) {
+				isp->phy.state = OTG_STATE_B_WAIT_ACON;
+				pr_debug("  --> b_wait_acon\n");
+			}
+			goto pulldown;
+		}
+pullup:
+		set |= OTG1_DP_PULLUP;
+		clr |= OTG1_DP_PULLDOWN;
+		break;
+	case OTG_STATE_A_SUSPEND:
+	case OTG_STATE_A_PERIPHERAL:
+		if (otg_ctrl & OTG_PULLUP)
+			goto pullup;
+		/* FALLTHROUGH */
+	// case OTG_STATE_B_WAIT_ACON:
+	default:
+pulldown:
+		set |= OTG1_DP_PULLDOWN;
+		clr |= OTG1_DP_PULLUP;
+		break;
+	}
+
+#	define toggle(OTG,ISP) do { \
+		if (otg_ctrl & OTG) set |= ISP; \
+		else clr |= ISP; \
+		} while (0)
+
+	if (!(isp->phy.otg->host))
+		otg_ctrl &= ~OTG_DRV_VBUS;
+
+	switch (isp->phy.state) {
+	case OTG_STATE_A_SUSPEND:
+		if (otg_ctrl & OTG_DRV_VBUS) {
+			set |= OTG1_VBUS_DRV;
+			break;
+		}
+		/* HNP failed for some reason (A_AIDL_BDIS timeout) */
+		notresponding(isp);
+
+		/* FALLTHROUGH */
+	case OTG_STATE_A_VBUS_ERR:
+		isp->phy.state = OTG_STATE_A_WAIT_VFALL;
+		pr_debug("  --> a_wait_vfall\n");
+		/* FALLTHROUGH */
+	case OTG_STATE_A_WAIT_VFALL:
+		/* FIXME usbcore thinks port power is still on ... */
+		clr |= OTG1_VBUS_DRV;
+		break;
+	case OTG_STATE_A_IDLE:
+		if (otg_ctrl & OTG_DRV_VBUS) {
+			isp->phy.state = OTG_STATE_A_WAIT_VRISE;
+			pr_debug("  --> a_wait_vrise\n");
+		}
+		/* FALLTHROUGH */
+	default:
+		toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV);
+	}
+
+	toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG);
+	toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG);
+
+#	undef toggle
+
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set);
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr);
+
+	/* HNP switch to host or peripheral; and SRP */
+	if (otg_change & OTG_PULLUP) {
+		u32 l;
+
+		switch (isp->phy.state) {
+		case OTG_STATE_B_IDLE:
+			if (clr & OTG1_DP_PULLUP)
+				break;
+			isp->phy.state = OTG_STATE_B_PERIPHERAL;
+			pr_debug("  --> b_peripheral\n");
+			break;
+		case OTG_STATE_A_SUSPEND:
+			if (clr & OTG1_DP_PULLUP)
+				break;
+			isp->phy.state = OTG_STATE_A_PERIPHERAL;
+			pr_debug("  --> a_peripheral\n");
+			break;
+		default:
+			break;
+		}
+		l = omap_readl(OTG_CTRL);
+		l |= OTG_PULLUP;
+		omap_writel(l, OTG_CTRL);
+	}
+
+	check_state(isp, __func__);
+	dump_regs(isp, "otg->isp1301");
+}
+
+static irqreturn_t omap_otg_irq(int irq, void *_isp)
+{
+	u16		otg_irq = omap_readw(OTG_IRQ_SRC);
+	u32		otg_ctrl;
+	int		ret = IRQ_NONE;
+	struct isp1301	*isp = _isp;
+	struct usb_otg	*otg = isp->phy.otg;
+
+	/* update ISP1301 transceiver from OTG controller */
+	if (otg_irq & OPRT_CHG) {
+		omap_writew(OPRT_CHG, OTG_IRQ_SRC);
+		isp1301_defer_work(isp, WORK_UPDATE_ISP);
+		ret = IRQ_HANDLED;
+
+	/* SRP to become b_peripheral failed */
+	} else if (otg_irq & B_SRP_TMROUT) {
+		pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL));
+		notresponding(isp);
+
+		/* gadget drivers that care should monitor all kinds of
+		 * remote wakeup (SRP, normal) using their own timer
+		 * to give "check cable and A-device" messages.
+		 */
+		if (isp->phy.state == OTG_STATE_B_SRP_INIT)
+			b_idle(isp, "srp_timeout");
+
+		omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* HNP to become b_host failed */
+	} else if (otg_irq & B_HNP_FAIL) {
+		pr_debug("otg: %s B_HNP_FAIL, %06x\n",
+				state_name(isp), omap_readl(OTG_CTRL));
+		notresponding(isp);
+
+		otg_ctrl = omap_readl(OTG_CTRL);
+		otg_ctrl |= OTG_BUSDROP;
+		otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+		omap_writel(otg_ctrl, OTG_CTRL);
+
+		/* subset of b_peripheral()... */
+		isp->phy.state = OTG_STATE_B_PERIPHERAL;
+		pr_debug("  --> b_peripheral\n");
+
+		omap_writew(B_HNP_FAIL, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* detect SRP from B-device ... */
+	} else if (otg_irq & A_SRP_DETECT) {
+		pr_debug("otg: %s SRP_DETECT, %06x\n",
+				state_name(isp), omap_readl(OTG_CTRL));
+
+		isp1301_defer_work(isp, WORK_UPDATE_OTG);
+		switch (isp->phy.state) {
+		case OTG_STATE_A_IDLE:
+			if (!otg->host)
+				break;
+			isp1301_defer_work(isp, WORK_HOST_RESUME);
+			otg_ctrl = omap_readl(OTG_CTRL);
+			otg_ctrl |= OTG_A_BUSREQ;
+			otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ)
+					& ~OTG_XCEIV_INPUTS
+					& OTG_CTRL_MASK;
+			omap_writel(otg_ctrl, OTG_CTRL);
+			break;
+		default:
+			break;
+		}
+
+		omap_writew(A_SRP_DETECT, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* timer expired:  T(a_wait_bcon) and maybe T(a_wait_vrise)
+	 * we don't track them separately
+	 */
+	} else if (otg_irq & A_REQ_TMROUT) {
+		otg_ctrl = omap_readl(OTG_CTRL);
+		pr_info("otg: BCON_TMOUT from %s, %06x\n",
+				state_name(isp), otg_ctrl);
+		notresponding(isp);
+
+		otg_ctrl |= OTG_BUSDROP;
+		otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+		omap_writel(otg_ctrl, OTG_CTRL);
+		isp->phy.state = OTG_STATE_A_WAIT_VFALL;
+
+		omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* A-supplied voltage fell too low; overcurrent */
+	} else if (otg_irq & A_VBUS_ERR) {
+		otg_ctrl = omap_readl(OTG_CTRL);
+		printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n",
+			state_name(isp), otg_irq, otg_ctrl);
+
+		otg_ctrl |= OTG_BUSDROP;
+		otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+		omap_writel(otg_ctrl, OTG_CTRL);
+		isp->phy.state = OTG_STATE_A_VBUS_ERR;
+
+		omap_writew(A_VBUS_ERR, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* switch driver; the transceiver code activates it,
+	 * ungating the udc clock or resuming OHCI.
+	 */
+	} else if (otg_irq & DRIVER_SWITCH) {
+		int	kick = 0;
+
+		otg_ctrl = omap_readl(OTG_CTRL);
+		printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n",
+				state_name(isp),
+				(otg_ctrl & OTG_DRIVER_SEL)
+					? "gadget" : "host",
+				otg_ctrl);
+		isp1301_defer_work(isp, WORK_UPDATE_ISP);
+
+		/* role is peripheral */
+		if (otg_ctrl & OTG_DRIVER_SEL) {
+			switch (isp->phy.state) {
+			case OTG_STATE_A_IDLE:
+				b_idle(isp, __func__);
+				break;
+			default:
+				break;
+			}
+			isp1301_defer_work(isp, WORK_UPDATE_ISP);
+
+		/* role is host */
+		} else {
+			if (!(otg_ctrl & OTG_ID)) {
+				otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+				omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL);
+			}
+
+			if (otg->host) {
+				switch (isp->phy.state) {
+				case OTG_STATE_B_WAIT_ACON:
+					isp->phy.state = OTG_STATE_B_HOST;
+					pr_debug("  --> b_host\n");
+					kick = 1;
+					break;
+				case OTG_STATE_A_WAIT_BCON:
+					isp->phy.state = OTG_STATE_A_HOST;
+					pr_debug("  --> a_host\n");
+					break;
+				case OTG_STATE_A_PERIPHERAL:
+					isp->phy.state = OTG_STATE_A_WAIT_BCON;
+					pr_debug("  --> a_wait_bcon\n");
+					break;
+				default:
+					break;
+				}
+				isp1301_defer_work(isp, WORK_HOST_RESUME);
+			}
+		}
+
+		omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+		if (kick)
+			usb_bus_start_enum(otg->host, otg->host->otg_port);
+	}
+
+	check_state(isp, __func__);
+	return ret;
+}
+
+static struct platform_device *otg_dev;
+
+static int isp1301_otg_init(struct isp1301 *isp)
+{
+	u32 l;
+
+	if (!otg_dev)
+		return -ENODEV;
+
+	dump_regs(isp, __func__);
+	/* some of these values are board-specific... */
+	l = omap_readl(OTG_SYSCON_2);
+	l |= OTG_EN
+		/* for B-device: */
+		| SRP_GPDATA		/* 9msec Bdev D+ pulse */
+		| SRP_GPDVBUS		/* discharge after VBUS pulse */
+		// | (3 << 24)		/* 2msec VBUS pulse */
+		/* for A-device: */
+		| (0 << 20)		/* 200ms nominal A_WAIT_VRISE timer */
+		| SRP_DPW		/* detect 167+ns SRP pulses */
+		| SRP_DATA | SRP_VBUS	/* accept both kinds of SRP pulse */
+		;
+	omap_writel(l, OTG_SYSCON_2);
+
+	update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
+	update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));
+
+	check_state(isp, __func__);
+	pr_debug("otg: %s, %s %06x\n",
+			state_name(isp), __func__, omap_readl(OTG_CTRL));
+
+	omap_writew(DRIVER_SWITCH | OPRT_CHG
+			| B_SRP_TMROUT | B_HNP_FAIL
+			| A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN);
+
+	l = omap_readl(OTG_SYSCON_2);
+	l |= OTG_EN;
+	omap_writel(l, OTG_SYSCON_2);
+
+	return 0;
+}
+
+static int otg_probe(struct platform_device *dev)
+{
+	// struct omap_usb_config *config = dev->platform_data;
+
+	otg_dev = dev;
+	return 0;
+}
+
+static int otg_remove(struct platform_device *dev)
+{
+	otg_dev = NULL;
+	return 0;
+}
+
+static struct platform_driver omap_otg_driver = {
+	.probe		= otg_probe,
+	.remove		= otg_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "omap_otg",
+	},
+};
+
+static int otg_bind(struct isp1301 *isp)
+{
+	int	status;
+
+	if (otg_dev)
+		return -EBUSY;
+
+	status = platform_driver_register(&omap_otg_driver);
+	if (status < 0)
+		return status;
+
+	if (otg_dev)
+		status = request_irq(otg_dev->resource[1].start, omap_otg_irq,
+				0, DRIVER_NAME, isp);
+	else
+		status = -ENODEV;
+
+	if (status < 0)
+		platform_driver_unregister(&omap_otg_driver);
+	return status;
+}
+
+static void otg_unbind(struct isp1301 *isp)
+{
+	if (!otg_dev)
+		return;
+	free_irq(otg_dev->resource[1].start, isp);
+}
+
+#else
+
+/* OTG controller isn't clocked */
+
+#endif	/* CONFIG_USB_OTG */
+
+/*-------------------------------------------------------------------------*/
+
+static void b_peripheral(struct isp1301 *isp)
+{
+	u32 l;
+
+	l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+	omap_writel(l, OTG_CTRL);
+
+	usb_gadget_vbus_connect(isp->phy.otg->gadget);
+
+#ifdef	CONFIG_USB_OTG
+	enable_vbus_draw(isp, 8);
+	otg_update_isp(isp);
+#else
+	enable_vbus_draw(isp, 100);
+	/* UDC driver just set OTG_BSESSVLD */
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP);
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN);
+	isp->phy.state = OTG_STATE_B_PERIPHERAL;
+	pr_debug("  --> b_peripheral\n");
+	dump_regs(isp, "2periph");
+#endif
+}
+
+static void isp_update_otg(struct isp1301 *isp, u8 stat)
+{
+	struct usb_otg		*otg = isp->phy.otg;
+	u8			isp_stat, isp_bstat;
+	enum usb_otg_state	state = isp->phy.state;
+
+	if (stat & INTR_BDIS_ACON)
+		pr_debug("OTG:  BDIS_ACON, %s\n", state_name(isp));
+
+	/* start certain state transitions right away */
+	isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
+	if (isp_stat & INTR_ID_GND) {
+		if (otg->default_a) {
+			switch (state) {
+			case OTG_STATE_B_IDLE:
+				a_idle(isp, "idle");
+				/* FALLTHROUGH */
+			case OTG_STATE_A_IDLE:
+				enable_vbus_source(isp);
+				/* FALLTHROUGH */
+			case OTG_STATE_A_WAIT_VRISE:
+				/* we skip over OTG_STATE_A_WAIT_BCON, since
+				 * the HC will transition to A_HOST (or
+				 * A_SUSPEND!) without our noticing except
+				 * when HNP is used.
+				 */
+				if (isp_stat & INTR_VBUS_VLD)
+					isp->phy.state = OTG_STATE_A_HOST;
+				break;
+			case OTG_STATE_A_WAIT_VFALL:
+				if (!(isp_stat & INTR_SESS_VLD))
+					a_idle(isp, "vfell");
+				break;
+			default:
+				if (!(isp_stat & INTR_VBUS_VLD))
+					isp->phy.state = OTG_STATE_A_VBUS_ERR;
+				break;
+			}
+			isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+		} else {
+			switch (state) {
+			case OTG_STATE_B_PERIPHERAL:
+			case OTG_STATE_B_HOST:
+			case OTG_STATE_B_WAIT_ACON:
+				usb_gadget_vbus_disconnect(otg->gadget);
+				break;
+			default:
+				break;
+			}
+			if (state != OTG_STATE_A_IDLE)
+				a_idle(isp, "id");
+			if (otg->host && state == OTG_STATE_A_IDLE)
+				isp1301_defer_work(isp, WORK_HOST_RESUME);
+			isp_bstat = 0;
+		}
+	} else {
+		u32 l;
+
+		/* if user unplugged mini-A end of cable,
+		 * don't bypass A_WAIT_VFALL.
+		 */
+		if (otg->default_a) {
+			switch (state) {
+			default:
+				isp->phy.state = OTG_STATE_A_WAIT_VFALL;
+				break;
+			case OTG_STATE_A_WAIT_VFALL:
+				state = OTG_STATE_A_IDLE;
+				/* khubd may take a while to notice and
+				 * handle this disconnect, so don't go
+				 * to B_IDLE quite yet.
+				 */
+				break;
+			case OTG_STATE_A_IDLE:
+				host_suspend(isp);
+				isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1,
+						MC1_BDIS_ACON_EN);
+				isp->phy.state = OTG_STATE_B_IDLE;
+				l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+				l &= ~OTG_CTRL_BITS;
+				omap_writel(l, OTG_CTRL);
+				break;
+			case OTG_STATE_B_IDLE:
+				break;
+			}
+		}
+		isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+
+		switch (isp->phy.state) {
+		case OTG_STATE_B_PERIPHERAL:
+		case OTG_STATE_B_WAIT_ACON:
+		case OTG_STATE_B_HOST:
+			if (likely(isp_bstat & OTG_B_SESS_VLD))
+				break;
+			enable_vbus_draw(isp, 0);
+#ifndef	CONFIG_USB_OTG
+			/* UDC driver will clear OTG_BSESSVLD */
+			isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1,
+						OTG1_DP_PULLDOWN);
+			isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1,
+						OTG1_DP_PULLUP);
+			dump_regs(isp, __func__);
+#endif
+			/* FALLTHROUGH */
+		case OTG_STATE_B_SRP_INIT:
+			b_idle(isp, __func__);
+			l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+			omap_writel(l, OTG_CTRL);
+			/* FALLTHROUGH */
+		case OTG_STATE_B_IDLE:
+			if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) {
+#ifdef	CONFIG_USB_OTG
+				update_otg1(isp, isp_stat);
+				update_otg2(isp, isp_bstat);
+#endif
+				b_peripheral(isp);
+			} else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD)))
+				isp_bstat |= OTG_B_SESS_END;
+			break;
+		case OTG_STATE_A_WAIT_VFALL:
+			break;
+		default:
+			pr_debug("otg: unsupported b-device %s\n",
+				state_name(isp));
+			break;
+		}
+	}
+
+	if (state != isp->phy.state)
+		pr_debug("  isp, %s -> %s\n",
+				otg_state_string(state), state_name(isp));
+
+#ifdef	CONFIG_USB_OTG
+	/* update the OTG controller state to match the isp1301; may
+	 * trigger OPRT_CHG irqs for changes going to the isp1301.
+	 */
+	update_otg1(isp, isp_stat);
+	update_otg2(isp, isp_bstat);
+	check_state(isp, __func__);
+#endif
+
+	dump_regs(isp, "isp1301->otg");
+}
+
+/*-------------------------------------------------------------------------*/
+
+static u8 isp1301_clear_latch(struct isp1301 *isp)
+{
+	u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch);
+	return latch;
+}
+
+static void
+isp1301_work(struct work_struct *work)
+{
+	struct isp1301	*isp = container_of(work, struct isp1301, work);
+	int		stop;
+
+	/* implicit lock:  we're the only task using this device */
+	isp->working = 1;
+	do {
+		stop = test_bit(WORK_STOP, &isp->todo);
+
+#ifdef	CONFIG_USB_OTG
+		/* transfer state from otg engine to isp1301 */
+		if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) {
+			otg_update_isp(isp);
+			put_device(&isp->client->dev);
+		}
+#endif
+		/* transfer state from isp1301 to otg engine */
+		if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) {
+			u8		stat = isp1301_clear_latch(isp);
+
+			isp_update_otg(isp, stat);
+			put_device(&isp->client->dev);
+		}
+
+		if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) {
+			u32	otg_ctrl;
+
+			/*
+			 * skip A_WAIT_VRISE; hc transitions invisibly
+			 * skip A_WAIT_BCON; same.
+			 */
+			switch (isp->phy.state) {
+			case OTG_STATE_A_WAIT_BCON:
+			case OTG_STATE_A_WAIT_VRISE:
+				isp->phy.state = OTG_STATE_A_HOST;
+				pr_debug("  --> a_host\n");
+				otg_ctrl = omap_readl(OTG_CTRL);
+				otg_ctrl |= OTG_A_BUSREQ;
+				otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ)
+						& OTG_CTRL_MASK;
+				omap_writel(otg_ctrl, OTG_CTRL);
+				break;
+			case OTG_STATE_B_WAIT_ACON:
+				isp->phy.state = OTG_STATE_B_HOST;
+				pr_debug("  --> b_host (acon)\n");
+				break;
+			case OTG_STATE_B_HOST:
+			case OTG_STATE_B_IDLE:
+			case OTG_STATE_A_IDLE:
+				break;
+			default:
+				pr_debug("  host resume in %s\n",
+						state_name(isp));
+			}
+			host_resume(isp);
+			// mdelay(10);
+			put_device(&isp->client->dev);
+		}
+
+		if (test_and_clear_bit(WORK_TIMER, &isp->todo)) {
+#ifdef	VERBOSE
+			dump_regs(isp, "timer");
+			if (!stop)
+				mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
+#endif
+			put_device(&isp->client->dev);
+		}
+
+		if (isp->todo)
+			dev_vdbg(&isp->client->dev,
+				"work done, todo = 0x%lx\n",
+				isp->todo);
+		if (stop) {
+			dev_dbg(&isp->client->dev, "stop\n");
+			break;
+		}
+	} while (isp->todo);
+	isp->working = 0;
+}
+
+static irqreturn_t isp1301_irq(int irq, void *isp)
+{
+	isp1301_defer_work(isp, WORK_UPDATE_OTG);
+	return IRQ_HANDLED;
+}
+
+static void isp1301_timer(unsigned long _isp)
+{
+	isp1301_defer_work((void *)_isp, WORK_TIMER);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void isp1301_release(struct device *dev)
+{
+	struct isp1301	*isp;
+
+	isp = dev_get_drvdata(dev);
+
+	/* FIXME -- not with a "new style" driver, it doesn't!! */
+
+	/* ugly -- i2c hijacks our memory hook to wait_for_completion() */
+	if (isp->i2c_release)
+		isp->i2c_release(dev);
+	kfree(isp->phy.otg);
+	kfree (isp);
+}
+
+static struct isp1301 *the_transceiver;
+
+static int __exit isp1301_remove(struct i2c_client *i2c)
+{
+	struct isp1301	*isp;
+
+	isp = i2c_get_clientdata(i2c);
+
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
+	free_irq(i2c->irq, isp);
+#ifdef	CONFIG_USB_OTG
+	otg_unbind(isp);
+#endif
+	if (machine_is_omap_h2())
+		gpio_free(2);
+
+	isp->timer.data = 0;
+	set_bit(WORK_STOP, &isp->todo);
+	del_timer_sync(&isp->timer);
+	flush_work_sync(&isp->work);
+
+	put_device(&i2c->dev);
+	the_transceiver = NULL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NOTE:  three modes are possible here, only one of which
+ * will be standards-conformant on any given system:
+ *
+ *  - OTG mode (dual-role), required if there's a Mini-AB connector
+ *  - HOST mode, for when there's one or more A (host) connectors
+ *  - DEVICE mode, for when there's a B/Mini-B (device) connector
+ *
+ * As a rule, you won't have an isp1301 chip unless it's there to
+ * support the OTG mode.  Other modes help testing USB controllers
+ * in isolation from (full) OTG support, or maybe so later board
+ * revisions can help to support those feature.
+ */
+
+#ifdef	CONFIG_USB_OTG
+
+static int isp1301_otg_enable(struct isp1301 *isp)
+{
+	power_up(isp);
+	isp1301_otg_init(isp);
+
+	/* NOTE:  since we don't change this, this provides
+	 * a few more interrupts than are strictly needed.
+	 */
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+		INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+		INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
+
+	dev_info(&isp->client->dev, "ready for dual-role USB ...\n");
+
+	return 0;
+}
+
+#endif
+
+/* add or disable the host device+driver */
+static int
+isp1301_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct isp1301	*isp = container_of(otg->phy, struct isp1301, phy);
+
+	if (!otg || isp != the_transceiver)
+		return -ENODEV;
+
+	if (!host) {
+		omap_writew(0, OTG_IRQ_EN);
+		power_down(isp);
+		otg->host = NULL;
+		return 0;
+	}
+
+#ifdef	CONFIG_USB_OTG
+	otg->host = host;
+	dev_dbg(&isp->client->dev, "registered host\n");
+	host_suspend(isp);
+	if (otg->gadget)
+		return isp1301_otg_enable(isp);
+	return 0;
+
+#elif	!defined(CONFIG_USB_GADGET_OMAP)
+	// FIXME update its refcount
+	otg->host = host;
+
+	power_up(isp);
+
+	if (machine_is_omap_h2())
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+
+	dev_info(&isp->client->dev, "A-Host sessions ok\n");
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+		INTR_ID_GND);
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+		INTR_ID_GND);
+
+	/* If this has a Mini-AB connector, this mode is highly
+	 * nonstandard ... but can be handy for testing, especially with
+	 * the Mini-A end of an OTG cable.  (Or something nonstandard
+	 * like MiniB-to-StandardB, maybe built with a gender mender.)
+	 */
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV);
+
+	dump_regs(isp, __func__);
+
+	return 0;
+
+#else
+	dev_dbg(&isp->client->dev, "host sessions not allowed\n");
+	return -EINVAL;
+#endif
+
+}
+
+static int
+isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
+{
+	struct isp1301	*isp = container_of(otg->phy, struct isp1301, phy);
+#ifndef	CONFIG_USB_OTG
+	u32 l;
+#endif
+
+	if (!otg || isp != the_transceiver)
+		return -ENODEV;
+
+	if (!gadget) {
+		omap_writew(0, OTG_IRQ_EN);
+		if (!otg->default_a)
+			enable_vbus_draw(isp, 0);
+		usb_gadget_vbus_disconnect(otg->gadget);
+		otg->gadget = NULL;
+		power_down(isp);
+		return 0;
+	}
+
+#ifdef	CONFIG_USB_OTG
+	otg->gadget = gadget;
+	dev_dbg(&isp->client->dev, "registered gadget\n");
+	/* gadget driver may be suspended until vbus_connect () */
+	if (otg->host)
+		return isp1301_otg_enable(isp);
+	return 0;
+
+#elif	!defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE)
+	otg->gadget = gadget;
+	// FIXME update its refcount
+
+	l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+	l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
+	l |= OTG_ID;
+	omap_writel(l, OTG_CTRL);
+
+	power_up(isp);
+	isp->phy.state = OTG_STATE_B_IDLE;
+
+	if (machine_is_omap_h2() || machine_is_omap_h3())
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+		INTR_SESS_VLD);
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+		INTR_VBUS_VLD);
+	dev_info(&isp->client->dev, "B-Peripheral sessions ok\n");
+	dump_regs(isp, __func__);
+
+	/* If this has a Mini-AB connector, this mode is highly
+	 * nonstandard ... but can be handy for testing, so long
+	 * as you don't plug a Mini-A cable into the jack.
+	 */
+	if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD)
+		b_peripheral(isp);
+
+	return 0;
+
+#else
+	dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n");
+	return -EINVAL;
+#endif
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int
+isp1301_set_power(struct usb_phy *dev, unsigned mA)
+{
+	if (!the_transceiver)
+		return -ENODEV;
+	if (dev->state == OTG_STATE_B_PERIPHERAL)
+		enable_vbus_draw(the_transceiver, mA);
+	return 0;
+}
+
+static int
+isp1301_start_srp(struct usb_otg *otg)
+{
+	struct isp1301	*isp = container_of(otg->phy, struct isp1301, phy);
+	u32		otg_ctrl;
+
+	if (!otg || isp != the_transceiver
+			|| isp->phy.state != OTG_STATE_B_IDLE)
+		return -ENODEV;
+
+	otg_ctrl = omap_readl(OTG_CTRL);
+	if (!(otg_ctrl & OTG_BSESSEND))
+		return -EINVAL;
+
+	otg_ctrl |= OTG_B_BUSREQ;
+	otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK;
+	omap_writel(otg_ctrl, OTG_CTRL);
+	isp->phy.state = OTG_STATE_B_SRP_INIT;
+
+	pr_debug("otg: SRP, %s ... %06x\n", state_name(isp),
+			omap_readl(OTG_CTRL));
+#ifdef	CONFIG_USB_OTG
+	check_state(isp, __func__);
+#endif
+	return 0;
+}
+
+static int
+isp1301_start_hnp(struct usb_otg *otg)
+{
+#ifdef	CONFIG_USB_OTG
+	struct isp1301	*isp = container_of(otg->phy, struct isp1301, phy);
+	u32 l;
+
+	if (!otg || isp != the_transceiver)
+		return -ENODEV;
+	if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable))
+		return -ENOTCONN;
+	if (!otg->default_a && (otg->gadget == NULL
+			|| !otg->gadget->b_hnp_enable))
+		return -ENOTCONN;
+
+	/* We want hardware to manage most HNP protocol timings.
+	 * So do this part as early as possible...
+	 */
+	switch (isp->phy.state) {
+	case OTG_STATE_B_HOST:
+		isp->phy.state = OTG_STATE_B_PERIPHERAL;
+		/* caller will suspend next */
+		break;
+	case OTG_STATE_A_HOST:
+#if 0
+		/* autoconnect mode avoids irq latency bugs */
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
+				MC1_BDIS_ACON_EN);
+#endif
+		/* caller must suspend then clear A_BUSREQ */
+		usb_gadget_vbus_connect(otg->gadget);
+		l = omap_readl(OTG_CTRL);
+		l |= OTG_A_SETB_HNPEN;
+		omap_writel(l, OTG_CTRL);
+
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		/* initiated by B-Host suspend */
+		break;
+	default:
+		return -EILSEQ;
+	}
+	pr_debug("otg: HNP %s, %06x ...\n",
+		state_name(isp), omap_readl(OTG_CTRL));
+	check_state(isp, __func__);
+	return 0;
+#else
+	/* srp-only */
+	return -EINVAL;
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	int			status;
+	struct isp1301		*isp;
+
+	if (the_transceiver)
+		return 0;
+
+	isp = kzalloc(sizeof *isp, GFP_KERNEL);
+	if (!isp)
+		return 0;
+
+	isp->phy.otg = kzalloc(sizeof *isp->phy.otg, GFP_KERNEL);
+	if (!isp->phy.otg) {
+		kfree(isp);
+		return 0;
+	}
+
+	INIT_WORK(&isp->work, isp1301_work);
+	init_timer(&isp->timer);
+	isp->timer.function = isp1301_timer;
+	isp->timer.data = (unsigned long) isp;
+
+	i2c_set_clientdata(i2c, isp);
+	isp->client = i2c;
+
+	/* verify the chip (shouldn't be necessary) */
+	status = isp1301_get_u16(isp, ISP1301_VENDOR_ID);
+	if (status != I2C_VENDOR_ID_PHILIPS) {
+		dev_dbg(&i2c->dev, "not philips id: %d\n", status);
+		goto fail;
+	}
+	status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID);
+	if (status != I2C_PRODUCT_ID_PHILIPS_1301) {
+		dev_dbg(&i2c->dev, "not isp1301, %d\n", status);
+		goto fail;
+	}
+	isp->i2c_release = i2c->dev.release;
+	i2c->dev.release = isp1301_release;
+
+	/* initial development used chiprev 2.00 */
+	status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE);
+	dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n",
+		status >> 8, status & 0xff);
+
+	/* make like power-on reset */
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK);
+
+	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI);
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI);
+
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1,
+				OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN);
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1,
+				~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN));
+
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
+
+#ifdef	CONFIG_USB_OTG
+	status = otg_bind(isp);
+	if (status < 0) {
+		dev_dbg(&i2c->dev, "can't bind OTG\n");
+		goto fail;
+	}
+#endif
+
+	if (machine_is_omap_h2()) {
+		/* full speed signaling by default */
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
+			MC1_SPEED);
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2,
+			MC2_SPD_SUSP_CTRL);
+
+		/* IRQ wired at M14 */
+		omap_cfg_reg(M14_1510_GPIO2);
+		if (gpio_request(2, "isp1301") == 0)
+			gpio_direction_input(2);
+		isp->irq_type = IRQF_TRIGGER_FALLING;
+	}
+
+	isp->irq_type |= IRQF_SAMPLE_RANDOM;
+	status = request_irq(i2c->irq, isp1301_irq,
+			isp->irq_type, DRIVER_NAME, isp);
+	if (status < 0) {
+		dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n",
+				i2c->irq, status);
+		goto fail;
+	}
+
+	isp->phy.dev = &i2c->dev;
+	isp->phy.label = DRIVER_NAME;
+	isp->phy.set_power = isp1301_set_power,
+
+	isp->phy.otg->phy = &isp->phy;
+	isp->phy.otg->set_host = isp1301_set_host,
+	isp->phy.otg->set_peripheral = isp1301_set_peripheral,
+	isp->phy.otg->start_srp = isp1301_start_srp,
+	isp->phy.otg->start_hnp = isp1301_start_hnp,
+
+	enable_vbus_draw(isp, 0);
+	power_down(isp);
+	the_transceiver = isp;
+
+#ifdef	CONFIG_USB_OTG
+	update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
+	update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));
+#endif
+
+	dump_regs(isp, __func__);
+
+#ifdef	VERBOSE
+	mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
+	dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES);
+#endif
+
+	status = usb_set_transceiver(&isp->phy);
+	if (status < 0)
+		dev_err(&i2c->dev, "can't register transceiver, %d\n",
+			status);
+
+	return 0;
+
+fail:
+	kfree(isp->phy.otg);
+	kfree(isp);
+	return -ENODEV;
+}
+
+static const struct i2c_device_id isp1301_id[] = {
+	{ "isp1301_omap", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, isp1301_id);
+
+static struct i2c_driver isp1301_driver = {
+	.driver = {
+		.name	= "isp1301_omap",
+	},
+	.probe		= isp1301_probe,
+	.remove		= __exit_p(isp1301_remove),
+	.id_table	= isp1301_id,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init isp_init(void)
+{
+	return i2c_add_driver(&isp1301_driver);
+}
+subsys_initcall(isp_init);
+
+static void __exit isp_exit(void)
+{
+	if (the_transceiver)
+		usb_set_transceiver(NULL);
+	i2c_del_driver(&isp1301_driver);
+}
+module_exit(isp_exit);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/msm_otg.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/msm_otg.c
new file mode 100644
index 0000000..1d0347c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/msm_otg.c
@@ -0,0 +1,1773 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/clk.h>
+
+#define MSM_USB_BASE	(motg->regs)
+#define DRIVER_NAME	"msm_otg"
+
+#define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
+
+#define USB_PHY_3P3_VOL_MIN	3050000 /* uV */
+#define USB_PHY_3P3_VOL_MAX	3300000 /* uV */
+#define USB_PHY_3P3_HPM_LOAD	50000	/* uA */
+#define USB_PHY_3P3_LPM_LOAD	4000	/* uA */
+
+#define USB_PHY_1P8_VOL_MIN	1800000 /* uV */
+#define USB_PHY_1P8_VOL_MAX	1800000 /* uV */
+#define USB_PHY_1P8_HPM_LOAD	50000	/* uA */
+#define USB_PHY_1P8_LPM_LOAD	4000	/* uA */
+
+#define USB_PHY_VDD_DIG_VOL_MIN	1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX	1320000 /* uV */
+
+static struct regulator *hsusb_3p3;
+static struct regulator *hsusb_1p8;
+static struct regulator *hsusb_vddcx;
+
+static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init)
+{
+	int ret = 0;
+
+	if (init) {
+		hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX");
+		if (IS_ERR(hsusb_vddcx)) {
+			dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
+			return PTR_ERR(hsusb_vddcx);
+		}
+
+		ret = regulator_set_voltage(hsusb_vddcx,
+				USB_PHY_VDD_DIG_VOL_MIN,
+				USB_PHY_VDD_DIG_VOL_MAX);
+		if (ret) {
+			dev_err(motg->phy.dev, "unable to set the voltage "
+					"for hsusb vddcx\n");
+			regulator_put(hsusb_vddcx);
+			return ret;
+		}
+
+		ret = regulator_enable(hsusb_vddcx);
+		if (ret) {
+			dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n");
+			regulator_put(hsusb_vddcx);
+		}
+	} else {
+		ret = regulator_set_voltage(hsusb_vddcx, 0,
+			USB_PHY_VDD_DIG_VOL_MAX);
+		if (ret)
+			dev_err(motg->phy.dev, "unable to set the voltage "
+					"for hsusb vddcx\n");
+		ret = regulator_disable(hsusb_vddcx);
+		if (ret)
+			dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n");
+
+		regulator_put(hsusb_vddcx);
+	}
+
+	return ret;
+}
+
+static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
+{
+	int rc = 0;
+
+	if (init) {
+		hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3");
+		if (IS_ERR(hsusb_3p3)) {
+			dev_err(motg->phy.dev, "unable to get hsusb 3p3\n");
+			return PTR_ERR(hsusb_3p3);
+		}
+
+		rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN,
+				USB_PHY_3P3_VOL_MAX);
+		if (rc) {
+			dev_err(motg->phy.dev, "unable to set voltage level "
+					"for hsusb 3p3\n");
+			goto put_3p3;
+		}
+		rc = regulator_enable(hsusb_3p3);
+		if (rc) {
+			dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n");
+			goto put_3p3;
+		}
+		hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8");
+		if (IS_ERR(hsusb_1p8)) {
+			dev_err(motg->phy.dev, "unable to get hsusb 1p8\n");
+			rc = PTR_ERR(hsusb_1p8);
+			goto disable_3p3;
+		}
+		rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN,
+				USB_PHY_1P8_VOL_MAX);
+		if (rc) {
+			dev_err(motg->phy.dev, "unable to set voltage level "
+					"for hsusb 1p8\n");
+			goto put_1p8;
+		}
+		rc = regulator_enable(hsusb_1p8);
+		if (rc) {
+			dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n");
+			goto put_1p8;
+		}
+
+		return 0;
+	}
+
+	regulator_disable(hsusb_1p8);
+put_1p8:
+	regulator_put(hsusb_1p8);
+disable_3p3:
+	regulator_disable(hsusb_3p3);
+put_3p3:
+	regulator_put(hsusb_3p3);
+	return rc;
+}
+
+#ifdef CONFIG_PM_SLEEP
+#define USB_PHY_SUSP_DIG_VOL  500000
+static int msm_hsusb_config_vddcx(int high)
+{
+	int max_vol = USB_PHY_VDD_DIG_VOL_MAX;
+	int min_vol;
+	int ret;
+
+	if (high)
+		min_vol = USB_PHY_VDD_DIG_VOL_MIN;
+	else
+		min_vol = USB_PHY_SUSP_DIG_VOL;
+
+	ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol);
+	if (ret) {
+		pr_err("%s: unable to set the voltage for regulator "
+			"HSUSB_VDDCX\n", __func__);
+		return ret;
+	}
+
+	pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol);
+
+	return ret;
+}
+#endif
+
+static int msm_hsusb_ldo_set_mode(int on)
+{
+	int ret = 0;
+
+	if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) {
+		pr_err("%s: HSUSB_1p8 is not initialized\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) {
+		pr_err("%s: HSUSB_3p3 is not initialized\n", __func__);
+		return -ENODEV;
+	}
+
+	if (on) {
+		ret = regulator_set_optimum_mode(hsusb_1p8,
+				USB_PHY_1P8_HPM_LOAD);
+		if (ret < 0) {
+			pr_err("%s: Unable to set HPM of the regulator "
+				"HSUSB_1p8\n", __func__);
+			return ret;
+		}
+		ret = regulator_set_optimum_mode(hsusb_3p3,
+				USB_PHY_3P3_HPM_LOAD);
+		if (ret < 0) {
+			pr_err("%s: Unable to set HPM of the regulator "
+				"HSUSB_3p3\n", __func__);
+			regulator_set_optimum_mode(hsusb_1p8,
+				USB_PHY_1P8_LPM_LOAD);
+			return ret;
+		}
+	} else {
+		ret = regulator_set_optimum_mode(hsusb_1p8,
+				USB_PHY_1P8_LPM_LOAD);
+		if (ret < 0)
+			pr_err("%s: Unable to set LPM of the regulator "
+				"HSUSB_1p8\n", __func__);
+		ret = regulator_set_optimum_mode(hsusb_3p3,
+				USB_PHY_3P3_LPM_LOAD);
+		if (ret < 0)
+			pr_err("%s: Unable to set LPM of the regulator "
+				"HSUSB_3p3\n", __func__);
+	}
+
+	pr_debug("reg (%s)\n", on ? "HPM" : "LPM");
+	return ret < 0 ? ret : 0;
+}
+
+static int ulpi_read(struct usb_phy *phy, u32 reg)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	int cnt = 0;
+
+	/* initiate read operation */
+	writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(phy->dev, "ulpi_read: timeout %08x\n",
+			readl(USB_ULPI_VIEWPORT));
+		return -ETIMEDOUT;
+	}
+	return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
+}
+
+static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	int cnt = 0;
+
+	/* initiate write operation */
+	writel(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(phy->dev, "ulpi_write: timeout\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static struct usb_phy_io_ops msm_otg_io_ops = {
+	.read = ulpi_read,
+	.write = ulpi_write,
+};
+
+static void ulpi_init(struct msm_otg *motg)
+{
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int *seq = pdata->phy_init_seq;
+
+	if (!seq)
+		return;
+
+	while (seq[0] >= 0) {
+		dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n",
+				seq[0], seq[1]);
+		ulpi_write(&motg->phy, seq[0], seq[1]);
+		seq += 2;
+	}
+}
+
+static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
+{
+	int ret;
+
+	if (assert) {
+		ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
+		if (ret)
+			dev_err(motg->phy.dev, "usb hs_clk assert failed\n");
+	} else {
+		ret = clk_reset(motg->clk, CLK_RESET_DEASSERT);
+		if (ret)
+			dev_err(motg->phy.dev, "usb hs_clk deassert failed\n");
+	}
+	return ret;
+}
+
+static int msm_otg_phy_clk_reset(struct msm_otg *motg)
+{
+	int ret;
+
+	ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT);
+	if (ret) {
+		dev_err(motg->phy.dev, "usb phy clk assert failed\n");
+		return ret;
+	}
+	usleep_range(10000, 12000);
+	ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT);
+	if (ret)
+		dev_err(motg->phy.dev, "usb phy clk deassert failed\n");
+	return ret;
+}
+
+static int msm_otg_phy_reset(struct msm_otg *motg)
+{
+	u32 val;
+	int ret;
+	int retries;
+
+	ret = msm_otg_link_clk_reset(motg, 1);
+	if (ret)
+		return ret;
+	ret = msm_otg_phy_clk_reset(motg);
+	if (ret)
+		return ret;
+	ret = msm_otg_link_clk_reset(motg, 0);
+	if (ret)
+		return ret;
+
+	val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	for (retries = 3; retries > 0; retries--) {
+		ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM,
+				ULPI_CLR(ULPI_FUNC_CTRL));
+		if (!ret)
+			break;
+		ret = msm_otg_phy_clk_reset(motg);
+		if (ret)
+			return ret;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	/* This reset calibrates the phy, if the above write succeeded */
+	ret = msm_otg_phy_clk_reset(motg);
+	if (ret)
+		return ret;
+
+	for (retries = 3; retries > 0; retries--) {
+		ret = ulpi_read(&motg->phy, ULPI_DEBUG);
+		if (ret != -ETIMEDOUT)
+			break;
+		ret = msm_otg_phy_clk_reset(motg);
+		if (ret)
+			return ret;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	dev_info(motg->phy.dev, "phy_reset: success\n");
+	return 0;
+}
+
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_otg_reset(struct usb_phy *phy)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int cnt = 0;
+	int ret;
+	u32 val = 0;
+	u32 ulpi_val = 0;
+
+	ret = msm_otg_phy_reset(motg);
+	if (ret) {
+		dev_err(phy->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	ulpi_init(motg);
+
+	writel(USBCMD_RESET, USB_USBCMD);
+	while (cnt < LINK_RESET_TIMEOUT_USEC) {
+		if (!(readl(USB_USBCMD) & USBCMD_RESET))
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= LINK_RESET_TIMEOUT_USEC)
+		return -ETIMEDOUT;
+
+	/* select ULPI phy */
+	writel(0x80000000, USB_PORTSC);
+
+	msleep(100);
+
+	writel(0x0, USB_AHBBURST);
+	writel(0x00, USB_AHBMODE);
+
+	if (pdata->otg_control == OTG_PHY_CONTROL) {
+		val = readl(USB_OTGSC);
+		if (pdata->mode == USB_OTG) {
+			ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID;
+			val |= OTGSC_IDIE | OTGSC_BSVIE;
+		} else if (pdata->mode == USB_PERIPHERAL) {
+			ulpi_val = ULPI_INT_SESS_VALID;
+			val |= OTGSC_BSVIE;
+		}
+		writel(val, USB_OTGSC);
+		ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE);
+		ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
+	}
+
+	return 0;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC	(100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_otg_suspend(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	struct usb_bus *bus = phy->otg->host;
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int cnt = 0;
+
+	if (atomic_read(&motg->in_lpm))
+		return 0;
+
+	disable_irq(motg->irq);
+	/*
+	 * Chipidea 45-nm PHY suspend sequence:
+	 *
+	 * Interrupt Latch Register auto-clear feature is not present
+	 * in all PHY versions. Latch register is clear on read type.
+	 * Clear latch register to avoid spurious wakeup from
+	 * low power mode (LPM).
+	 *
+	 * PHY comparators are disabled when PHY enters into low power
+	 * mode (LPM). Keep PHY comparators ON in LPM only when we expect
+	 * VBUS/Id notifications from USB PHY. Otherwise turn off USB
+	 * PHY comparators. This save significant amount of power.
+	 *
+	 * PLL is not turned off when PHY enters into low power mode (LPM).
+	 * Disable PLL for maximum power savings.
+	 */
+
+	if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) {
+		ulpi_read(phy, 0x14);
+		if (pdata->otg_control == OTG_PHY_CONTROL)
+			ulpi_write(phy, 0x01, 0x30);
+		ulpi_write(phy, 0x08, 0x09);
+	}
+
+	/*
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
+		dev_err(phy->dev, "Unable to suspend PHY\n");
+		msm_otg_reset(phy);
+		enable_irq(motg->irq);
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+
+	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
+			motg->pdata->otg_control == OTG_PMIC_CONTROL)
+		writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL);
+
+	clk_disable(motg->pclk);
+	clk_disable(motg->clk);
+	if (motg->core_clk)
+		clk_disable(motg->core_clk);
+
+	if (!IS_ERR(motg->pclk_src))
+		clk_disable(motg->pclk_src);
+
+	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
+			motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+		msm_hsusb_ldo_set_mode(0);
+		msm_hsusb_config_vddcx(0);
+	}
+
+	if (device_may_wakeup(phy->dev))
+		enable_irq_wake(motg->irq);
+	if (bus)
+		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+
+	atomic_set(&motg->in_lpm, 1);
+	enable_irq(motg->irq);
+
+	dev_info(phy->dev, "USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_otg_resume(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	struct usb_bus *bus = phy->otg->host;
+	int cnt = 0;
+	unsigned temp;
+
+	if (!atomic_read(&motg->in_lpm))
+		return 0;
+
+	if (!IS_ERR(motg->pclk_src))
+		clk_enable(motg->pclk_src);
+
+	clk_enable(motg->pclk);
+	clk_enable(motg->clk);
+	if (motg->core_clk)
+		clk_enable(motg->core_clk);
+
+	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
+			motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+		msm_hsusb_ldo_set_mode(1);
+		msm_hsusb_config_vddcx(1);
+		writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL);
+	}
+
+	temp = readl(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel(temp, USB_USBCMD);
+
+	/*
+	 * PHY comes out of low power mode (LPM) in case of wakeup
+	 * from asynchronous interrupt.
+	 */
+	if (!(readl(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC);
+	while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+		if (!(readl(USB_PORTSC) & PORTSC_PHCD))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+		/*
+		 * This is a fatal error. Reset the link and
+		 * PHY. USB state can not be restored. Re-insertion
+		 * of USB cable is the only way to get USB working.
+		 */
+		dev_err(phy->dev, "Unable to resume USB."
+				"Re-plugin the cable\n");
+		msm_otg_reset(phy);
+	}
+
+skip_phy_resume:
+	if (device_may_wakeup(phy->dev))
+		disable_irq_wake(motg->irq);
+	if (bus)
+		set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+
+	atomic_set(&motg->in_lpm, 0);
+
+	if (motg->async_int) {
+		motg->async_int = 0;
+		pm_runtime_put(phy->dev);
+		enable_irq(motg->irq);
+	}
+
+	dev_info(phy->dev, "USB exited from low power mode\n");
+
+	return 0;
+}
+#endif
+
+static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
+{
+	if (motg->cur_power == mA)
+		return;
+
+	/* TODO: Notify PMIC about available current */
+	dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
+	motg->cur_power = mA;
+}
+
+static int msm_otg_set_power(struct usb_phy *phy, unsigned mA)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+
+	/*
+	 * Gadget driver uses set_power method to notify about the
+	 * available current based on suspend/configured states.
+	 *
+	 * IDEV_CHG can be drawn irrespective of suspend/un-configured
+	 * states when CDP/ACA is connected.
+	 */
+	if (motg->chg_type == USB_SDP_CHARGER)
+		msm_otg_notify_charger(motg, mA);
+
+	return 0;
+}
+
+static void msm_otg_start_host(struct usb_phy *phy, int on)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	struct usb_hcd *hcd;
+
+	if (!phy->otg->host)
+		return;
+
+	hcd = bus_to_hcd(phy->otg->host);
+
+	if (on) {
+		dev_dbg(phy->dev, "host on\n");
+
+		if (pdata->vbus_power)
+			pdata->vbus_power(1);
+		/*
+		 * Some boards have a switch cotrolled by gpio
+		 * to enable/disable internal HUB. Enable internal
+		 * HUB before kicking the host.
+		 */
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_A_HOST);
+#ifdef CONFIG_USB
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+#endif
+	} else {
+		dev_dbg(phy->dev, "host off\n");
+
+#ifdef CONFIG_USB
+		usb_remove_hcd(hcd);
+#endif
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_UNDEFINED);
+		if (pdata->vbus_power)
+			pdata->vbus_power(0);
+	}
+}
+
+static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+	struct usb_hcd *hcd;
+
+	/*
+	 * Fail host registration if this board can support
+	 * only peripheral configuration.
+	 */
+	if (motg->pdata->mode == USB_PERIPHERAL) {
+		dev_info(otg->phy->dev, "Host mode is not supported\n");
+		return -ENODEV;
+	}
+
+	if (!host) {
+		if (otg->phy->state == OTG_STATE_A_HOST) {
+			pm_runtime_get_sync(otg->phy->dev);
+			msm_otg_start_host(otg->phy, 0);
+			otg->host = NULL;
+			otg->phy->state = OTG_STATE_UNDEFINED;
+			schedule_work(&motg->sm_work);
+		} else {
+			otg->host = NULL;
+		}
+
+		return 0;
+	}
+
+	hcd = bus_to_hcd(host);
+	hcd->power_budget = motg->pdata->power_budget;
+
+	otg->host = host;
+	dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n");
+
+	/*
+	 * Kick the state machine work, if peripheral is not supported
+	 * or peripheral is already registered with us.
+	 */
+	if (motg->pdata->mode == USB_HOST || otg->gadget) {
+		pm_runtime_get_sync(otg->phy->dev);
+		schedule_work(&motg->sm_work);
+	}
+
+	return 0;
+}
+
+static void msm_otg_start_peripheral(struct usb_phy *phy, int on)
+{
+	struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+
+	if (!phy->otg->gadget)
+		return;
+
+	if (on) {
+		dev_dbg(phy->dev, "gadget on\n");
+		/*
+		 * Some boards have a switch cotrolled by gpio
+		 * to enable/disable internal HUB. Disable internal
+		 * HUB before kicking the gadget.
+		 */
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_B_PERIPHERAL);
+		usb_gadget_vbus_connect(phy->otg->gadget);
+	} else {
+		dev_dbg(phy->dev, "gadget off\n");
+		usb_gadget_vbus_disconnect(phy->otg->gadget);
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_UNDEFINED);
+	}
+
+}
+
+static int msm_otg_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+
+	/*
+	 * Fail peripheral registration if this board can support
+	 * only host configuration.
+	 */
+	if (motg->pdata->mode == USB_HOST) {
+		dev_info(otg->phy->dev, "Peripheral mode is not supported\n");
+		return -ENODEV;
+	}
+
+	if (!gadget) {
+		if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
+			pm_runtime_get_sync(otg->phy->dev);
+			msm_otg_start_peripheral(otg->phy, 0);
+			otg->gadget = NULL;
+			otg->phy->state = OTG_STATE_UNDEFINED;
+			schedule_work(&motg->sm_work);
+		} else {
+			otg->gadget = NULL;
+		}
+
+		return 0;
+	}
+	otg->gadget = gadget;
+	dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n");
+
+	/*
+	 * Kick the state machine work, if host is not supported
+	 * or host is already registered with us.
+	 */
+	if (motg->pdata->mode == USB_PERIPHERAL || otg->host) {
+		pm_runtime_get_sync(otg->phy->dev);
+		schedule_work(&motg->sm_work);
+	}
+
+	return 0;
+}
+
+static bool msm_chg_check_secondary_det(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 chg_det;
+	bool ret = false;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		ret = chg_det & (1 << 4);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x87);
+		ret = chg_det & 1;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static void msm_chg_enable_secondary_det(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 chg_det;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		/* Turn off charger block */
+		chg_det |= ~(1 << 1);
+		ulpi_write(phy, chg_det, 0x34);
+		udelay(20);
+		/* control chg block via ULPI */
+		chg_det &= ~(1 << 3);
+		ulpi_write(phy, chg_det, 0x34);
+		/* put it in host mode for enabling D- source */
+		chg_det &= ~(1 << 2);
+		ulpi_write(phy, chg_det, 0x34);
+		/* Turn on chg detect block */
+		chg_det &= ~(1 << 1);
+		ulpi_write(phy, chg_det, 0x34);
+		udelay(20);
+		/* enable chg detection */
+		chg_det &= ~(1 << 0);
+		ulpi_write(phy, chg_det, 0x34);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		/*
+		 * Configure DM as current source, DP as current sink
+		 * and enable battery charging comparators.
+		 */
+		ulpi_write(phy, 0x8, 0x85);
+		ulpi_write(phy, 0x2, 0x85);
+		ulpi_write(phy, 0x1, 0x85);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool msm_chg_check_primary_det(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 chg_det;
+	bool ret = false;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		ret = chg_det & (1 << 4);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x87);
+		ret = chg_det & 1;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static void msm_chg_enable_primary_det(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 chg_det;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		/* enable chg detection */
+		chg_det &= ~(1 << 0);
+		ulpi_write(phy, chg_det, 0x34);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		/*
+		 * Configure DP as current source, DM as current sink
+		 * and enable battery charging comparators.
+		 */
+		ulpi_write(phy, 0x2, 0x85);
+		ulpi_write(phy, 0x1, 0x85);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool msm_chg_check_dcd(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 line_state;
+	bool ret = false;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		line_state = ulpi_read(phy, 0x15);
+		ret = !(line_state & 1);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		line_state = ulpi_read(phy, 0x87);
+		ret = line_state & 2;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static void msm_chg_disable_dcd(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 chg_det;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		chg_det &= ~(1 << 5);
+		ulpi_write(phy, chg_det, 0x34);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		ulpi_write(phy, 0x10, 0x86);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_enable_dcd(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 chg_det;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		/* Turn on D+ current source */
+		chg_det |= (1 << 5);
+		ulpi_write(phy, chg_det, 0x34);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Data contact detection enable */
+		ulpi_write(phy, 0x10, 0x85);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_block_on(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 func_ctrl, chg_det;
+
+	/* put the controller in non-driving mode */
+	func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+	func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+	func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+	ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		/* control chg block via ULPI */
+		chg_det &= ~(1 << 3);
+		ulpi_write(phy, chg_det, 0x34);
+		/* Turn on chg detect block */
+		chg_det &= ~(1 << 1);
+		ulpi_write(phy, chg_det, 0x34);
+		udelay(20);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Clear charger detecting control bits */
+		ulpi_write(phy, 0x3F, 0x86);
+		/* Clear alt interrupt latch and enable bits */
+		ulpi_write(phy, 0x1F, 0x92);
+		ulpi_write(phy, 0x1F, 0x95);
+		udelay(100);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_chg_block_off(struct msm_otg *motg)
+{
+	struct usb_phy *phy = &motg->phy;
+	u32 func_ctrl, chg_det;
+
+	switch (motg->pdata->phy_type) {
+	case CI_45NM_INTEGRATED_PHY:
+		chg_det = ulpi_read(phy, 0x34);
+		/* Turn off charger block */
+		chg_det |= ~(1 << 1);
+		ulpi_write(phy, chg_det, 0x34);
+		break;
+	case SNPS_28NM_INTEGRATED_PHY:
+		/* Clear charger detecting control bits */
+		ulpi_write(phy, 0x3F, 0x86);
+		/* Clear alt interrupt latch and enable bits */
+		ulpi_write(phy, 0x1F, 0x92);
+		ulpi_write(phy, 0x1F, 0x95);
+		break;
+	default:
+		break;
+	}
+
+	/* put the controller in normal mode */
+	func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+	func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+	func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+	ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+}
+
+#define MSM_CHG_DCD_POLL_TIME		(100 * HZ/1000) /* 100 msec */
+#define MSM_CHG_DCD_MAX_RETRIES		6 /* Tdcd_tmout = 6 * 100 msec */
+#define MSM_CHG_PRIMARY_DET_TIME	(40 * HZ/1000) /* TVDPSRC_ON */
+#define MSM_CHG_SECONDARY_DET_TIME	(40 * HZ/1000) /* TVDMSRC_ON */
+static void msm_chg_detect_work(struct work_struct *w)
+{
+	struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
+	struct usb_phy *phy = &motg->phy;
+	bool is_dcd, tmout, vout;
+	unsigned long delay;
+
+	dev_dbg(phy->dev, "chg detection work\n");
+	switch (motg->chg_state) {
+	case USB_CHG_STATE_UNDEFINED:
+		pm_runtime_get_sync(phy->dev);
+		msm_chg_block_on(motg);
+		msm_chg_enable_dcd(motg);
+		motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+		motg->dcd_retries = 0;
+		delay = MSM_CHG_DCD_POLL_TIME;
+		break;
+	case USB_CHG_STATE_WAIT_FOR_DCD:
+		is_dcd = msm_chg_check_dcd(motg);
+		tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
+		if (is_dcd || tmout) {
+			msm_chg_disable_dcd(motg);
+			msm_chg_enable_primary_det(motg);
+			delay = MSM_CHG_PRIMARY_DET_TIME;
+			motg->chg_state = USB_CHG_STATE_DCD_DONE;
+		} else {
+			delay = MSM_CHG_DCD_POLL_TIME;
+		}
+		break;
+	case USB_CHG_STATE_DCD_DONE:
+		vout = msm_chg_check_primary_det(motg);
+		if (vout) {
+			msm_chg_enable_secondary_det(motg);
+			delay = MSM_CHG_SECONDARY_DET_TIME;
+			motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+		} else {
+			motg->chg_type = USB_SDP_CHARGER;
+			motg->chg_state = USB_CHG_STATE_DETECTED;
+			delay = 0;
+		}
+		break;
+	case USB_CHG_STATE_PRIMARY_DONE:
+		vout = msm_chg_check_secondary_det(motg);
+		if (vout)
+			motg->chg_type = USB_DCP_CHARGER;
+		else
+			motg->chg_type = USB_CDP_CHARGER;
+		motg->chg_state = USB_CHG_STATE_SECONDARY_DONE;
+		/* fall through */
+	case USB_CHG_STATE_SECONDARY_DONE:
+		motg->chg_state = USB_CHG_STATE_DETECTED;
+	case USB_CHG_STATE_DETECTED:
+		msm_chg_block_off(motg);
+		dev_dbg(phy->dev, "charger = %d\n", motg->chg_type);
+		schedule_work(&motg->sm_work);
+		return;
+	default:
+		return;
+	}
+
+	schedule_delayed_work(&motg->chg_work, delay);
+}
+
+/*
+ * We support OTG, Peripheral only and Host only configurations. In case
+ * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen
+ * via Id pin status or user request (debugfs). Id/BSV interrupts are not
+ * enabled when switch is controlled by user and default mode is supplied
+ * by board file, which can be changed by userspace later.
+ */
+static void msm_otg_init_sm(struct msm_otg *motg)
+{
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	u32 otgsc = readl(USB_OTGSC);
+
+	switch (pdata->mode) {
+	case USB_OTG:
+		if (pdata->otg_control == OTG_PHY_CONTROL) {
+			if (otgsc & OTGSC_ID)
+				set_bit(ID, &motg->inputs);
+			else
+				clear_bit(ID, &motg->inputs);
+
+			if (otgsc & OTGSC_BSV)
+				set_bit(B_SESS_VLD, &motg->inputs);
+			else
+				clear_bit(B_SESS_VLD, &motg->inputs);
+		} else if (pdata->otg_control == OTG_USER_CONTROL) {
+			if (pdata->default_mode == USB_HOST) {
+				clear_bit(ID, &motg->inputs);
+			} else if (pdata->default_mode == USB_PERIPHERAL) {
+				set_bit(ID, &motg->inputs);
+				set_bit(B_SESS_VLD, &motg->inputs);
+			} else {
+				set_bit(ID, &motg->inputs);
+				clear_bit(B_SESS_VLD, &motg->inputs);
+			}
+		}
+		break;
+	case USB_HOST:
+		clear_bit(ID, &motg->inputs);
+		break;
+	case USB_PERIPHERAL:
+		set_bit(ID, &motg->inputs);
+		if (otgsc & OTGSC_BSV)
+			set_bit(B_SESS_VLD, &motg->inputs);
+		else
+			clear_bit(B_SESS_VLD, &motg->inputs);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_otg_sm_work(struct work_struct *w)
+{
+	struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
+	struct usb_otg *otg = motg->phy.otg;
+
+	switch (otg->phy->state) {
+	case OTG_STATE_UNDEFINED:
+		dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n");
+		msm_otg_reset(otg->phy);
+		msm_otg_init_sm(motg);
+		otg->phy->state = OTG_STATE_B_IDLE;
+		/* FALL THROUGH */
+	case OTG_STATE_B_IDLE:
+		dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n");
+		if (!test_bit(ID, &motg->inputs) && otg->host) {
+			/* disable BSV bit */
+			writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
+			msm_otg_start_host(otg->phy, 1);
+			otg->phy->state = OTG_STATE_A_HOST;
+		} else if (test_bit(B_SESS_VLD, &motg->inputs)) {
+			switch (motg->chg_state) {
+			case USB_CHG_STATE_UNDEFINED:
+				msm_chg_detect_work(&motg->chg_work.work);
+				break;
+			case USB_CHG_STATE_DETECTED:
+				switch (motg->chg_type) {
+				case USB_DCP_CHARGER:
+					msm_otg_notify_charger(motg,
+							IDEV_CHG_MAX);
+					break;
+				case USB_CDP_CHARGER:
+					msm_otg_notify_charger(motg,
+							IDEV_CHG_MAX);
+					msm_otg_start_peripheral(otg->phy, 1);
+					otg->phy->state
+						= OTG_STATE_B_PERIPHERAL;
+					break;
+				case USB_SDP_CHARGER:
+					msm_otg_notify_charger(motg, IUNIT);
+					msm_otg_start_peripheral(otg->phy, 1);
+					otg->phy->state
+						= OTG_STATE_B_PERIPHERAL;
+					break;
+				default:
+					break;
+				}
+				break;
+			default:
+				break;
+			}
+		} else {
+			/*
+			 * If charger detection work is pending, decrement
+			 * the pm usage counter to balance with the one that
+			 * is incremented in charger detection work.
+			 */
+			if (cancel_delayed_work_sync(&motg->chg_work)) {
+				pm_runtime_put_sync(otg->phy->dev);
+				msm_otg_reset(otg->phy);
+			}
+			msm_otg_notify_charger(motg, 0);
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			motg->chg_type = USB_INVALID_CHARGER;
+		}
+		pm_runtime_put_sync(otg->phy->dev);
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n");
+		if (!test_bit(B_SESS_VLD, &motg->inputs) ||
+				!test_bit(ID, &motg->inputs)) {
+			msm_otg_notify_charger(motg, 0);
+			msm_otg_start_peripheral(otg->phy, 0);
+			motg->chg_state = USB_CHG_STATE_UNDEFINED;
+			motg->chg_type = USB_INVALID_CHARGER;
+			otg->phy->state = OTG_STATE_B_IDLE;
+			msm_otg_reset(otg->phy);
+			schedule_work(w);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n");
+		if (test_bit(ID, &motg->inputs)) {
+			msm_otg_start_host(otg->phy, 0);
+			otg->phy->state = OTG_STATE_B_IDLE;
+			msm_otg_reset(otg->phy);
+			schedule_work(w);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static irqreturn_t msm_otg_irq(int irq, void *data)
+{
+	struct msm_otg *motg = data;
+	struct usb_phy *phy = &motg->phy;
+	u32 otgsc = 0;
+
+	if (atomic_read(&motg->in_lpm)) {
+		disable_irq_nosync(irq);
+		motg->async_int = 1;
+		pm_runtime_get(phy->dev);
+		return IRQ_HANDLED;
+	}
+
+	otgsc = readl(USB_OTGSC);
+	if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS)))
+		return IRQ_NONE;
+
+	if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) {
+		if (otgsc & OTGSC_ID)
+			set_bit(ID, &motg->inputs);
+		else
+			clear_bit(ID, &motg->inputs);
+		dev_dbg(phy->dev, "ID set/clear\n");
+		pm_runtime_get_noresume(phy->dev);
+	} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
+		if (otgsc & OTGSC_BSV)
+			set_bit(B_SESS_VLD, &motg->inputs);
+		else
+			clear_bit(B_SESS_VLD, &motg->inputs);
+		dev_dbg(phy->dev, "BSV set/clear\n");
+		pm_runtime_get_noresume(phy->dev);
+	}
+
+	writel(otgsc, USB_OTGSC);
+	schedule_work(&motg->sm_work);
+	return IRQ_HANDLED;
+}
+
+static int msm_otg_mode_show(struct seq_file *s, void *unused)
+{
+	struct msm_otg *motg = s->private;
+	struct usb_otg *otg = motg->phy.otg;
+
+	switch (otg->phy->state) {
+	case OTG_STATE_A_HOST:
+		seq_printf(s, "host\n");
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		seq_printf(s, "peripheral\n");
+		break;
+	default:
+		seq_printf(s, "none\n");
+		break;
+	}
+
+	return 0;
+}
+
+static int msm_otg_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, msm_otg_mode_show, inode->i_private);
+}
+
+static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	struct seq_file *s = file->private_data;
+	struct msm_otg *motg = s->private;
+	char buf[16];
+	struct usb_otg *otg = motg->phy.otg;
+	int status = count;
+	enum usb_mode_type req_mode;
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
+		status = -EFAULT;
+		goto out;
+	}
+
+	if (!strncmp(buf, "host", 4)) {
+		req_mode = USB_HOST;
+	} else if (!strncmp(buf, "peripheral", 10)) {
+		req_mode = USB_PERIPHERAL;
+	} else if (!strncmp(buf, "none", 4)) {
+		req_mode = USB_NONE;
+	} else {
+		status = -EINVAL;
+		goto out;
+	}
+
+	switch (req_mode) {
+	case USB_NONE:
+		switch (otg->phy->state) {
+		case OTG_STATE_A_HOST:
+		case OTG_STATE_B_PERIPHERAL:
+			set_bit(ID, &motg->inputs);
+			clear_bit(B_SESS_VLD, &motg->inputs);
+			break;
+		default:
+			goto out;
+		}
+		break;
+	case USB_PERIPHERAL:
+		switch (otg->phy->state) {
+		case OTG_STATE_B_IDLE:
+		case OTG_STATE_A_HOST:
+			set_bit(ID, &motg->inputs);
+			set_bit(B_SESS_VLD, &motg->inputs);
+			break;
+		default:
+			goto out;
+		}
+		break;
+	case USB_HOST:
+		switch (otg->phy->state) {
+		case OTG_STATE_B_IDLE:
+		case OTG_STATE_B_PERIPHERAL:
+			clear_bit(ID, &motg->inputs);
+			break;
+		default:
+			goto out;
+		}
+		break;
+	default:
+		goto out;
+	}
+
+	pm_runtime_get_sync(otg->phy->dev);
+	schedule_work(&motg->sm_work);
+out:
+	return status;
+}
+
+const struct file_operations msm_otg_mode_fops = {
+	.open = msm_otg_mode_open,
+	.read = seq_read,
+	.write = msm_otg_mode_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static struct dentry *msm_otg_dbg_root;
+static struct dentry *msm_otg_dbg_mode;
+
+static int msm_otg_debugfs_init(struct msm_otg *motg)
+{
+	msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL);
+
+	if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root))
+		return -ENODEV;
+
+	msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR,
+				msm_otg_dbg_root, motg, &msm_otg_mode_fops);
+	if (!msm_otg_dbg_mode) {
+		debugfs_remove(msm_otg_dbg_root);
+		msm_otg_dbg_root = NULL;
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void msm_otg_debugfs_cleanup(void)
+{
+	debugfs_remove(msm_otg_dbg_mode);
+	debugfs_remove(msm_otg_dbg_root);
+}
+
+static int __init msm_otg_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+	struct msm_otg *motg;
+	struct usb_phy *phy;
+
+	dev_info(&pdev->dev, "msm_otg probe\n");
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
+		return -ENODEV;
+	}
+
+	motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+	if (!motg) {
+		dev_err(&pdev->dev, "unable to allocate msm_otg\n");
+		return -ENOMEM;
+	}
+
+	motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+	if (!motg->phy.otg) {
+		dev_err(&pdev->dev, "unable to allocate msm_otg\n");
+		return -ENOMEM;
+	}
+
+	motg->pdata = pdev->dev.platform_data;
+	phy = &motg->phy;
+	phy->dev = &pdev->dev;
+
+	motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk");
+	if (IS_ERR(motg->phy_reset_clk)) {
+		dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
+		ret = PTR_ERR(motg->phy_reset_clk);
+		goto free_motg;
+	}
+
+	motg->clk = clk_get(&pdev->dev, "usb_hs_clk");
+	if (IS_ERR(motg->clk)) {
+		dev_err(&pdev->dev, "failed to get usb_hs_clk\n");
+		ret = PTR_ERR(motg->clk);
+		goto put_phy_reset_clk;
+	}
+	clk_set_rate(motg->clk, 60000000);
+
+	/*
+	 * If USB Core is running its protocol engine based on CORE CLK,
+	 * CORE CLK  must be running at >55Mhz for correct HSUSB
+	 * operation and USB core cannot tolerate frequency changes on
+	 * CORE CLK. For such USB cores, vote for maximum clk frequency
+	 * on pclk source
+	 */
+	 if (motg->pdata->pclk_src_name) {
+		motg->pclk_src = clk_get(&pdev->dev,
+			motg->pdata->pclk_src_name);
+		if (IS_ERR(motg->pclk_src))
+			goto put_clk;
+		clk_set_rate(motg->pclk_src, INT_MAX);
+		clk_enable(motg->pclk_src);
+	} else
+		motg->pclk_src = ERR_PTR(-ENOENT);
+
+
+	motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
+	if (IS_ERR(motg->pclk)) {
+		dev_err(&pdev->dev, "failed to get usb_hs_pclk\n");
+		ret = PTR_ERR(motg->pclk);
+		goto put_pclk_src;
+	}
+
+	/*
+	 * USB core clock is not present on all MSM chips. This
+	 * clock is introduced to remove the dependency on AXI
+	 * bus frequency.
+	 */
+	motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk");
+	if (IS_ERR(motg->core_clk))
+		motg->core_clk = NULL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get platform resource mem\n");
+		ret = -ENODEV;
+		goto put_core_clk;
+	}
+
+	motg->regs = ioremap(res->start, resource_size(res));
+	if (!motg->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_core_clk;
+	}
+	dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs);
+
+	motg->irq = platform_get_irq(pdev, 0);
+	if (!motg->irq) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		ret = -ENODEV;
+		goto free_regs;
+	}
+
+	clk_enable(motg->clk);
+	clk_enable(motg->pclk);
+
+	ret = msm_hsusb_init_vddcx(motg, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
+		goto free_regs;
+	}
+
+	ret = msm_hsusb_ldo_init(motg, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
+		goto vddcx_exit;
+	}
+	ret = msm_hsusb_ldo_set_mode(1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+		goto ldo_exit;
+	}
+
+	if (motg->core_clk)
+		clk_enable(motg->core_clk);
+
+	writel(0, USB_USBINTR);
+	writel(0, USB_OTGSC);
+
+	INIT_WORK(&motg->sm_work, msm_otg_sm_work);
+	INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
+	ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
+					"msm_otg", motg);
+	if (ret) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		goto disable_clks;
+	}
+
+	phy->init = msm_otg_reset;
+	phy->set_power = msm_otg_set_power;
+
+	phy->io_ops = &msm_otg_io_ops;
+
+	phy->otg->phy = &motg->phy;
+	phy->otg->set_host = msm_otg_set_host;
+	phy->otg->set_peripheral = msm_otg_set_peripheral;
+
+	ret = usb_set_transceiver(&motg->phy);
+	if (ret) {
+		dev_err(&pdev->dev, "usb_set_transceiver failed\n");
+		goto free_irq;
+	}
+
+	platform_set_drvdata(pdev, motg);
+	device_init_wakeup(&pdev->dev, 1);
+
+	if (motg->pdata->mode == USB_OTG &&
+			motg->pdata->otg_control == OTG_USER_CONTROL) {
+		ret = msm_otg_debugfs_init(motg);
+		if (ret)
+			dev_dbg(&pdev->dev, "mode debugfs file is"
+					"not available\n");
+	}
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+free_irq:
+	free_irq(motg->irq, motg);
+disable_clks:
+	clk_disable(motg->pclk);
+	clk_disable(motg->clk);
+ldo_exit:
+	msm_hsusb_ldo_init(motg, 0);
+vddcx_exit:
+	msm_hsusb_init_vddcx(motg, 0);
+free_regs:
+	iounmap(motg->regs);
+put_core_clk:
+	if (motg->core_clk)
+		clk_put(motg->core_clk);
+	clk_put(motg->pclk);
+put_pclk_src:
+	if (!IS_ERR(motg->pclk_src)) {
+		clk_disable(motg->pclk_src);
+		clk_put(motg->pclk_src);
+	}
+put_clk:
+	clk_put(motg->clk);
+put_phy_reset_clk:
+	clk_put(motg->phy_reset_clk);
+free_motg:
+	kfree(motg->phy.otg);
+	kfree(motg);
+	return ret;
+}
+
+static int __devexit msm_otg_remove(struct platform_device *pdev)
+{
+	struct msm_otg *motg = platform_get_drvdata(pdev);
+	struct usb_phy *phy = &motg->phy;
+	int cnt = 0;
+
+	if (phy->otg->host || phy->otg->gadget)
+		return -EBUSY;
+
+	msm_otg_debugfs_cleanup();
+	cancel_delayed_work_sync(&motg->chg_work);
+	cancel_work_sync(&motg->sm_work);
+
+	pm_runtime_resume(&pdev->dev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+
+	usb_set_transceiver(NULL);
+	free_irq(motg->irq, motg);
+
+	/*
+	 * Put PHY in low power mode.
+	 */
+	ulpi_read(phy, 0x14);
+	ulpi_write(phy, 0x08, 0x09);
+
+	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC)
+		dev_err(phy->dev, "Unable to suspend PHY\n");
+
+	clk_disable(motg->pclk);
+	clk_disable(motg->clk);
+	if (motg->core_clk)
+		clk_disable(motg->core_clk);
+	if (!IS_ERR(motg->pclk_src)) {
+		clk_disable(motg->pclk_src);
+		clk_put(motg->pclk_src);
+	}
+	msm_hsusb_ldo_init(motg, 0);
+
+	iounmap(motg->regs);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	clk_put(motg->phy_reset_clk);
+	clk_put(motg->pclk);
+	clk_put(motg->clk);
+	if (motg->core_clk)
+		clk_put(motg->core_clk);
+
+	kfree(motg->phy.otg);
+	kfree(motg);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_otg_runtime_idle(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+	struct usb_otg *otg = motg->phy.otg;
+
+	dev_dbg(dev, "OTG runtime idle\n");
+
+	/*
+	 * It is observed some times that a spurious interrupt
+	 * comes when PHY is put into LPM immediately after PHY reset.
+	 * This 1 sec delay also prevents entering into LPM immediately
+	 * after asynchronous interrupt.
+	 */
+	if (otg->phy->state != OTG_STATE_UNDEFINED)
+		pm_schedule_suspend(dev, 1000);
+
+	return -EAGAIN;
+}
+
+static int msm_otg_runtime_suspend(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "OTG runtime suspend\n");
+	return msm_otg_suspend(motg);
+}
+
+static int msm_otg_runtime_resume(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "OTG runtime resume\n");
+	return msm_otg_resume(motg);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_otg_pm_suspend(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "OTG PM suspend\n");
+	return msm_otg_suspend(motg);
+}
+
+static int msm_otg_pm_resume(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(dev, "OTG PM resume\n");
+
+	ret = msm_otg_resume(motg);
+	if (ret)
+		return ret;
+
+	/*
+	 * Runtime PM Documentation recommends bringing the
+	 * device to full powered state upon resume.
+	 */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_otg_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume)
+	SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume,
+				msm_otg_runtime_idle)
+};
+#endif
+
+static struct platform_driver msm_otg_driver = {
+	.remove = __devexit_p(msm_otg_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &msm_otg_dev_pm_ops,
+#endif
+	},
+};
+
+static int __init msm_otg_init(void)
+{
+	return platform_driver_probe(&msm_otg_driver, msm_otg_probe);
+}
+
+static void __exit msm_otg_exit(void)
+{
+	platform_driver_unregister(&msm_otg_driver);
+}
+
+module_init(msm_otg_init);
+module_exit(msm_otg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM USB transceiver driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/mv_otg.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/mv_otg.c
new file mode 100644
index 0000000..6cc6c3f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/mv_otg.c
@@ -0,0 +1,973 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ * Author: Chao Xie <chao.xie@marvell.com>
+ *	   Neil Zhang <zhangwm@marvell.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_data/mv_usb.h>
+
+#include "mv_otg.h"
+
+#define	DRIVER_DESC	"Marvell USB OTG transceiver driver"
+#define	DRIVER_VERSION	"Jan 20, 2010"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+static const char driver_name[] = "mv-otg";
+
+static char *state_string[] = {
+	"undefined",
+	"b_idle",
+	"b_srp_init",
+	"b_peripheral",
+	"b_wait_acon",
+	"b_host",
+	"a_idle",
+	"a_wait_vrise",
+	"a_wait_bcon",
+	"a_host",
+	"a_suspend",
+	"a_peripheral",
+	"a_wait_vfall",
+	"a_vbus_err"
+};
+
+static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
+{
+	struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy);
+	if (mvotg->pdata->set_vbus == NULL)
+		return -ENODEV;
+
+	return mvotg->pdata->set_vbus(on);
+}
+
+static int mv_otg_set_host(struct usb_otg *otg,
+			   struct usb_bus *host)
+{
+	otg->host = host;
+
+	return 0;
+}
+
+static int mv_otg_set_peripheral(struct usb_otg *otg,
+				 struct usb_gadget *gadget)
+{
+	otg->gadget = gadget;
+
+	return 0;
+}
+
+static void mv_otg_run_state_machine(struct mv_otg *mvotg,
+				     unsigned long delay)
+{
+	dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
+	if (!mvotg->qwork)
+		return;
+
+	queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
+}
+
+static void mv_otg_timer_await_bcon(unsigned long data)
+{
+	struct mv_otg *mvotg = (struct mv_otg *) data;
+
+	mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
+
+	dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+}
+
+static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
+{
+	struct timer_list *timer;
+
+	if (id >= OTG_TIMER_NUM)
+		return -EINVAL;
+
+	timer = &mvotg->otg_ctrl.timer[id];
+
+	if (timer_pending(timer))
+		del_timer(timer);
+
+	return 0;
+}
+
+static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
+			    unsigned long interval,
+			    void (*callback) (unsigned long))
+{
+	struct timer_list *timer;
+
+	if (id >= OTG_TIMER_NUM)
+		return -EINVAL;
+
+	timer = &mvotg->otg_ctrl.timer[id];
+	if (timer_pending(timer)) {
+		dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
+		return -EBUSY;
+	}
+
+	init_timer(timer);
+	timer->data = (unsigned long) mvotg;
+	timer->function = callback;
+	timer->expires = jiffies + interval;
+	add_timer(timer);
+
+	return 0;
+}
+
+static int mv_otg_reset(struct mv_otg *mvotg)
+{
+	unsigned int loops;
+	u32 tmp;
+
+	/* Stop the controller */
+	tmp = readl(&mvotg->op_regs->usbcmd);
+	tmp &= ~USBCMD_RUN_STOP;
+	writel(tmp, &mvotg->op_regs->usbcmd);
+
+	/* Reset the controller to get default values */
+	writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
+
+	loops = 500;
+	while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
+		if (loops == 0) {
+			dev_err(&mvotg->pdev->dev,
+				"Wait for RESET completed TIMEOUT\n");
+			return -ETIMEDOUT;
+		}
+		loops--;
+		udelay(20);
+	}
+
+	writel(0x0, &mvotg->op_regs->usbintr);
+	tmp = readl(&mvotg->op_regs->usbsts);
+	writel(tmp, &mvotg->op_regs->usbsts);
+
+	return 0;
+}
+
+static void mv_otg_init_irq(struct mv_otg *mvotg)
+{
+	u32 otgsc;
+
+	mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
+	    | OTGSC_INTR_A_VBUS_VALID;
+	mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
+	    | OTGSC_INTSTS_A_VBUS_VALID;
+
+	if (mvotg->pdata->vbus == NULL) {
+		mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
+		    | OTGSC_INTR_B_SESSION_END;
+		mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
+		    | OTGSC_INTSTS_B_SESSION_END;
+	}
+
+	if (mvotg->pdata->id == NULL) {
+		mvotg->irq_en |= OTGSC_INTR_USB_ID;
+		mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
+	}
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+	otgsc |= mvotg->irq_en;
+	writel(otgsc, &mvotg->op_regs->otgsc);
+}
+
+static void mv_otg_start_host(struct mv_otg *mvotg, int on)
+{
+#ifdef CONFIG_USB
+	struct usb_otg *otg = mvotg->phy.otg;
+	struct usb_hcd *hcd;
+
+	if (!otg->host)
+		return;
+
+	dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
+
+	hcd = bus_to_hcd(otg->host);
+
+	if (on)
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	else
+		usb_remove_hcd(hcd);
+#endif /* CONFIG_USB */
+}
+
+static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
+{
+	struct usb_otg *otg = mvotg->phy.otg;
+
+	if (!otg->gadget)
+		return;
+
+	dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off");
+
+	if (on)
+		usb_gadget_vbus_connect(otg->gadget);
+	else
+		usb_gadget_vbus_disconnect(otg->gadget);
+}
+
+static void otg_clock_enable(struct mv_otg *mvotg)
+{
+	unsigned int i;
+
+	for (i = 0; i < mvotg->clknum; i++)
+		clk_enable(mvotg->clk[i]);
+}
+
+static void otg_clock_disable(struct mv_otg *mvotg)
+{
+	unsigned int i;
+
+	for (i = 0; i < mvotg->clknum; i++)
+		clk_disable(mvotg->clk[i]);
+}
+
+static int mv_otg_enable_internal(struct mv_otg *mvotg)
+{
+	int retval = 0;
+
+	if (mvotg->active)
+		return 0;
+
+	dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
+
+	otg_clock_enable(mvotg);
+	if (mvotg->pdata->phy_init) {
+		retval = mvotg->pdata->phy_init(mvotg->phy_regs);
+		if (retval) {
+			dev_err(&mvotg->pdev->dev,
+				"init phy error %d\n", retval);
+			otg_clock_disable(mvotg);
+			return retval;
+		}
+	}
+	mvotg->active = 1;
+
+	return 0;
+
+}
+
+static int mv_otg_enable(struct mv_otg *mvotg)
+{
+	if (mvotg->clock_gating)
+		return mv_otg_enable_internal(mvotg);
+
+	return 0;
+}
+
+static void mv_otg_disable_internal(struct mv_otg *mvotg)
+{
+	if (mvotg->active) {
+		dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
+		if (mvotg->pdata->phy_deinit)
+			mvotg->pdata->phy_deinit(mvotg->phy_regs);
+		otg_clock_disable(mvotg);
+		mvotg->active = 0;
+	}
+}
+
+static void mv_otg_disable(struct mv_otg *mvotg)
+{
+	if (mvotg->clock_gating)
+		mv_otg_disable_internal(mvotg);
+}
+
+static void mv_otg_update_inputs(struct mv_otg *mvotg)
+{
+	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+	u32 otgsc;
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+
+	if (mvotg->pdata->vbus) {
+		if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
+			otg_ctrl->b_sess_vld = 1;
+			otg_ctrl->b_sess_end = 0;
+		} else {
+			otg_ctrl->b_sess_vld = 0;
+			otg_ctrl->b_sess_end = 1;
+		}
+	} else {
+		otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
+		otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
+	}
+
+	if (mvotg->pdata->id)
+		otg_ctrl->id = !!mvotg->pdata->id->poll();
+	else
+		otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
+
+	if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
+		otg_ctrl->a_bus_req = 1;
+
+	otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
+	otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
+
+	dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
+	dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id);
+	dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
+	dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
+	dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
+	dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
+}
+
+static void mv_otg_update_state(struct mv_otg *mvotg)
+{
+	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+	struct usb_phy *phy = &mvotg->phy;
+	int old_state = phy->state;
+
+	switch (old_state) {
+	case OTG_STATE_UNDEFINED:
+		phy->state = OTG_STATE_B_IDLE;
+		/* FALL THROUGH */
+	case OTG_STATE_B_IDLE:
+		if (otg_ctrl->id == 0)
+			phy->state = OTG_STATE_A_IDLE;
+		else if (otg_ctrl->b_sess_vld)
+			phy->state = OTG_STATE_B_PERIPHERAL;
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
+			phy->state = OTG_STATE_B_IDLE;
+		break;
+	case OTG_STATE_A_IDLE:
+		if (otg_ctrl->id)
+			phy->state = OTG_STATE_B_IDLE;
+		else if (!(otg_ctrl->a_bus_drop) &&
+			 (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
+			phy->state = OTG_STATE_A_WAIT_VRISE;
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		if (otg_ctrl->a_vbus_vld)
+			phy->state = OTG_STATE_A_WAIT_BCON;
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if (otg_ctrl->id || otg_ctrl->a_bus_drop
+		    || otg_ctrl->a_wait_bcon_timeout) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+			phy->state = OTG_STATE_A_WAIT_VFALL;
+			otg_ctrl->a_bus_req = 0;
+		} else if (!otg_ctrl->a_vbus_vld) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+			phy->state = OTG_STATE_A_VBUS_ERR;
+		} else if (otg_ctrl->b_conn) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+			phy->state = OTG_STATE_A_HOST;
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if (otg_ctrl->id || !otg_ctrl->b_conn
+		    || otg_ctrl->a_bus_drop)
+			phy->state = OTG_STATE_A_WAIT_BCON;
+		else if (!otg_ctrl->a_vbus_vld)
+			phy->state = OTG_STATE_A_VBUS_ERR;
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (otg_ctrl->id
+		    || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
+		    || otg_ctrl->a_bus_req)
+			phy->state = OTG_STATE_A_IDLE;
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if (otg_ctrl->id || otg_ctrl->a_clr_err
+		    || otg_ctrl->a_bus_drop) {
+			otg_ctrl->a_clr_err = 0;
+			phy->state = OTG_STATE_A_WAIT_VFALL;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void mv_otg_work(struct work_struct *work)
+{
+	struct mv_otg *mvotg;
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+	int old_state;
+
+	mvotg = container_of((struct delayed_work *)work, struct mv_otg, work);
+
+run:
+	/* work queue is single thread, or we need spin_lock to protect */
+	phy = &mvotg->phy;
+	otg = phy->otg;
+	old_state = phy->state;
+
+	if (!mvotg->active)
+		return;
+
+	mv_otg_update_inputs(mvotg);
+	mv_otg_update_state(mvotg);
+
+	if (old_state != phy->state) {
+		dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
+			 state_string[old_state],
+			 state_string[phy->state]);
+
+		switch (phy->state) {
+		case OTG_STATE_B_IDLE:
+			otg->default_a = 0;
+			if (old_state == OTG_STATE_B_PERIPHERAL)
+				mv_otg_start_periphrals(mvotg, 0);
+			mv_otg_reset(mvotg);
+			mv_otg_disable(mvotg);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			mv_otg_enable(mvotg);
+			mv_otg_start_periphrals(mvotg, 1);
+			break;
+		case OTG_STATE_A_IDLE:
+			otg->default_a = 1;
+			mv_otg_enable(mvotg);
+			if (old_state == OTG_STATE_A_WAIT_VFALL)
+				mv_otg_start_host(mvotg, 0);
+			mv_otg_reset(mvotg);
+			break;
+		case OTG_STATE_A_WAIT_VRISE:
+			mv_otg_set_vbus(otg, 1);
+			break;
+		case OTG_STATE_A_WAIT_BCON:
+			if (old_state != OTG_STATE_A_HOST)
+				mv_otg_start_host(mvotg, 1);
+			mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
+					 T_A_WAIT_BCON,
+					 mv_otg_timer_await_bcon);
+			/*
+			 * Now, we directly enter A_HOST. So set b_conn = 1
+			 * here. In fact, it need host driver to notify us.
+			 */
+			mvotg->otg_ctrl.b_conn = 1;
+			break;
+		case OTG_STATE_A_HOST:
+			break;
+		case OTG_STATE_A_WAIT_VFALL:
+			/*
+			 * Now, we has exited A_HOST. So set b_conn = 0
+			 * here. In fact, it need host driver to notify us.
+			 */
+			mvotg->otg_ctrl.b_conn = 0;
+			mv_otg_set_vbus(otg, 0);
+			break;
+		case OTG_STATE_A_VBUS_ERR:
+			break;
+		default:
+			break;
+		}
+		goto run;
+	}
+}
+
+static irqreturn_t mv_otg_irq(int irq, void *dev)
+{
+	struct mv_otg *mvotg = dev;
+	u32 otgsc;
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+	writel(otgsc, &mvotg->op_regs->otgsc);
+
+	/*
+	 * if we have vbus, then the vbus detection for B-device
+	 * will be done by mv_otg_inputs_irq().
+	 */
+	if (mvotg->pdata->vbus)
+		if ((otgsc & OTGSC_STS_USB_ID) &&
+		    !(otgsc & OTGSC_INTSTS_USB_ID))
+			return IRQ_NONE;
+
+	if ((otgsc & mvotg->irq_status) == 0)
+		return IRQ_NONE;
+
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
+{
+	struct mv_otg *mvotg = dev;
+
+	/* The clock may disabled at this time */
+	if (!mvotg->active) {
+		mv_otg_enable(mvotg);
+		mv_otg_init_irq(mvotg);
+	}
+
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t
+get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			 mvotg->otg_ctrl.a_bus_req);
+}
+
+static ssize_t
+set_a_bus_req(struct device *dev, struct device_attribute *attr,
+	      const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+
+	if (count > 2)
+		return -1;
+
+	/* We will use this interface to change to A device */
+	if (mvotg->phy.state != OTG_STATE_B_IDLE
+	    && mvotg->phy.state != OTG_STATE_A_IDLE)
+		return -1;
+
+	/* The clock may disabled and we need to set irq for ID detected */
+	mv_otg_enable(mvotg);
+	mv_otg_init_irq(mvotg);
+
+	if (buf[0] == '1') {
+		mvotg->otg_ctrl.a_bus_req = 1;
+		mvotg->otg_ctrl.a_bus_drop = 0;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_bus_req = 1\n");
+
+		if (spin_trylock(&mvotg->wq_lock)) {
+			mv_otg_run_state_machine(mvotg, 0);
+			spin_unlock(&mvotg->wq_lock);
+		}
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req,
+		   set_a_bus_req);
+
+static ssize_t
+set_a_clr_err(struct device *dev, struct device_attribute *attr,
+	      const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	if (!mvotg->phy.otg->default_a)
+		return -1;
+
+	if (count > 2)
+		return -1;
+
+	if (buf[0] == '1') {
+		mvotg->otg_ctrl.a_clr_err = 1;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_clr_err = 1\n");
+	}
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
+
+static ssize_t
+get_a_bus_drop(struct device *dev, struct device_attribute *attr,
+	       char *buf)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			 mvotg->otg_ctrl.a_bus_drop);
+}
+
+static ssize_t
+set_a_bus_drop(struct device *dev, struct device_attribute *attr,
+	       const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	if (!mvotg->phy.otg->default_a)
+		return -1;
+
+	if (count > 2)
+		return -1;
+
+	if (buf[0] == '0') {
+		mvotg->otg_ctrl.a_bus_drop = 0;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_bus_drop = 0\n");
+	} else if (buf[0] == '1') {
+		mvotg->otg_ctrl.a_bus_drop = 1;
+		mvotg->otg_ctrl.a_bus_req = 0;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_bus_drop = 1\n");
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: and a_bus_req = 0\n");
+	}
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR,
+		   get_a_bus_drop, set_a_bus_drop);
+
+static struct attribute *inputs_attrs[] = {
+	&dev_attr_a_bus_req.attr,
+	&dev_attr_a_clr_err.attr,
+	&dev_attr_a_bus_drop.attr,
+	NULL,
+};
+
+static struct attribute_group inputs_attr_group = {
+	.name = "inputs",
+	.attrs = inputs_attrs,
+};
+
+int mv_otg_remove(struct platform_device *pdev)
+{
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+	int clk_i;
+
+	sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group);
+
+	if (mvotg->irq)
+		free_irq(mvotg->irq, mvotg);
+
+	if (mvotg->pdata->vbus)
+		free_irq(mvotg->pdata->vbus->irq, mvotg);
+	if (mvotg->pdata->id)
+		free_irq(mvotg->pdata->id->irq, mvotg);
+
+	if (mvotg->qwork) {
+		flush_workqueue(mvotg->qwork);
+		destroy_workqueue(mvotg->qwork);
+	}
+
+	mv_otg_disable(mvotg);
+
+	if (mvotg->cap_regs)
+		iounmap(mvotg->cap_regs);
+
+	if (mvotg->phy_regs)
+		iounmap(mvotg->phy_regs);
+
+	for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++)
+		clk_put(mvotg->clk[clk_i]);
+
+	usb_set_transceiver(NULL);
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(mvotg->phy.otg);
+	kfree(mvotg);
+
+	return 0;
+}
+
+static int mv_otg_probe(struct platform_device *pdev)
+{
+	struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+	struct mv_otg *mvotg;
+	struct usb_otg *otg;
+	struct resource *r;
+	int retval = 0, clk_i, i;
+	size_t size;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "failed to get platform data\n");
+		return -ENODEV;
+	}
+
+	size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum;
+	mvotg = kzalloc(size, GFP_KERNEL);
+	if (!mvotg) {
+		dev_err(&pdev->dev, "failed to allocate memory!\n");
+		return -ENOMEM;
+	}
+
+	otg = kzalloc(sizeof *otg, GFP_KERNEL);
+	if (!otg) {
+		kfree(mvotg);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, mvotg);
+
+	mvotg->pdev = pdev;
+	mvotg->pdata = pdata;
+
+	mvotg->clknum = pdata->clknum;
+	for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) {
+		mvotg->clk[clk_i] = clk_get(&pdev->dev, pdata->clkname[clk_i]);
+		if (IS_ERR(mvotg->clk[clk_i])) {
+			retval = PTR_ERR(mvotg->clk[clk_i]);
+			goto err_put_clk;
+		}
+	}
+
+	mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
+	if (!mvotg->qwork) {
+		dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
+		retval = -ENOMEM;
+		goto err_put_clk;
+	}
+
+	INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
+
+	/* OTG common part */
+	mvotg->pdev = pdev;
+	mvotg->phy.dev = &pdev->dev;
+	mvotg->phy.otg = otg;
+	mvotg->phy.label = driver_name;
+	mvotg->phy.state = OTG_STATE_UNDEFINED;
+
+	otg->phy = &mvotg->phy;
+	otg->set_host = mv_otg_set_host;
+	otg->set_peripheral = mv_otg_set_peripheral;
+	otg->set_vbus = mv_otg_set_vbus;
+
+	for (i = 0; i < OTG_TIMER_NUM; i++)
+		init_timer(&mvotg->otg_ctrl.timer[i]);
+
+	r = platform_get_resource_byname(mvotg->pdev,
+					 IORESOURCE_MEM, "phyregs");
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
+		retval = -ENODEV;
+		goto err_destroy_workqueue;
+	}
+
+	mvotg->phy_regs = ioremap(r->start, resource_size(r));
+	if (mvotg->phy_regs == NULL) {
+		dev_err(&pdev->dev, "failed to map phy I/O memory\n");
+		retval = -EFAULT;
+		goto err_destroy_workqueue;
+	}
+
+	r = platform_get_resource_byname(mvotg->pdev,
+					 IORESOURCE_MEM, "capregs");
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		retval = -ENODEV;
+		goto err_unmap_phyreg;
+	}
+
+	mvotg->cap_regs = ioremap(r->start, resource_size(r));
+	if (mvotg->cap_regs == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		retval = -EFAULT;
+		goto err_unmap_phyreg;
+	}
+
+	/* we will acces controller register, so enable the udc controller */
+	retval = mv_otg_enable_internal(mvotg);
+	if (retval) {
+		dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
+		goto err_unmap_capreg;
+	}
+
+	mvotg->op_regs =
+		(struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
+			+ (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
+
+	if (pdata->id) {
+		retval = request_threaded_irq(pdata->id->irq, NULL,
+					      mv_otg_inputs_irq,
+					      IRQF_ONESHOT, "id", mvotg);
+		if (retval) {
+			dev_info(&pdev->dev,
+				 "Failed to request irq for ID\n");
+			pdata->id = NULL;
+		}
+	}
+
+	if (pdata->vbus) {
+		mvotg->clock_gating = 1;
+		retval = request_threaded_irq(pdata->vbus->irq, NULL,
+					      mv_otg_inputs_irq,
+					      IRQF_ONESHOT, "vbus", mvotg);
+		if (retval) {
+			dev_info(&pdev->dev,
+				 "Failed to request irq for VBUS, "
+				 "disable clock gating\n");
+			mvotg->clock_gating = 0;
+			pdata->vbus = NULL;
+		}
+	}
+
+	if (pdata->disable_otg_clock_gating)
+		mvotg->clock_gating = 0;
+
+	mv_otg_reset(mvotg);
+	mv_otg_init_irq(mvotg);
+
+	r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no IRQ resource defined\n");
+		retval = -ENODEV;
+		goto err_disable_clk;
+	}
+
+	mvotg->irq = r->start;
+	if (request_irq(mvotg->irq, mv_otg_irq, IRQF_SHARED,
+			driver_name, mvotg)) {
+		dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
+			mvotg->irq);
+		mvotg->irq = 0;
+		retval = -ENODEV;
+		goto err_disable_clk;
+	}
+
+	retval = usb_set_transceiver(&mvotg->phy);
+	if (retval < 0) {
+		dev_err(&pdev->dev, "can't register transceiver, %d\n",
+			retval);
+		goto err_free_irq;
+	}
+
+	retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group);
+	if (retval < 0) {
+		dev_dbg(&pdev->dev,
+			"Can't register sysfs attr group: %d\n", retval);
+		goto err_set_transceiver;
+	}
+
+	spin_lock_init(&mvotg->wq_lock);
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 2 * HZ);
+		spin_unlock(&mvotg->wq_lock);
+	}
+
+	dev_info(&pdev->dev,
+		 "successful probe OTG device %s clock gating.\n",
+		 mvotg->clock_gating ? "with" : "without");
+
+	return 0;
+
+err_set_transceiver:
+	usb_set_transceiver(NULL);
+err_free_irq:
+	free_irq(mvotg->irq, mvotg);
+err_disable_clk:
+	if (pdata->vbus)
+		free_irq(pdata->vbus->irq, mvotg);
+	if (pdata->id)
+		free_irq(pdata->id->irq, mvotg);
+	mv_otg_disable_internal(mvotg);
+err_unmap_capreg:
+	iounmap(mvotg->cap_regs);
+err_unmap_phyreg:
+	iounmap(mvotg->phy_regs);
+err_destroy_workqueue:
+	flush_workqueue(mvotg->qwork);
+	destroy_workqueue(mvotg->qwork);
+err_put_clk:
+	for (clk_i--; clk_i >= 0; clk_i--)
+		clk_put(mvotg->clk[clk_i]);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(otg);
+	kfree(mvotg);
+
+	return retval;
+}
+
+#ifdef CONFIG_PM
+static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+
+	if (mvotg->phy.state != OTG_STATE_B_IDLE) {
+		dev_info(&pdev->dev,
+			 "OTG state is not B_IDLE, it is %d!\n",
+			 mvotg->phy.state);
+		return -EAGAIN;
+	}
+
+	if (!mvotg->clock_gating)
+		mv_otg_disable_internal(mvotg);
+
+	return 0;
+}
+
+static int mv_otg_resume(struct platform_device *pdev)
+{
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+	u32 otgsc;
+
+	if (!mvotg->clock_gating) {
+		mv_otg_enable_internal(mvotg);
+
+		otgsc = readl(&mvotg->op_regs->otgsc);
+		otgsc |= mvotg->irq_en;
+		writel(otgsc, &mvotg->op_regs->otgsc);
+
+		if (spin_trylock(&mvotg->wq_lock)) {
+			mv_otg_run_state_machine(mvotg, 0);
+			spin_unlock(&mvotg->wq_lock);
+		}
+	}
+	return 0;
+}
+#endif
+
+static struct platform_driver mv_otg_driver = {
+	.probe = mv_otg_probe,
+	.remove = __exit_p(mv_otg_remove),
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = driver_name,
+		   },
+#ifdef CONFIG_PM
+	.suspend = mv_otg_suspend,
+	.resume = mv_otg_resume,
+#endif
+};
+
+static int __init mv_otg_init(void)
+{
+	return platform_driver_register(&mv_otg_driver);
+}
+
+static void __exit mv_otg_exit(void)
+{
+	platform_driver_unregister(&mv_otg_driver);
+}
+
+module_init(mv_otg_init);
+module_exit(mv_otg_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/mv_otg.h b/ap/os/linux/linux-3.4.x/drivers/usb/otg/mv_otg.h
new file mode 100644
index 0000000..8a9e351
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/mv_otg.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef	__MV_USB_OTG_CONTROLLER__
+#define	__MV_USB_OTG_CONTROLLER__
+
+#include <linux/types.h>
+
+/* Command Register Bit Masks */
+#define USBCMD_RUN_STOP			(0x00000001)
+#define USBCMD_CTRL_RESET		(0x00000002)
+
+/* otgsc Register Bit Masks */
+#define OTGSC_CTRL_VUSB_DISCHARGE		0x00000001
+#define OTGSC_CTRL_VUSB_CHARGE			0x00000002
+#define OTGSC_CTRL_OTG_TERM			0x00000008
+#define OTGSC_CTRL_DATA_PULSING			0x00000010
+#define OTGSC_STS_USB_ID			0x00000100
+#define OTGSC_STS_A_VBUS_VALID			0x00000200
+#define OTGSC_STS_A_SESSION_VALID		0x00000400
+#define OTGSC_STS_B_SESSION_VALID		0x00000800
+#define OTGSC_STS_B_SESSION_END			0x00001000
+#define OTGSC_STS_1MS_TOGGLE			0x00002000
+#define OTGSC_STS_DATA_PULSING			0x00004000
+#define OTGSC_INTSTS_USB_ID			0x00010000
+#define OTGSC_INTSTS_A_VBUS_VALID		0x00020000
+#define OTGSC_INTSTS_A_SESSION_VALID		0x00040000
+#define OTGSC_INTSTS_B_SESSION_VALID		0x00080000
+#define OTGSC_INTSTS_B_SESSION_END		0x00100000
+#define OTGSC_INTSTS_1MS			0x00200000
+#define OTGSC_INTSTS_DATA_PULSING		0x00400000
+#define OTGSC_INTR_USB_ID			0x01000000
+#define OTGSC_INTR_A_VBUS_VALID			0x02000000
+#define OTGSC_INTR_A_SESSION_VALID		0x04000000
+#define OTGSC_INTR_B_SESSION_VALID		0x08000000
+#define OTGSC_INTR_B_SESSION_END		0x10000000
+#define OTGSC_INTR_1MS_TIMER			0x20000000
+#define OTGSC_INTR_DATA_PULSING			0x40000000
+
+#define CAPLENGTH_MASK		(0xff)
+
+/* Timer's interval, unit 10ms */
+#define T_A_WAIT_VRISE		100
+#define T_A_WAIT_BCON		2000
+#define T_A_AIDL_BDIS		100
+#define T_A_BIDL_ADIS		20
+#define T_B_ASE0_BRST		400
+#define T_B_SE0_SRP		300
+#define T_B_SRP_FAIL		2000
+#define T_B_DATA_PLS		10
+#define T_B_SRP_INIT		100
+#define T_A_SRP_RSPNS		10
+#define T_A_DRV_RSM		5
+
+enum otg_function {
+	OTG_B_DEVICE = 0,
+	OTG_A_DEVICE
+};
+
+enum mv_otg_timer {
+	A_WAIT_BCON_TIMER = 0,
+	OTG_TIMER_NUM
+};
+
+/* PXA OTG state machine */
+struct mv_otg_ctrl {
+	/* internal variables */
+	u8 a_set_b_hnp_en;	/* A-Device set b_hnp_en */
+	u8 b_srp_done;
+	u8 b_hnp_en;
+
+	/* OTG inputs */
+	u8 a_bus_drop;
+	u8 a_bus_req;
+	u8 a_clr_err;
+	u8 a_bus_resume;
+	u8 a_bus_suspend;
+	u8 a_conn;
+	u8 a_sess_vld;
+	u8 a_srp_det;
+	u8 a_vbus_vld;
+	u8 b_bus_req;		/* B-Device Require Bus */
+	u8 b_bus_resume;
+	u8 b_bus_suspend;
+	u8 b_conn;
+	u8 b_se0_srp;
+	u8 b_sess_end;
+	u8 b_sess_vld;
+	u8 id;
+	u8 a_suspend_req;
+
+	/*Timer event */
+	u8 a_aidl_bdis_timeout;
+	u8 b_ase0_brst_timeout;
+	u8 a_bidl_adis_timeout;
+	u8 a_wait_bcon_timeout;
+
+	struct timer_list timer[OTG_TIMER_NUM];
+};
+
+#define VUSBHS_MAX_PORTS	8
+
+struct mv_otg_regs {
+	u32 usbcmd;		/* Command register */
+	u32 usbsts;		/* Status register */
+	u32 usbintr;		/* Interrupt enable */
+	u32 frindex;		/* Frame index */
+	u32 reserved1[1];
+	u32 deviceaddr;		/* Device Address */
+	u32 eplistaddr;		/* Endpoint List Address */
+	u32 ttctrl;		/* HOST TT status and control */
+	u32 burstsize;		/* Programmable Burst Size */
+	u32 txfilltuning;	/* Host Transmit Pre-Buffer Packet Tuning */
+	u32 reserved[4];
+	u32 epnak;		/* Endpoint NAK */
+	u32 epnaken;		/* Endpoint NAK Enable */
+	u32 configflag;		/* Configured Flag register */
+	u32 portsc[VUSBHS_MAX_PORTS];	/* Port Status/Control x, x = 1..8 */
+	u32 otgsc;
+	u32 usbmode;		/* USB Host/Device mode */
+	u32 epsetupstat;	/* Endpoint Setup Status */
+	u32 epprime;		/* Endpoint Initialize */
+	u32 epflush;		/* Endpoint De-initialize */
+	u32 epstatus;		/* Endpoint Status */
+	u32 epcomplete;		/* Endpoint Interrupt On Complete */
+	u32 epctrlx[16];	/* Endpoint Control, where x = 0.. 15 */
+	u32 mcr;		/* Mux Control */
+	u32 isr;		/* Interrupt Status */
+	u32 ier;		/* Interrupt Enable */
+};
+
+struct mv_otg {
+	struct usb_phy phy;
+	struct mv_otg_ctrl otg_ctrl;
+
+	/* base address */
+	void __iomem *phy_regs;
+	void __iomem *cap_regs;
+	struct mv_otg_regs __iomem *op_regs;
+
+	struct platform_device *pdev;
+	int irq;
+	u32 irq_status;
+	u32 irq_en;
+
+	struct delayed_work work;
+	struct workqueue_struct *qwork;
+
+	spinlock_t wq_lock;
+
+	struct mv_usb_platform_data *pdata;
+
+	unsigned int active;
+	unsigned int clock_gating;
+	unsigned int clknum;
+	struct clk *clk[0];
+};
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/nop-usb-xceiv.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/nop-usb-xceiv.c
new file mode 100644
index 0000000..58b26df
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/nop-usb-xceiv.c
@@ -0,0 +1,175 @@
+/*
+ * drivers/usb/otg/nop-usb-xceiv.c
+ *
+ * NOP USB transceiver for all USB transceiver which are either built-in
+ * into USB IP or which are mostly autonomous.
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Current status:
+ *	This provides a "nop" transceiver for PHYs which are
+ *	autonomous such as isp1504, isp1707, etc.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/otg.h>
+#include <linux/slab.h>
+
+struct nop_usb_xceiv {
+	struct usb_phy		phy;
+	struct device		*dev;
+};
+
+static struct platform_device *pd;
+
+void usb_nop_xceiv_register(void)
+{
+	if (pd)
+		return;
+	pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0);
+	if (!pd) {
+		printk(KERN_ERR "Unable to register usb nop transceiver\n");
+		return;
+	}
+}
+EXPORT_SYMBOL(usb_nop_xceiv_register);
+
+void usb_nop_xceiv_unregister(void)
+{
+	platform_device_unregister(pd);
+	pd = NULL;
+}
+EXPORT_SYMBOL(usb_nop_xceiv_unregister);
+
+static int nop_set_suspend(struct usb_phy *x, int suspend)
+{
+	return 0;
+}
+
+static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
+{
+	if (!otg)
+		return -ENODEV;
+
+	if (!gadget) {
+		otg->gadget = NULL;
+		return -ENODEV;
+	}
+
+	otg->gadget = gadget;
+	otg->phy->state = OTG_STATE_B_IDLE;
+	return 0;
+}
+
+static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	if (!otg)
+		return -ENODEV;
+
+	if (!host) {
+		otg->host = NULL;
+		return -ENODEV;
+	}
+
+	otg->host = host;
+	return 0;
+}
+
+static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
+{
+	struct nop_usb_xceiv	*nop;
+	int err;
+
+	nop = kzalloc(sizeof *nop, GFP_KERNEL);
+	if (!nop)
+		return -ENOMEM;
+
+	nop->phy.otg = kzalloc(sizeof *nop->phy.otg, GFP_KERNEL);
+	if (!nop->phy.otg) {
+		kfree(nop);
+		return -ENOMEM;
+	}
+
+	nop->dev		= &pdev->dev;
+	nop->phy.dev		= nop->dev;
+	nop->phy.label		= "nop-xceiv";
+	nop->phy.set_suspend	= nop_set_suspend;
+	nop->phy.state		= OTG_STATE_UNDEFINED;
+
+	nop->phy.otg->phy		= &nop->phy;
+	nop->phy.otg->set_host		= nop_set_host;
+	nop->phy.otg->set_peripheral	= nop_set_peripheral;
+
+	err = usb_set_transceiver(&nop->phy);
+	if (err) {
+		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
+			err);
+		goto exit;
+	}
+
+	platform_set_drvdata(pdev, nop);
+
+	ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier);
+
+	return 0;
+exit:
+	kfree(nop->phy.otg);
+	kfree(nop);
+	return err;
+}
+
+static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev)
+{
+	struct nop_usb_xceiv *nop = platform_get_drvdata(pdev);
+
+	usb_set_transceiver(NULL);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(nop->phy.otg);
+	kfree(nop);
+
+	return 0;
+}
+
+static struct platform_driver nop_usb_xceiv_driver = {
+	.probe		= nop_usb_xceiv_probe,
+	.remove		= __devexit_p(nop_usb_xceiv_remove),
+	.driver		= {
+		.name	= "nop_usb_xceiv",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init nop_usb_xceiv_init(void)
+{
+	return platform_driver_register(&nop_usb_xceiv_driver);
+}
+subsys_initcall(nop_usb_xceiv_init);
+
+static void __exit nop_usb_xceiv_exit(void)
+{
+	platform_driver_unregister(&nop_usb_xceiv_driver);
+}
+module_exit(nop_usb_xceiv_exit);
+
+MODULE_ALIAS("platform:nop_usb_xceiv");
+MODULE_AUTHOR("Texas Instruments Inc");
+MODULE_DESCRIPTION("NOP USB Transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg.c
new file mode 100644
index 0000000..801e597
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg.c
@@ -0,0 +1,102 @@
+/*
+ * otg.c -- USB OTG utility code
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/device.h>
+
+#include <linux/usb/otg.h>
+
+static struct usb_phy *phy;
+
+/**
+ * usb_get_transceiver - find the (single) USB transceiver
+ *
+ * Returns the transceiver driver, after getting a refcount to it; or
+ * null if there is no such transceiver.  The caller is responsible for
+ * calling usb_put_transceiver() to release that count.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *usb_get_transceiver(void)
+{
+	if (phy)
+		get_device(phy->dev);
+	return phy;
+}
+EXPORT_SYMBOL(usb_get_transceiver);
+
+/**
+ * usb_put_transceiver - release the (single) USB transceiver
+ * @x: the transceiver returned by usb_get_transceiver()
+ *
+ * Releases a refcount the caller received from usb_get_transceiver().
+ *
+ * For use by USB host and peripheral drivers.
+ */
+void usb_put_transceiver(struct usb_phy *x)
+{
+	if (x)
+		put_device(x->dev);
+}
+EXPORT_SYMBOL(usb_put_transceiver);
+
+/**
+ * usb_set_transceiver - declare the (single) USB transceiver
+ * @x: the USB transceiver to be used; or NULL
+ *
+ * This call is exclusively for use by transceiver drivers, which
+ * coordinate the activities of drivers for host and peripheral
+ * controllers, and in some cases for VBUS current regulation.
+ */
+int usb_set_transceiver(struct usb_phy *x)
+{
+	if (phy && x)
+		return -EBUSY;
+	phy = x;
+	return 0;
+}
+EXPORT_SYMBOL(usb_set_transceiver);
+
+const char *otg_state_string(enum usb_otg_state state)
+{
+	switch (state) {
+	case OTG_STATE_A_IDLE:
+		return "a_idle";
+	case OTG_STATE_A_WAIT_VRISE:
+		return "a_wait_vrise";
+	case OTG_STATE_A_WAIT_BCON:
+		return "a_wait_bcon";
+	case OTG_STATE_A_HOST:
+		return "a_host";
+	case OTG_STATE_A_SUSPEND:
+		return "a_suspend";
+	case OTG_STATE_A_PERIPHERAL:
+		return "a_peripheral";
+	case OTG_STATE_A_WAIT_VFALL:
+		return "a_wait_vfall";
+	case OTG_STATE_A_VBUS_ERR:
+		return "a_vbus_err";
+	case OTG_STATE_B_IDLE:
+		return "b_idle";
+	case OTG_STATE_B_SRP_INIT:
+		return "b_srp_init";
+	case OTG_STATE_B_PERIPHERAL:
+		return "b_peripheral";
+	case OTG_STATE_B_WAIT_ACON:
+		return "b_wait_acon";
+	case OTG_STATE_B_HOST:
+		return "b_host";
+	default:
+		return "UNDEFINED";
+	}
+}
+EXPORT_SYMBOL(otg_state_string);
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg_fsm.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg_fsm.c
new file mode 100644
index 0000000..e87edda
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg_fsm.c
@@ -0,0 +1,348 @@
+/*
+ * OTG Finite State Machine from OTG spec
+ *
+ * Copyright (C) 2007,2008 Freescale Semiconductor, Inc.
+ *
+ * Author:	Li Yang <LeoLi@freescale.com>
+ *		Jerry Huang <Chang-Ming.Huang@freescale.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+#include "otg_fsm.h"
+
+/* Change USB protocol when there is a protocol change */
+static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+	int ret = 0;
+
+	if (fsm->protocol != protocol) {
+		VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
+			fsm->protocol, protocol);
+		/* stop old protocol */
+		if (fsm->protocol == PROTO_HOST)
+			ret = fsm->ops->start_host(fsm, 0);
+		else if (fsm->protocol == PROTO_GADGET)
+			ret = fsm->ops->start_gadget(fsm, 0);
+		if (ret)
+			return ret;
+
+		/* start new protocol */
+		if (protocol == PROTO_HOST)
+			ret = fsm->ops->start_host(fsm, 1);
+		else if (protocol == PROTO_GADGET)
+			ret = fsm->ops->start_gadget(fsm, 1);
+		if (ret)
+			return ret;
+
+		fsm->protocol = protocol;
+		return 0;
+	}
+
+	return 0;
+}
+
+static int state_changed;
+
+/* Called when leaving a state.  Do state clean up jobs here */
+void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
+{
+	switch (old_state) {
+	case OTG_STATE_B_IDLE:
+		otg_del_timer(fsm, b_se0_srp_tmr);
+		fsm->b_se0_srp = 0;
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		fsm->b_srp_done = 0;
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		otg_del_timer(fsm, b_ase0_brst_tmr);
+		fsm->b_ase0_brst_tmout = 0;
+		break;
+	case OTG_STATE_B_HOST:
+		break;
+	case OTG_STATE_A_IDLE:
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		otg_del_timer(fsm, a_wait_vrise_tmr);
+		fsm->a_wait_vrise_tmout = 0;
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		otg_del_timer(fsm, a_wait_bcon_tmr);
+		fsm->a_wait_bcon_tmout = 0;
+		break;
+	case OTG_STATE_A_HOST:
+		otg_del_timer(fsm, a_wait_enum_tmr);
+		break;
+	case OTG_STATE_A_SUSPEND:
+		otg_del_timer(fsm, a_aidl_bdis_tmr);
+		fsm->a_aidl_bdis_tmout = 0;
+		fsm->a_suspend_req = 0;
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		otg_del_timer(fsm, a_wait_vrise_tmr);
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		break;
+	default:
+		break;
+	}
+}
+
+/* Called when entering a state */
+int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+	state_changed = 1;
+	if (fsm->otg->phy->state == new_state)
+		return 0;
+	VDBG("Set state: %s\n", otg_state_string(new_state));
+	otg_leave_state(fsm, fsm->otg->phy->state);
+	switch (new_state) {
+	case OTG_STATE_B_IDLE:
+		otg_drv_vbus(fsm, 0);
+		otg_chrg_vbus(fsm, 0);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_UNDEF);
+		otg_add_timer(fsm, b_se0_srp_tmr);
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		otg_start_pulse(fsm);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_UNDEF);
+		otg_add_timer(fsm, b_srp_fail_tmr);
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		otg_chrg_vbus(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_GADGET);
+		otg_loc_conn(fsm, 1);
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		otg_chrg_vbus(fsm, 0);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_HOST);
+		otg_add_timer(fsm, b_ase0_brst_tmr);
+		fsm->a_bus_suspend = 0;
+		break;
+	case OTG_STATE_B_HOST:
+		otg_chrg_vbus(fsm, 0);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 1);
+		otg_set_protocol(fsm, PROTO_HOST);
+		usb_bus_start_enum(fsm->otg->host,
+				fsm->otg->host->otg_port);
+		break;
+	case OTG_STATE_A_IDLE:
+		otg_drv_vbus(fsm, 0);
+		otg_chrg_vbus(fsm, 0);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_HOST);
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		otg_drv_vbus(fsm, 1);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_HOST);
+		otg_add_timer(fsm, a_wait_vrise_tmr);
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		otg_drv_vbus(fsm, 1);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_HOST);
+		otg_add_timer(fsm, a_wait_bcon_tmr);
+		break;
+	case OTG_STATE_A_HOST:
+		otg_drv_vbus(fsm, 1);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 1);
+		otg_set_protocol(fsm, PROTO_HOST);
+		/*
+		 * When HNP is triggered while a_bus_req = 0, a_host will
+		 * suspend too fast to complete a_set_b_hnp_en
+		 */
+		if (!fsm->a_bus_req || fsm->a_suspend_req)
+			otg_add_timer(fsm, a_wait_enum_tmr);
+		break;
+	case OTG_STATE_A_SUSPEND:
+		otg_drv_vbus(fsm, 1);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_HOST);
+		otg_add_timer(fsm, a_aidl_bdis_tmr);
+
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_GADGET);
+		otg_drv_vbus(fsm, 1);
+		otg_loc_conn(fsm, 1);
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		otg_drv_vbus(fsm, 0);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_HOST);
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		otg_drv_vbus(fsm, 0);
+		otg_loc_conn(fsm, 0);
+		otg_loc_sof(fsm, 0);
+		otg_set_protocol(fsm, PROTO_UNDEF);
+		break;
+	default:
+		break;
+	}
+
+	fsm->otg->phy->state = new_state;
+	return 0;
+}
+
+/* State change judgement */
+int otg_statemachine(struct otg_fsm *fsm)
+{
+	enum usb_otg_state state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fsm->lock, flags);
+
+	state = fsm->otg->phy->state;
+	state_changed = 0;
+	/* State machine state change judgement */
+
+	switch (state) {
+	case OTG_STATE_UNDEFINED:
+		VDBG("fsm->id = %d\n", fsm->id);
+		if (fsm->id)
+			otg_set_state(fsm, OTG_STATE_B_IDLE);
+		else
+			otg_set_state(fsm, OTG_STATE_A_IDLE);
+		break;
+	case OTG_STATE_B_IDLE:
+		if (!fsm->id)
+			otg_set_state(fsm, OTG_STATE_A_IDLE);
+		else if (fsm->b_sess_vld && fsm->otg->gadget)
+			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+		else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp)
+			otg_set_state(fsm, OTG_STATE_B_SRP_INIT);
+		break;
+	case OTG_STATE_B_SRP_INIT:
+		if (!fsm->id || fsm->b_srp_done)
+			otg_set_state(fsm, OTG_STATE_B_IDLE);
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if (!fsm->id || !fsm->b_sess_vld)
+			otg_set_state(fsm, OTG_STATE_B_IDLE);
+		else if (fsm->b_bus_req && fsm->otg->
+				gadget->b_hnp_enable && fsm->a_bus_suspend)
+			otg_set_state(fsm, OTG_STATE_B_WAIT_ACON);
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if (fsm->a_conn)
+			otg_set_state(fsm, OTG_STATE_B_HOST);
+		else if (!fsm->id || !fsm->b_sess_vld)
+			otg_set_state(fsm, OTG_STATE_B_IDLE);
+		else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) {
+			fsm->b_ase0_brst_tmout = 0;
+			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		if (!fsm->id || !fsm->b_sess_vld)
+			otg_set_state(fsm, OTG_STATE_B_IDLE);
+		else if (!fsm->b_bus_req || !fsm->a_conn)
+			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+		break;
+	case OTG_STATE_A_IDLE:
+		if (fsm->id)
+			otg_set_state(fsm, OTG_STATE_B_IDLE);
+		else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det))
+			otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE);
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld ||
+				fsm->a_wait_vrise_tmout) {
+			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+		}
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if (!fsm->a_vbus_vld)
+			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+		else if (fsm->b_conn)
+			otg_set_state(fsm, OTG_STATE_A_HOST);
+		else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+		break;
+	case OTG_STATE_A_HOST:
+		if ((!fsm->a_bus_req || fsm->a_suspend_req) &&
+				fsm->otg->host->b_hnp_enable)
+			otg_set_state(fsm, OTG_STATE_A_SUSPEND);
+		else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+		else if (!fsm->a_vbus_vld)
+			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if (!fsm->b_conn && fsm->otg->host->b_hnp_enable)
+			otg_set_state(fsm, OTG_STATE_A_PERIPHERAL);
+		else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+		else if (fsm->a_bus_req || fsm->b_bus_resume)
+			otg_set_state(fsm, OTG_STATE_A_HOST);
+		else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+		else if (!fsm->a_vbus_vld)
+			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if (fsm->id || fsm->a_bus_drop)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+		else if (fsm->b_bus_suspend)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+		else if (!fsm->a_vbus_vld)
+			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld &&
+					!fsm->b_conn))
+			otg_set_state(fsm, OTG_STATE_A_IDLE);
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err)
+			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&fsm->lock, flags);
+
+	VDBG("quit statemachine, changed = %d\n", state_changed);
+	return state_changed;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg_fsm.h b/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg_fsm.h
new file mode 100644
index 0000000..c30a2e1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/otg_fsm.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#undef DEBUG
+#undef VERBOSE
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk(KERN_DEBUG "[%s]  " fmt , \
+				 __func__, ## args)
+#else
+#define DBG(fmt, args...)	do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define VDBG		DBG
+#else
+#define VDBG(stuff...)	do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
+#else
+#define MPC_LOC do {} while (0)
+#endif
+
+#define PROTO_UNDEF	(0)
+#define PROTO_HOST	(1)
+#define PROTO_GADGET	(2)
+
+/* OTG state machine according to the OTG spec */
+struct otg_fsm {
+	/* Input */
+	int a_bus_resume;
+	int a_bus_suspend;
+	int a_conn;
+	int a_sess_vld;
+	int a_srp_det;
+	int a_vbus_vld;
+	int b_bus_resume;
+	int b_bus_suspend;
+	int b_conn;
+	int b_se0_srp;
+	int b_sess_end;
+	int b_sess_vld;
+	int id;
+
+	/* Internal variables */
+	int a_set_b_hnp_en;
+	int b_srp_done;
+	int b_hnp_enable;
+
+	/* Timeout indicator for timers */
+	int a_wait_vrise_tmout;
+	int a_wait_bcon_tmout;
+	int a_aidl_bdis_tmout;
+	int b_ase0_brst_tmout;
+
+	/* Informative variables */
+	int a_bus_drop;
+	int a_bus_req;
+	int a_clr_err;
+	int a_suspend_req;
+	int b_bus_req;
+
+	/* Output */
+	int drv_vbus;
+	int loc_conn;
+	int loc_sof;
+
+	struct otg_fsm_ops *ops;
+	struct usb_otg *otg;
+
+	/* Current usb protocol used: 0:undefine; 1:host; 2:client */
+	int protocol;
+	spinlock_t lock;
+};
+
+struct otg_fsm_ops {
+	void	(*chrg_vbus)(int on);
+	void	(*drv_vbus)(int on);
+	void	(*loc_conn)(int on);
+	void	(*loc_sof)(int on);
+	void	(*start_pulse)(void);
+	void	(*add_timer)(void *timer);
+	void	(*del_timer)(void *timer);
+	int	(*start_host)(struct otg_fsm *fsm, int on);
+	int	(*start_gadget)(struct otg_fsm *fsm, int on);
+};
+
+
+static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on)
+{
+	fsm->ops->chrg_vbus(on);
+}
+
+static inline void otg_drv_vbus(struct otg_fsm *fsm, int on)
+{
+	if (fsm->drv_vbus != on) {
+		fsm->drv_vbus = on;
+		fsm->ops->drv_vbus(on);
+	}
+}
+
+static inline void otg_loc_conn(struct otg_fsm *fsm, int on)
+{
+	if (fsm->loc_conn != on) {
+		fsm->loc_conn = on;
+		fsm->ops->loc_conn(on);
+	}
+}
+
+static inline void otg_loc_sof(struct otg_fsm *fsm, int on)
+{
+	if (fsm->loc_sof != on) {
+		fsm->loc_sof = on;
+		fsm->ops->loc_sof(on);
+	}
+}
+
+static inline void otg_start_pulse(struct otg_fsm *fsm)
+{
+	fsm->ops->start_pulse();
+}
+
+static inline void otg_add_timer(struct otg_fsm *fsm, void *timer)
+{
+	fsm->ops->add_timer(timer);
+}
+
+static inline void otg_del_timer(struct otg_fsm *fsm, void *timer)
+{
+	fsm->ops->del_timer(timer);
+}
+
+int otg_statemachine(struct otg_fsm *fsm);
+
+/* Defined by device specific driver, for different timer implementation */
+extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr,
+	*a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr,
+	*a_wait_enum_tmr;
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/twl4030-usb.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/twl4030-usb.c
new file mode 100644
index 0000000..c4a86da
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/twl4030-usb.c
@@ -0,0 +1,734 @@
+/*
+ * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004-2007 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Current status:
+ *	- HS USB ULPI mode works.
+ *	- 3-pin mode support may be added in future.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/i2c/twl.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+
+/* Register defines */
+
+#define MCPC_CTRL			0x30
+#define MCPC_CTRL_RTSOL			(1 << 7)
+#define MCPC_CTRL_EXTSWR		(1 << 6)
+#define MCPC_CTRL_EXTSWC		(1 << 5)
+#define MCPC_CTRL_VOICESW		(1 << 4)
+#define MCPC_CTRL_OUT64K		(1 << 3)
+#define MCPC_CTRL_RTSCTSSW		(1 << 2)
+#define MCPC_CTRL_HS_UART		(1 << 0)
+
+#define MCPC_IO_CTRL			0x33
+#define MCPC_IO_CTRL_MICBIASEN		(1 << 5)
+#define MCPC_IO_CTRL_CTS_NPU		(1 << 4)
+#define MCPC_IO_CTRL_RXD_PU		(1 << 3)
+#define MCPC_IO_CTRL_TXDTYP		(1 << 2)
+#define MCPC_IO_CTRL_CTSTYP		(1 << 1)
+#define MCPC_IO_CTRL_RTSTYP		(1 << 0)
+
+#define MCPC_CTRL2			0x36
+#define MCPC_CTRL2_MCPC_CK_EN		(1 << 0)
+
+#define OTHER_FUNC_CTRL			0x80
+#define OTHER_FUNC_CTRL_BDIS_ACON_EN	(1 << 4)
+#define OTHER_FUNC_CTRL_FIVEWIRE_MODE	(1 << 2)
+
+#define OTHER_IFC_CTRL			0x83
+#define OTHER_IFC_CTRL_OE_INT_EN	(1 << 6)
+#define OTHER_IFC_CTRL_CEA2011_MODE	(1 << 5)
+#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN	(1 << 4)
+#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT	(1 << 3)
+#define OTHER_IFC_CTRL_HIZ_ULPI		(1 << 2)
+#define OTHER_IFC_CTRL_ALT_INT_REROUTE	(1 << 0)
+
+#define OTHER_INT_EN_RISE		0x86
+#define OTHER_INT_EN_FALL		0x89
+#define OTHER_INT_STS			0x8C
+#define OTHER_INT_LATCH			0x8D
+#define OTHER_INT_VB_SESS_VLD		(1 << 7)
+#define OTHER_INT_DM_HI			(1 << 6) /* not valid for "latch" reg */
+#define OTHER_INT_DP_HI			(1 << 5) /* not valid for "latch" reg */
+#define OTHER_INT_BDIS_ACON		(1 << 3) /* not valid for "fall" regs */
+#define OTHER_INT_MANU			(1 << 1)
+#define OTHER_INT_ABNORMAL_STRESS	(1 << 0)
+
+#define ID_STATUS			0x96
+#define ID_RES_FLOAT			(1 << 4)
+#define ID_RES_440K			(1 << 3)
+#define ID_RES_200K			(1 << 2)
+#define ID_RES_102K			(1 << 1)
+#define ID_RES_GND			(1 << 0)
+
+#define POWER_CTRL			0xAC
+#define POWER_CTRL_OTG_ENAB		(1 << 5)
+
+#define OTHER_IFC_CTRL2			0xAF
+#define OTHER_IFC_CTRL2_ULPI_STP_LOW	(1 << 4)
+#define OTHER_IFC_CTRL2_ULPI_TXEN_POL	(1 << 3)
+#define OTHER_IFC_CTRL2_ULPI_4PIN_2430	(1 << 2)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK	(3 << 0) /* bits 0 and 1 */
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N	(0 << 0)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N	(1 << 0)
+
+#define REG_CTRL_EN			0xB2
+#define REG_CTRL_ERROR			0xB5
+#define ULPI_I2C_CONFLICT_INTEN		(1 << 0)
+
+#define OTHER_FUNC_CTRL2		0xB8
+#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN	(1 << 0)
+
+/* following registers do not have separate _clr and _set registers */
+#define VBUS_DEBOUNCE			0xC0
+#define ID_DEBOUNCE			0xC1
+#define VBAT_TIMER			0xD3
+#define PHY_PWR_CTRL			0xFD
+#define PHY_PWR_PHYPWD			(1 << 0)
+#define PHY_CLK_CTRL			0xFE
+#define PHY_CLK_CTRL_CLOCKGATING_EN	(1 << 2)
+#define PHY_CLK_CTRL_CLK32K_EN		(1 << 1)
+#define REQ_PHY_DPLL_CLK		(1 << 0)
+#define PHY_CLK_CTRL_STS		0xFF
+#define PHY_DPLL_CLK			(1 << 0)
+
+/* In module TWL4030_MODULE_PM_MASTER */
+#define STS_HW_CONDITIONS		0x0F
+
+/* In module TWL4030_MODULE_PM_RECEIVER */
+#define VUSB_DEDICATED1			0x7D
+#define VUSB_DEDICATED2			0x7E
+#define VUSB1V5_DEV_GRP			0x71
+#define VUSB1V5_TYPE			0x72
+#define VUSB1V5_REMAP			0x73
+#define VUSB1V8_DEV_GRP			0x74
+#define VUSB1V8_TYPE			0x75
+#define VUSB1V8_REMAP			0x76
+#define VUSB3V1_DEV_GRP			0x77
+#define VUSB3V1_TYPE			0x78
+#define VUSB3V1_REMAP			0x79
+
+/* In module TWL4030_MODULE_INTBR */
+#define PMBR1				0x0D
+#define GPIO_USB_4PIN_ULPI_2430C	(3 << 0)
+
+struct twl4030_usb {
+	struct usb_phy		phy;
+	struct device		*dev;
+
+	/* TWL4030 internal USB regulator supplies */
+	struct regulator	*usb1v5;
+	struct regulator	*usb1v8;
+	struct regulator	*usb3v1;
+
+	/* for vbus reporting with irqs disabled */
+	spinlock_t		lock;
+
+	/* pin configuration */
+	enum twl4030_usb_mode	usb_mode;
+
+	int			irq;
+	u8			linkstat;
+	bool			vbus_supplied;
+	u8			asleep;
+	bool			irq_enabled;
+};
+
+/* internal define on top of container_of */
+#define phy_to_twl(x)		container_of((x), struct twl4030_usb, phy)
+
+/*-------------------------------------------------------------------------*/
+
+static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
+		u8 module, u8 data, u8 address)
+{
+	u8 check;
+
+	if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+	    (twl_i2c_read_u8(module, &check, address) >= 0) &&
+						(check == data))
+		return 0;
+	dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
+			1, module, address, check, data);
+
+	/* Failed once: Try again */
+	if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+	    (twl_i2c_read_u8(module, &check, address) >= 0) &&
+						(check == data))
+		return 0;
+	dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
+			2, module, address, check, data);
+
+	/* Failed again: Return error */
+	return -EBUSY;
+}
+
+#define twl4030_usb_write_verify(twl, address, data)	\
+	twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_USB, (data), (address))
+
+static inline int twl4030_usb_write(struct twl4030_usb *twl,
+		u8 address, u8 data)
+{
+	int ret = 0;
+
+	ret = twl_i2c_write_u8(TWL4030_MODULE_USB, data, address);
+	if (ret < 0)
+		dev_dbg(twl->dev,
+			"TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
+	return ret;
+}
+
+static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
+{
+	u8 data;
+	int ret = 0;
+
+	ret = twl_i2c_read_u8(module, &data, address);
+	if (ret >= 0)
+		ret = data;
+	else
+		dev_dbg(twl->dev,
+			"TWL4030:readb[0x%x,0x%x] Error %d\n",
+					module, address, ret);
+
+	return ret;
+}
+
+static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address)
+{
+	return twl4030_readb(twl, TWL4030_MODULE_USB, address);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+	return twl4030_usb_write(twl, ULPI_SET(reg), bits);
+}
+
+static inline int
+twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+	return twl4030_usb_write(twl, ULPI_CLR(reg), bits);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
+{
+	int	status;
+	int	linkstat = USB_EVENT_NONE;
+	struct usb_otg *otg = twl->phy.otg;
+
+	twl->vbus_supplied = false;
+
+	/*
+	 * For ID/VBUS sensing, see manual section 15.4.8 ...
+	 * except when using only battery backup power, two
+	 * comparators produce VBUS_PRES and ID_PRES signals,
+	 * which don't match docs elsewhere.  But ... BIT(7)
+	 * and BIT(2) of STS_HW_CONDITIONS, respectively, do
+	 * seem to match up.  If either is true the USB_PRES
+	 * signal is active, the OTG module is activated, and
+	 * its interrupt may be raised (may wake the system).
+	 */
+	status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER,
+			STS_HW_CONDITIONS);
+	if (status < 0)
+		dev_err(twl->dev, "USB link status err %d\n", status);
+	else if (status & (BIT(7) | BIT(2))) {
+		if (status & (BIT(7)))
+                        twl->vbus_supplied = true;
+
+		if (status & BIT(2))
+			linkstat = USB_EVENT_ID;
+		else
+			linkstat = USB_EVENT_VBUS;
+	} else
+		linkstat = USB_EVENT_NONE;
+
+	dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
+			status, status, linkstat);
+
+	twl->phy.last_event = linkstat;
+
+	/* REVISIT this assumes host and peripheral controllers
+	 * are registered, and that both are active...
+	 */
+
+	spin_lock_irq(&twl->lock);
+	twl->linkstat = linkstat;
+	if (linkstat == USB_EVENT_ID) {
+		otg->default_a = true;
+		twl->phy.state = OTG_STATE_A_IDLE;
+	} else {
+		otg->default_a = false;
+		twl->phy.state = OTG_STATE_B_IDLE;
+	}
+	spin_unlock_irq(&twl->lock);
+
+	return linkstat;
+}
+
+static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
+{
+	twl->usb_mode = mode;
+
+	switch (mode) {
+	case T2_USB_MODE_ULPI:
+		twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL,
+					ULPI_IFC_CTRL_CARKITMODE);
+		twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+		twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL,
+					ULPI_FUNC_CTRL_XCVRSEL_MASK |
+					ULPI_FUNC_CTRL_OPMODE_MASK);
+		break;
+	case -1:
+		/* FIXME: power on defaults */
+		break;
+	default:
+		dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
+				mode);
+		break;
+	};
+}
+
+static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
+{
+	unsigned long timeout;
+	int val = twl4030_usb_read(twl, PHY_CLK_CTRL);
+
+	if (val >= 0) {
+		if (on) {
+			/* enable DPLL to access PHY registers over I2C */
+			val |= REQ_PHY_DPLL_CLK;
+			WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
+						(u8)val) < 0);
+
+			timeout = jiffies + HZ;
+			while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
+							PHY_DPLL_CLK)
+				&& time_before(jiffies, timeout))
+					udelay(10);
+			if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
+							PHY_DPLL_CLK))
+				dev_err(twl->dev, "Timeout setting T2 HSUSB "
+						"PHY DPLL clock\n");
+		} else {
+			/* let ULPI control the DPLL clock */
+			val &= ~REQ_PHY_DPLL_CLK;
+			WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
+						(u8)val) < 0);
+		}
+	}
+}
+
+static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
+{
+	u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
+
+	if (on)
+		pwr &= ~PHY_PWR_PHYPWD;
+	else
+		pwr |= PHY_PWR_PHYPWD;
+
+	WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
+}
+
+static void twl4030_phy_power(struct twl4030_usb *twl, int on)
+{
+	if (on) {
+		regulator_enable(twl->usb3v1);
+		regulator_enable(twl->usb1v8);
+		/*
+		 * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
+		 * in twl4030) resets the VUSB_DEDICATED2 register. This reset
+		 * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
+		 * SLEEP. We work around this by clearing the bit after usv3v1
+		 * is re-activated. This ensures that VUSB3V1 is really active.
+		 */
+		twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
+							VUSB_DEDICATED2);
+		regulator_enable(twl->usb1v5);
+		__twl4030_phy_power(twl, 1);
+		twl4030_usb_write(twl, PHY_CLK_CTRL,
+				  twl4030_usb_read(twl, PHY_CLK_CTRL) |
+					(PHY_CLK_CTRL_CLOCKGATING_EN |
+						PHY_CLK_CTRL_CLK32K_EN));
+	} else {
+		__twl4030_phy_power(twl, 0);
+		regulator_disable(twl->usb1v5);
+		regulator_disable(twl->usb1v8);
+		regulator_disable(twl->usb3v1);
+	}
+}
+
+static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
+{
+	if (twl->asleep)
+		return;
+
+	twl4030_phy_power(twl, 0);
+	twl->asleep = 1;
+	dev_dbg(twl->dev, "%s\n", __func__);
+}
+
+static void __twl4030_phy_resume(struct twl4030_usb *twl)
+{
+	twl4030_phy_power(twl, 1);
+	twl4030_i2c_access(twl, 1);
+	twl4030_usb_set_mode(twl, twl->usb_mode);
+	if (twl->usb_mode == T2_USB_MODE_ULPI)
+		twl4030_i2c_access(twl, 0);
+}
+
+static void twl4030_phy_resume(struct twl4030_usb *twl)
+{
+	if (!twl->asleep)
+		return;
+	__twl4030_phy_resume(twl);
+	twl->asleep = 0;
+	dev_dbg(twl->dev, "%s\n", __func__);
+}
+
+static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
+{
+	/* Enable writing to power configuration registers */
+	twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG1,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+
+	twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG2,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+
+	/* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/
+	/*twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/
+
+	/* input to VUSB3V1 LDO is from VBAT, not VBUS */
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
+
+	/* Initialize 3.1V regulator */
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
+
+	twl->usb3v1 = regulator_get(twl->dev, "usb3v1");
+	if (IS_ERR(twl->usb3v1))
+		return -ENODEV;
+
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
+
+	/* Initialize 1.5V regulator */
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
+
+	twl->usb1v5 = regulator_get(twl->dev, "usb1v5");
+	if (IS_ERR(twl->usb1v5))
+		goto fail1;
+
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
+
+	/* Initialize 1.8V regulator */
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
+
+	twl->usb1v8 = regulator_get(twl->dev, "usb1v8");
+	if (IS_ERR(twl->usb1v8))
+		goto fail2;
+
+	twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
+
+	/* disable access to power configuration registers */
+	twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+
+	return 0;
+
+fail2:
+	regulator_put(twl->usb1v5);
+	twl->usb1v5 = NULL;
+fail1:
+	regulator_put(twl->usb3v1);
+	twl->usb3v1 = NULL;
+	return -ENODEV;
+}
+
+static ssize_t twl4030_usb_vbus_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct twl4030_usb *twl = dev_get_drvdata(dev);
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&twl->lock, flags);
+	ret = sprintf(buf, "%s\n",
+			twl->vbus_supplied ? "on" : "off");
+	spin_unlock_irqrestore(&twl->lock, flags);
+
+	return ret;
+}
+static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
+
+static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
+{
+	struct twl4030_usb *twl = _twl;
+	int status;
+
+	status = twl4030_usb_linkstat(twl);
+	if (status >= 0) {
+		/* FIXME add a set_power() method so that B-devices can
+		 * configure the charger appropriately.  It's not always
+		 * correct to consume VBUS power, and how much current to
+		 * consume is a function of the USB configuration chosen
+		 * by the host.
+		 *
+		 * REVISIT usb_gadget_vbus_connect(...) as needed, ditto
+		 * its disconnect() sibling, when changing to/from the
+		 * USB_LINK_VBUS state.  musb_hdrc won't care until it
+		 * starts to handle softconnect right.
+		 */
+		if (status == USB_EVENT_NONE)
+			twl4030_phy_suspend(twl, 0);
+		else
+			twl4030_phy_resume(twl);
+
+		atomic_notifier_call_chain(&twl->phy.notifier, status,
+				twl->phy.otg->gadget);
+	}
+	sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+
+	return IRQ_HANDLED;
+}
+
+static void twl4030_usb_phy_init(struct twl4030_usb *twl)
+{
+	int status;
+
+	status = twl4030_usb_linkstat(twl);
+	if (status >= 0) {
+		if (status == USB_EVENT_NONE) {
+			__twl4030_phy_power(twl, 0);
+			twl->asleep = 1;
+		} else {
+			__twl4030_phy_resume(twl);
+			twl->asleep = 0;
+		}
+
+		atomic_notifier_call_chain(&twl->phy.notifier, status,
+				twl->phy.otg->gadget);
+	}
+	sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+}
+
+static int twl4030_set_suspend(struct usb_phy *x, int suspend)
+{
+	struct twl4030_usb *twl = phy_to_twl(x);
+
+	if (suspend)
+		twl4030_phy_suspend(twl, 1);
+	else
+		twl4030_phy_resume(twl);
+
+	return 0;
+}
+
+static int twl4030_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	if (!otg)
+		return -ENODEV;
+
+	otg->gadget = gadget;
+	if (!gadget)
+		otg->phy->state = OTG_STATE_UNDEFINED;
+
+	return 0;
+}
+
+static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	if (!otg)
+		return -ENODEV;
+
+	otg->host = host;
+	if (!host)
+		otg->phy->state = OTG_STATE_UNDEFINED;
+
+	return 0;
+}
+
+static int __devinit twl4030_usb_probe(struct platform_device *pdev)
+{
+	struct twl4030_usb_data *pdata = pdev->dev.platform_data;
+	struct twl4030_usb	*twl;
+	int			status, err;
+	struct usb_otg		*otg;
+
+	if (!pdata) {
+		dev_dbg(&pdev->dev, "platform_data not available\n");
+		return -EINVAL;
+	}
+
+	twl = kzalloc(sizeof *twl, GFP_KERNEL);
+	if (!twl)
+		return -ENOMEM;
+
+	otg = kzalloc(sizeof *otg, GFP_KERNEL);
+	if (!otg) {
+		kfree(twl);
+		return -ENOMEM;
+	}
+
+	twl->dev		= &pdev->dev;
+	twl->irq		= platform_get_irq(pdev, 0);
+	twl->usb_mode		= pdata->usb_mode;
+	twl->vbus_supplied	= false;
+	twl->asleep		= 1;
+
+	twl->phy.dev		= twl->dev;
+	twl->phy.label		= "twl4030";
+	twl->phy.otg		= otg;
+	twl->phy.set_suspend	= twl4030_set_suspend;
+
+	otg->phy		= &twl->phy;
+	otg->set_host		= twl4030_set_host;
+	otg->set_peripheral	= twl4030_set_peripheral;
+
+	/* init spinlock for workqueue */
+	spin_lock_init(&twl->lock);
+
+	err = twl4030_usb_ldo_init(twl);
+	if (err) {
+		dev_err(&pdev->dev, "ldo init failed\n");
+		kfree(otg);
+		kfree(twl);
+		return err;
+	}
+	usb_set_transceiver(&twl->phy);
+
+	platform_set_drvdata(pdev, twl);
+	if (device_create_file(&pdev->dev, &dev_attr_vbus))
+		dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+	ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
+
+	/* Our job is to use irqs and status from the power module
+	 * to keep the transceiver disabled when nothing's connected.
+	 *
+	 * FIXME we actually shouldn't start enabling it until the
+	 * USB controller drivers have said they're ready, by calling
+	 * set_host() and/or set_peripheral() ... OTG_capable boards
+	 * need both handles, otherwise just one suffices.
+	 */
+	twl->irq_enabled = true;
+	status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+			"twl4030_usb", twl);
+	if (status < 0) {
+		dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
+			twl->irq, status);
+		kfree(otg);
+		kfree(twl);
+		return status;
+	}
+
+	/* Power down phy or make it work according to
+	 * current link state.
+	 */
+	twl4030_usb_phy_init(twl);
+
+	dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
+	return 0;
+}
+
+static int __exit twl4030_usb_remove(struct platform_device *pdev)
+{
+	struct twl4030_usb *twl = platform_get_drvdata(pdev);
+	int val;
+
+	free_irq(twl->irq, twl);
+	device_remove_file(twl->dev, &dev_attr_vbus);
+
+	/* set transceiver mode to power on defaults */
+	twl4030_usb_set_mode(twl, -1);
+
+	/* autogate 60MHz ULPI clock,
+	 * clear dpll clock request for i2c access,
+	 * disable 32KHz
+	 */
+	val = twl4030_usb_read(twl, PHY_CLK_CTRL);
+	if (val >= 0) {
+		val |= PHY_CLK_CTRL_CLOCKGATING_EN;
+		val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK);
+		twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val);
+	}
+
+	/* disable complete OTG block */
+	twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+
+	if (!twl->asleep)
+		twl4030_phy_power(twl, 0);
+	regulator_put(twl->usb1v5);
+	regulator_put(twl->usb1v8);
+	regulator_put(twl->usb3v1);
+
+	kfree(twl->phy.otg);
+	kfree(twl);
+
+	return 0;
+}
+
+static struct platform_driver twl4030_usb_driver = {
+	.probe		= twl4030_usb_probe,
+	.remove		= __exit_p(twl4030_usb_remove),
+	.driver		= {
+		.name	= "twl4030_usb",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init twl4030_usb_init(void)
+{
+	return platform_driver_register(&twl4030_usb_driver);
+}
+subsys_initcall(twl4030_usb_init);
+
+static void __exit twl4030_usb_exit(void)
+{
+	platform_driver_unregister(&twl4030_usb_driver);
+}
+module_exit(twl4030_usb_exit);
+
+MODULE_ALIAS("platform:twl4030_usb");
+MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation");
+MODULE_DESCRIPTION("TWL4030 USB transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/twl6030-usb.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/twl6030-usb.c
new file mode 100644
index 0000000..e3fa387
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/twl6030-usb.c
@@ -0,0 +1,539 @@
+/*
+ * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Hema HK <hemahk@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c/twl.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+/* usb register definitions */
+#define USB_VENDOR_ID_LSB		0x00
+#define USB_VENDOR_ID_MSB		0x01
+#define USB_PRODUCT_ID_LSB		0x02
+#define USB_PRODUCT_ID_MSB		0x03
+#define USB_VBUS_CTRL_SET		0x04
+#define USB_VBUS_CTRL_CLR		0x05
+#define USB_ID_CTRL_SET			0x06
+#define USB_ID_CTRL_CLR			0x07
+#define USB_VBUS_INT_SRC		0x08
+#define USB_VBUS_INT_LATCH_SET		0x09
+#define USB_VBUS_INT_LATCH_CLR		0x0A
+#define USB_VBUS_INT_EN_LO_SET		0x0B
+#define USB_VBUS_INT_EN_LO_CLR		0x0C
+#define USB_VBUS_INT_EN_HI_SET		0x0D
+#define USB_VBUS_INT_EN_HI_CLR		0x0E
+#define USB_ID_INT_SRC			0x0F
+#define USB_ID_INT_LATCH_SET		0x10
+#define USB_ID_INT_LATCH_CLR		0x11
+
+#define USB_ID_INT_EN_LO_SET		0x12
+#define USB_ID_INT_EN_LO_CLR		0x13
+#define USB_ID_INT_EN_HI_SET		0x14
+#define USB_ID_INT_EN_HI_CLR		0x15
+#define USB_OTG_ADP_CTRL		0x16
+#define USB_OTG_ADP_HIGH		0x17
+#define USB_OTG_ADP_LOW			0x18
+#define USB_OTG_ADP_RISE		0x19
+#define USB_OTG_REVISION		0x1A
+
+/* to be moved to LDO */
+#define TWL6030_MISC2			0xE5
+#define TWL6030_CFG_LDO_PD2		0xF5
+#define TWL6030_BACKUP_REG		0xFA
+
+#define STS_HW_CONDITIONS		0x21
+
+/* In module TWL6030_MODULE_PM_MASTER */
+#define STS_HW_CONDITIONS		0x21
+#define STS_USB_ID			BIT(2)
+
+/* In module TWL6030_MODULE_PM_RECEIVER */
+#define VUSB_CFG_TRANS			0x71
+#define VUSB_CFG_STATE			0x72
+#define VUSB_CFG_VOLTAGE		0x73
+
+/* in module TWL6030_MODULE_MAIN_CHARGE */
+
+#define CHARGERUSB_CTRL1		0x8
+
+#define CONTROLLER_STAT1		0x03
+#define	VBUS_DET			BIT(2)
+
+struct twl6030_usb {
+	struct usb_phy		phy;
+	struct device		*dev;
+
+	/* for vbus reporting with irqs disabled */
+	spinlock_t		lock;
+
+	struct regulator		*usb3v3;
+
+	/* used to set vbus, in atomic path */
+	struct work_struct	set_vbus_work;
+
+	int			irq1;
+	int			irq2;
+	u8			linkstat;
+	u8			asleep;
+	bool			irq_enabled;
+	bool			vbus_enable;
+	unsigned long		features;
+};
+
+#define phy_to_twl(x)		container_of((x), struct twl6030_usb, phy)
+
+/*-------------------------------------------------------------------------*/
+
+static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module,
+						u8 data, u8 address)
+{
+	int ret = 0;
+
+	ret = twl_i2c_write_u8(module, data, address);
+	if (ret < 0)
+		dev_err(twl->dev,
+			"Write[0x%x] Error %d\n", address, ret);
+	return ret;
+}
+
+static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
+{
+	u8 data, ret = 0;
+
+	ret = twl_i2c_read_u8(module, &data, address);
+	if (ret >= 0)
+		ret = data;
+	else
+		dev_err(twl->dev,
+			"readb[0x%x,0x%x] Error %d\n",
+					module, address, ret);
+	return ret;
+}
+
+static int twl6030_phy_init(struct usb_phy *x)
+{
+	struct twl6030_usb *twl;
+	struct device *dev;
+	struct twl4030_usb_data *pdata;
+
+	twl = phy_to_twl(x);
+	dev  = twl->dev;
+	pdata = dev->platform_data;
+
+	if (twl->linkstat == USB_EVENT_ID)
+		pdata->phy_power(twl->dev, 1, 1);
+	else
+		pdata->phy_power(twl->dev, 0, 1);
+
+	return 0;
+}
+
+static void twl6030_phy_shutdown(struct usb_phy *x)
+{
+	struct twl6030_usb *twl;
+	struct device *dev;
+	struct twl4030_usb_data *pdata;
+
+	twl = phy_to_twl(x);
+	dev  = twl->dev;
+	pdata = dev->platform_data;
+	pdata->phy_power(twl->dev, 0, 0);
+}
+
+static int twl6030_phy_suspend(struct usb_phy *x, int suspend)
+{
+	struct twl6030_usb *twl = phy_to_twl(x);
+	struct device *dev = twl->dev;
+	struct twl4030_usb_data *pdata = dev->platform_data;
+
+	pdata->phy_suspend(dev, suspend);
+
+	return 0;
+}
+
+static int twl6030_start_srp(struct usb_otg *otg)
+{
+	struct twl6030_usb *twl = phy_to_twl(otg->phy);
+
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET);
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET);
+
+	mdelay(100);
+	twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR);
+
+	return 0;
+}
+
+static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
+{
+	char *regulator_name;
+
+	if (twl->features & TWL6025_SUBCLASS)
+		regulator_name = "ldousb";
+	else
+		regulator_name = "vusb";
+
+	/* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
+	twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
+
+	/* Program CFG_LDO_PD2 register and set VUSB bit */
+	twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2);
+
+	/* Program MISC2 register and set bit VUSB_IN_VBAT */
+	twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
+
+	twl->usb3v3 = regulator_get(twl->dev, regulator_name);
+	if (IS_ERR(twl->usb3v3))
+		return -ENODEV;
+
+	/* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET);
+
+	/*
+	 * Program the USB_ID_CTRL_SET register to enable GND drive
+	 * and the ID comparators
+	 */
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET);
+
+	return 0;
+}
+
+static ssize_t twl6030_usb_vbus_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct twl6030_usb *twl = dev_get_drvdata(dev);
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&twl->lock, flags);
+
+	switch (twl->linkstat) {
+	case USB_EVENT_VBUS:
+	       ret = snprintf(buf, PAGE_SIZE, "vbus\n");
+	       break;
+	case USB_EVENT_ID:
+	       ret = snprintf(buf, PAGE_SIZE, "id\n");
+	       break;
+	case USB_EVENT_NONE:
+	       ret = snprintf(buf, PAGE_SIZE, "none\n");
+	       break;
+	default:
+	       ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
+	}
+	spin_unlock_irqrestore(&twl->lock, flags);
+
+	return ret;
+}
+static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
+
+static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
+{
+	struct twl6030_usb *twl = _twl;
+	struct usb_otg *otg = twl->phy.otg;
+	int status;
+	u8 vbus_state, hw_state;
+
+	hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
+
+	vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE,
+						CONTROLLER_STAT1);
+	if (!(hw_state & STS_USB_ID)) {
+		if (vbus_state & VBUS_DET) {
+			regulator_enable(twl->usb3v3);
+			twl->asleep = 1;
+			status = USB_EVENT_VBUS;
+			otg->default_a = false;
+			twl->phy.state = OTG_STATE_B_IDLE;
+			twl->linkstat = status;
+			twl->phy.last_event = status;
+			atomic_notifier_call_chain(&twl->phy.notifier,
+						status, otg->gadget);
+		} else {
+			status = USB_EVENT_NONE;
+			twl->linkstat = status;
+			twl->phy.last_event = status;
+			atomic_notifier_call_chain(&twl->phy.notifier,
+						status, otg->gadget);
+			if (twl->asleep) {
+				regulator_disable(twl->usb3v3);
+				twl->asleep = 0;
+			}
+		}
+	}
+	sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
+{
+	struct twl6030_usb *twl = _twl;
+	struct usb_otg *otg = twl->phy.otg;
+	int status = USB_EVENT_NONE;
+	u8 hw_state;
+
+	hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
+
+	if (hw_state & STS_USB_ID) {
+
+		regulator_enable(twl->usb3v3);
+		twl->asleep = 1;
+		twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1);
+		twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
+								0x10);
+		status = USB_EVENT_ID;
+		otg->default_a = true;
+		twl->phy.state = OTG_STATE_A_IDLE;
+		twl->linkstat = status;
+		twl->phy.last_event = status;
+		atomic_notifier_call_chain(&twl->phy.notifier, status,
+							otg->gadget);
+	} else  {
+		twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR,
+								0x10);
+		twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
+								0x1);
+	}
+	twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status);
+
+	return IRQ_HANDLED;
+}
+
+static int twl6030_set_peripheral(struct usb_otg *otg,
+		struct usb_gadget *gadget)
+{
+	if (!otg)
+		return -ENODEV;
+
+	otg->gadget = gadget;
+	if (!gadget)
+		otg->phy->state = OTG_STATE_UNDEFINED;
+
+	return 0;
+}
+
+static int twl6030_enable_irq(struct usb_phy *x)
+{
+	struct twl6030_usb *twl = phy_to_twl(x);
+
+	twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x1);
+	twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C);
+	twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C);
+
+	twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
+				REG_INT_MSK_LINE_C);
+	twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
+				REG_INT_MSK_STS_C);
+	twl6030_usb_irq(twl->irq2, twl);
+	twl6030_usbotg_irq(twl->irq1, twl);
+
+	return 0;
+}
+
+static void otg_set_vbus_work(struct work_struct *data)
+{
+	struct twl6030_usb *twl = container_of(data, struct twl6030_usb,
+								set_vbus_work);
+
+	/*
+	 * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
+	 * register. This enables boost mode.
+	 */
+
+	if (twl->vbus_enable)
+		twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40,
+							CHARGERUSB_CTRL1);
+	else
+		twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00,
+							CHARGERUSB_CTRL1);
+}
+
+static int twl6030_set_vbus(struct usb_otg *otg, bool enabled)
+{
+	struct twl6030_usb *twl = phy_to_twl(otg->phy);
+
+	twl->vbus_enable = enabled;
+	schedule_work(&twl->set_vbus_work);
+
+	return 0;
+}
+
+static int twl6030_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	if (!otg)
+		return -ENODEV;
+
+	otg->host = host;
+	if (!host)
+		otg->phy->state = OTG_STATE_UNDEFINED;
+	return 0;
+}
+
+static int __devinit twl6030_usb_probe(struct platform_device *pdev)
+{
+	struct twl6030_usb	*twl;
+	int			status, err;
+	struct twl4030_usb_data *pdata;
+	struct usb_otg		*otg;
+	struct device *dev = &pdev->dev;
+	pdata = dev->platform_data;
+
+	twl = kzalloc(sizeof *twl, GFP_KERNEL);
+	if (!twl)
+		return -ENOMEM;
+
+	otg = kzalloc(sizeof *otg, GFP_KERNEL);
+	if (!otg) {
+		kfree(twl);
+		return -ENOMEM;
+	}
+
+	twl->dev		= &pdev->dev;
+	twl->irq1		= platform_get_irq(pdev, 0);
+	twl->irq2		= platform_get_irq(pdev, 1);
+	twl->features		= pdata->features;
+
+	twl->phy.dev		= twl->dev;
+	twl->phy.label		= "twl6030";
+	twl->phy.otg		= otg;
+	twl->phy.init		= twl6030_phy_init;
+	twl->phy.shutdown	= twl6030_phy_shutdown;
+	twl->phy.set_suspend	= twl6030_phy_suspend;
+
+	otg->phy		= &twl->phy;
+	otg->set_host		= twl6030_set_host;
+	otg->set_peripheral	= twl6030_set_peripheral;
+	otg->set_vbus		= twl6030_set_vbus;
+	otg->start_srp		= twl6030_start_srp;
+
+	/* init spinlock for workqueue */
+	spin_lock_init(&twl->lock);
+
+	err = twl6030_usb_ldo_init(twl);
+	if (err) {
+		dev_err(&pdev->dev, "ldo init failed\n");
+		kfree(otg);
+		kfree(twl);
+		return err;
+	}
+	usb_set_transceiver(&twl->phy);
+
+	platform_set_drvdata(pdev, twl);
+	if (device_create_file(&pdev->dev, &dev_attr_vbus))
+		dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+	ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
+
+	INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
+
+	twl->irq_enabled = true;
+	status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+			"twl6030_usb", twl);
+	if (status < 0) {
+		dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+			twl->irq1, status);
+		device_remove_file(twl->dev, &dev_attr_vbus);
+		kfree(otg);
+		kfree(twl);
+		return status;
+	}
+
+	status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+			"twl6030_usb", twl);
+	if (status < 0) {
+		dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+			twl->irq2, status);
+		free_irq(twl->irq1, twl);
+		device_remove_file(twl->dev, &dev_attr_vbus);
+		kfree(otg);
+		kfree(twl);
+		return status;
+	}
+
+	twl->asleep = 0;
+	pdata->phy_init(dev);
+	twl6030_phy_suspend(&twl->phy, 0);
+	twl6030_enable_irq(&twl->phy);
+	dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
+
+	return 0;
+}
+
+static int __exit twl6030_usb_remove(struct platform_device *pdev)
+{
+	struct twl6030_usb *twl = platform_get_drvdata(pdev);
+
+	struct twl4030_usb_data *pdata;
+	struct device *dev = &pdev->dev;
+	pdata = dev->platform_data;
+
+	twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
+		REG_INT_MSK_LINE_C);
+	twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
+			REG_INT_MSK_STS_C);
+	free_irq(twl->irq1, twl);
+	free_irq(twl->irq2, twl);
+	regulator_put(twl->usb3v3);
+	pdata->phy_exit(twl->dev);
+	device_remove_file(twl->dev, &dev_attr_vbus);
+	cancel_work_sync(&twl->set_vbus_work);
+	kfree(twl->phy.otg);
+	kfree(twl);
+
+	return 0;
+}
+
+static struct platform_driver twl6030_usb_driver = {
+	.probe		= twl6030_usb_probe,
+	.remove		= __exit_p(twl6030_usb_remove),
+	.driver		= {
+		.name	= "twl6030_usb",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init twl6030_usb_init(void)
+{
+	return platform_driver_register(&twl6030_usb_driver);
+}
+subsys_initcall(twl6030_usb_init);
+
+static void __exit twl6030_usb_exit(void)
+{
+	platform_driver_unregister(&twl6030_usb_driver);
+}
+module_exit(twl6030_usb_exit);
+
+MODULE_ALIAS("platform:twl6030_usb");
+MODULE_AUTHOR("Hema HK <hemahk@ti.com>");
+MODULE_DESCRIPTION("TWL6030 USB transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/ulpi.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/ulpi.c
new file mode 100644
index 0000000..217339d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/ulpi.c
@@ -0,0 +1,283 @@
+/*
+ * Generic ULPI USB transceiver support
+ *
+ * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * Based on sources from
+ *
+ *   Sascha Hauer <s.hauer@pengutronix.de>
+ *   Freescale Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+
+struct ulpi_info {
+	unsigned int	id;
+	char		*name;
+};
+
+#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
+#define ULPI_INFO(_id, _name)		\
+	{				\
+		.id	= (_id),	\
+		.name	= (_name),	\
+	}
+
+/* ULPI hardcoded IDs, used for probing */
+static struct ulpi_info ulpi_ids[] = {
+	ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
+	ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
+};
+
+static int ulpi_set_otg_flags(struct usb_phy *phy)
+{
+	unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
+			     ULPI_OTG_CTRL_DM_PULLDOWN;
+
+	if (phy->flags & ULPI_OTG_ID_PULLUP)
+		flags |= ULPI_OTG_CTRL_ID_PULLUP;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for Dp/DmPulldown is enabled.
+	 */
+	if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
+		flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
+
+	if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
+		flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
+
+	if (phy->flags & ULPI_OTG_EXTVBUSIND)
+		flags |= ULPI_OTG_CTRL_EXTVBUSIND;
+
+	return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
+}
+
+static int ulpi_set_fc_flags(struct usb_phy *phy)
+{
+	unsigned int flags = 0;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for XcvrSelect is Full Speed.
+	 */
+	if (phy->flags & ULPI_FC_HS)
+		flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
+	else if (phy->flags & ULPI_FC_LS)
+		flags |= ULPI_FUNC_CTRL_LOW_SPEED;
+	else if (phy->flags & ULPI_FC_FS4LS)
+		flags |= ULPI_FUNC_CTRL_FS4LS;
+	else
+		flags |= ULPI_FUNC_CTRL_FULL_SPEED;
+
+	if (phy->flags & ULPI_FC_TERMSEL)
+		flags |= ULPI_FUNC_CTRL_TERMSELECT;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for OpMode is Normal Operation.
+	 */
+	if (phy->flags & ULPI_FC_OP_NODRV)
+		flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+	else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
+		flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
+	else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
+		flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
+	else
+		flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for SuspendM is Powered.
+	 */
+	flags |= ULPI_FUNC_CTRL_SUSPENDM;
+
+	return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
+}
+
+static int ulpi_set_ic_flags(struct usb_phy *phy)
+{
+	unsigned int flags = 0;
+
+	if (phy->flags & ULPI_IC_AUTORESUME)
+		flags |= ULPI_IFC_CTRL_AUTORESUME;
+
+	if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
+		flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
+
+	if (phy->flags & ULPI_IC_IND_PASSTHRU)
+		flags |= ULPI_IFC_CTRL_PASSTHRU;
+
+	if (phy->flags & ULPI_IC_PROTECT_DIS)
+		flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
+
+	return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
+}
+
+static int ulpi_set_flags(struct usb_phy *phy)
+{
+	int ret;
+
+	ret = ulpi_set_otg_flags(phy);
+	if (ret)
+		return ret;
+
+	ret = ulpi_set_ic_flags(phy);
+	if (ret)
+		return ret;
+
+	return ulpi_set_fc_flags(phy);
+}
+
+static int ulpi_check_integrity(struct usb_phy *phy)
+{
+	int ret, i;
+	unsigned int val = 0x55;
+
+	for (i = 0; i < 2; i++) {
+		ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
+		if (ret < 0)
+			return ret;
+
+		ret = usb_phy_io_read(phy, ULPI_SCRATCH);
+		if (ret < 0)
+			return ret;
+
+		if (ret != val) {
+			pr_err("ULPI integrity check: failed!");
+			return -ENODEV;
+		}
+		val = val << 1;
+	}
+
+	pr_info("ULPI integrity check: passed.\n");
+
+	return 0;
+}
+
+static int ulpi_init(struct usb_phy *phy)
+{
+	int i, vid, pid, ret;
+	u32 ulpi_id = 0;
+
+	for (i = 0; i < 4; i++) {
+		ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
+		if (ret < 0)
+			return ret;
+		ulpi_id = (ulpi_id << 8) | ret;
+	}
+	vid = ulpi_id & 0xffff;
+	pid = ulpi_id >> 16;
+
+	pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
+
+	for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
+		if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
+			pr_info("Found %s ULPI transceiver.\n",
+				ulpi_ids[i].name);
+			break;
+		}
+	}
+
+	ret = ulpi_check_integrity(phy);
+	if (ret)
+		return ret;
+
+	return ulpi_set_flags(phy);
+}
+
+static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct usb_phy *phy = otg->phy;
+	unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
+
+	if (!host) {
+		otg->host = NULL;
+		return 0;
+	}
+
+	otg->host = host;
+
+	flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
+		   ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
+		   ULPI_IFC_CTRL_CARKITMODE);
+
+	if (phy->flags & ULPI_IC_6PIN_SERIAL)
+		flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
+	else if (phy->flags & ULPI_IC_3PIN_SERIAL)
+		flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
+	else if (phy->flags & ULPI_IC_CARKIT)
+		flags |= ULPI_IFC_CTRL_CARKITMODE;
+
+	return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
+}
+
+static int ulpi_set_vbus(struct usb_otg *otg, bool on)
+{
+	struct usb_phy *phy = otg->phy;
+	unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
+
+	flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
+
+	if (on) {
+		if (phy->flags & ULPI_OTG_DRVVBUS)
+			flags |= ULPI_OTG_CTRL_DRVVBUS;
+
+		if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
+			flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
+	}
+
+	return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
+}
+
+struct usb_phy *
+otg_ulpi_create(struct usb_phy_io_ops *ops,
+		unsigned int flags)
+{
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+
+	phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return NULL;
+
+	otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+	if (!otg) {
+		kfree(phy);
+		return NULL;
+	}
+
+	phy->label	= "ULPI";
+	phy->flags	= flags;
+	phy->io_ops	= ops;
+	phy->otg	= otg;
+	phy->init	= ulpi_init;
+
+	otg->phy	= phy;
+	otg->set_host	= ulpi_set_host;
+	otg->set_vbus	= ulpi_set_vbus;
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(otg_ulpi_create);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/usb/otg/ulpi_viewport.c b/ap/os/linux/linux-3.4.x/drivers/usb/otg/ulpi_viewport.c
new file mode 100644
index 0000000..c5ba7e5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/usb/otg/ulpi_viewport.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+#define ULPI_VIEW_WAKEUP	(1 << 31)
+#define ULPI_VIEW_RUN		(1 << 30)
+#define ULPI_VIEW_WRITE		(1 << 29)
+#define ULPI_VIEW_READ		(0 << 29)
+#define ULPI_VIEW_ADDR(x)	(((x) & 0xff) << 16)
+#define ULPI_VIEW_DATA_READ(x)	(((x) >> 8) & 0xff)
+#define ULPI_VIEW_DATA_WRITE(x)	((x) & 0xff)
+
+static int ulpi_viewport_wait(void __iomem *view, u32 mask)
+{
+	unsigned long usec = 2000;
+
+	while (usec--) {
+		if (!(readl(view) & mask))
+			return 0;
+
+		udelay(1);
+	};
+
+	return -ETIMEDOUT;
+}
+
+static int ulpi_viewport_read(struct usb_phy *otg, u32 reg)
+{
+	int ret;
+	void __iomem *view = otg->io_priv;
+
+	writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+	ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+	if (ret)
+		return ret;
+
+	writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view);
+	ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+	if (ret)
+		return ret;
+
+	return ULPI_VIEW_DATA_READ(readl(view));
+}
+
+static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg)
+{
+	int ret;
+	void __iomem *view = otg->io_priv;
+
+	writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+	ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+	if (ret)
+		return ret;
+
+	writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) |
+						 ULPI_VIEW_ADDR(reg), view);
+
+	return ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+}
+
+struct usb_phy_io_ops ulpi_viewport_access_ops = {
+	.read	= ulpi_viewport_read,
+	.write	= ulpi_viewport_write,
+};