commit 3613e3eed3705ed6006f0f440774ecb62664c050
Author: Maarten ter Huurne <maarten@treewalker.org>
Date:   Tue Aug 7 15:18:03 2012 +0200

    USB: musb: Add JZ4770 support from Ingenic
    
    Taken from linux-2.6.31.3-jz-20110809-FR1.patch.gz.
    Only the drivers relevant to JZ4770 are included.
    
    The original patch contains dozens of commits from later mainline kernels
    up to and including 2.6.36. I included only the platform specific changes
    in this commit. Also I restructured the driver as a module.

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index a7773a3..a4368e3 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -82,6 +82,7 @@ config USB_ARCH_HAS_HCD
 	default y if ARM				# SL-811
 	default y if BLACKFIN				# SL-811
 	default y if SUPERH				# r8a66597-hcd
+	default y if MACH_JZ4770			# internal OTG
 	default PCI
 
 # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
@@ -95,7 +96,7 @@ config USB
 	  traditional PC serial port.  The bus supplies power to peripherals
 	  and allows for hot swapping.  Up to 127 USB peripherals can be
 	  connected to a single USB host in a tree structure.
-	  
+
 	  The USB host is the root of the tree, the peripherals are the
 	  leaves and the inner nodes are special USB devices called hubs.
 	  Most PCs now have USB host ports, used to connect peripherals
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index ef0c3f9..8c03b8f 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -8,7 +8,7 @@ config USB_MUSB_HDRC
 	tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
 	depends on USB && USB_GADGET
 	select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
-	select NOP_USB_XCEIV if (SOC_OMAPTI81XX || SOC_OMAPAM33XX)
+	select NOP_USB_XCEIV if (SOC_OMAPTI81XX || SOC_OMAPAM33XX || MACH_JZ4770)
 	select TWL4030_USB if MACH_OMAP_3430SDP
 	select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
 	select USB_OTG_UTILS
@@ -67,6 +67,10 @@ config USB_MUSB_UX500
 	tristate "U8500 and U5500"
 	depends on (ARCH_U8500 && AB8500_USB)
 
+config USB_MUSB_JZ4770
+	tristate "JZ4770"
+	depends on MACH_JZ4770
+
 endchoice
 
 choice
@@ -82,6 +86,10 @@ choice
 	  should be able to build all these drivers into one kernel to
 	  allow using DMA on multiplatform kernels.
 
+config USB_MUSB_PERIPHERAL_HOTPLUG
+	bool "Support Ingenic USB Device Controller Hotplug"
+	depends on USB_MUSB_JZ4770
+
 config USB_UX500_DMA
 	bool 'ST Ericsson U8500 and U5500'
 	depends on USB_MUSB_UX500
@@ -90,7 +98,7 @@ config USB_UX500_DMA
 
 config USB_INVENTRA_DMA
 	bool 'Inventra'
-	depends on USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
+	depends on USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN || USB_MUSB_JZ4770
 	help
 	  Enable DMA transfers using Mentor's engine.
 
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 3b85871..f722f76 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_MUSB_DAVINCI)			+= davinci.o
 obj-$(CONFIG_USB_MUSB_DA8XX)			+= da8xx.o
 obj-$(CONFIG_USB_MUSB_BLACKFIN)			+= blackfin.o
 obj-$(CONFIG_USB_MUSB_UX500)			+= ux500.o
+obj-$(CONFIG_USB_MUSB_JZ4770)			+= jz4760.o
 
 # the kconfig must guarantee that only one of the
 # possible I/O schemes will be enabled at a time ...
diff --git a/drivers/usb/musb/jz4760.c b/drivers/usb/musb/jz4760.c
new file mode 100644
index 0000000..0aabff0
--- /dev/null
+++ b/drivers/usb/musb/jz4760.c
@@ -0,0 +1,383 @@
+/*
+ * Author: River <zwang@ingenic.cn>
+ * Restructured by Maarten ter Huurne <maarten@treewalker.org>, using the
+ * tusb6010 module as a template.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+
+#include <asm/mach-jz4770/jz4770.h>
+
+#include "musb_core.h"
+
+struct jz_musb_glue {
+	struct device		*dev;
+	struct platform_device	*musb;
+};
+
+static inline void jz_musb_phy_enable(void)
+{
+	printk(KERN_INFO "jz4760: Enable USB PHY.\n");
+
+	__cpm_enable_otg_phy();
+
+	/* Wait PHY Clock Stable. */
+	udelay(300);
+}
+
+static inline void jz_musb_phy_disable(void)
+{
+	printk(KERN_INFO "jz4760: Disable USB PHY.\n");
+
+	__cpm_suspend_otg_phy();
+}
+
+static inline void jz_musb_phy_reset(void)
+{
+	REG_CPM_USBPCR |= USBPCR_POR;
+	udelay(30);
+	REG_CPM_USBPCR &= ~USBPCR_POR;
+
+	udelay(300);
+}
+
+static inline void jz_musb_set_device_only_mode(void)
+{
+	printk(KERN_INFO "jz4760: Device only mode.\n");
+
+	/* Device Mode. */
+	REG_CPM_USBPCR &= ~(1 << 31);
+
+	REG_CPM_USBPCR |= USBPCR_VBUSVLDEXT;
+}
+
+static inline void jz_musb_set_normal_mode(void)
+{
+	printk(KERN_INFO "jz4760: Normal mode.\n");
+
+	__gpio_as_otg_drvvbus();
+
+	/* OTG Mode. */
+	REG_CPM_USBPCR |= (1 << 31);
+
+	REG_CPM_USBPCR &= ~((1 << 24) | (1 << 23) | (1 << 20));
+
+	REG_CPM_USBPCR |= ((1 << 28) | (1 << 29));
+}
+
+static inline void jz_musb_init_regs(struct musb *musb)
+{
+	/* fil */
+	REG_CPM_USBVBFIL = 0x80;
+
+	/* rdt */
+	REG_CPM_USBRDT = 0x96;
+
+	/* rdt - filload_en */
+	REG_CPM_USBRDT |= (1 << 25);
+
+	/* TXRISETUNE & TXVREFTUNE. */
+	REG_CPM_USBPCR &= ~0x3f;
+	REG_CPM_USBPCR |= 0x35;
+
+	if (is_host_enabled(musb)) {
+		jz_musb_set_normal_mode();
+	}else
+		jz_musb_set_device_only_mode();
+
+	jz_musb_phy_reset();
+}
+
+static void jz_musb_set_vbus(struct musb *musb, int is_on)
+{
+	u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+	/* HDRC controls CPEN, but beware current surges during device
+	 * connect.  They can trigger transient overcurrent conditions
+	 * that must be ignored.
+	 */
+
+	if (is_on) {
+		musb->is_active = 1;
+		musb->xceiv->otg->default_a = 1;
+		musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+		devctl |= MUSB_DEVCTL_SESSION;
+
+		MUSB_HST_MODE(musb);
+	} else {
+		musb->is_active = 0;
+
+		/* NOTE:  we're skipping A_WAIT_VFALL -> A_IDLE and
+		 * jumping right to B_IDLE...
+		 */
+
+		musb->xceiv->otg->default_a = 0;
+		musb->xceiv->state = OTG_STATE_B_IDLE;
+		devctl &= ~MUSB_DEVCTL_SESSION;
+
+		MUSB_DEV_MODE(musb);
+	}
+	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+	dev_dbg(musb->xceiv->dev, "VBUS %s, devctl %02x "
+		/* otg %3x conf %08x prcm %08x */ "\n",
+		otg_state_string(musb->xceiv->state),
+		musb_readb(musb->mregs, MUSB_DEVCTL));
+}
+
+/* ---------------------- OTG ID PIN Routines ---------------------------- */
+
+static struct timer_list otg_id_pin_stable_timer;
+
+static unsigned int read_gpio_pin(unsigned int pin, unsigned int loop)
+{
+	unsigned int t, v;
+	unsigned int i;
+
+	i = loop;
+
+	v = t = 0;
+
+	while (i--) {
+		t = __gpio_get_pin(pin);
+		if (v != t)
+			i = loop;
+
+		v = t;
+	}
+
+	return v;
+}
+
+static void do_otg_id_pin_state(struct musb *musb)
+{
+	unsigned int default_a;
+	unsigned int pin = read_gpio_pin(GPIO_OTG_ID_PIN, 5000);
+
+	default_a = !pin;
+
+	musb->xceiv->otg->default_a = default_a;
+
+	jz_musb_set_vbus(musb, default_a);
+
+	if (pin) {
+		/* B */
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+		__gpio_unmask_irq(OTG_HOTPLUG_PIN);
+#endif
+		__gpio_as_irq_fall_edge(GPIO_OTG_ID_PIN);
+	} else {
+		/* A */
+		if (is_otg_enabled(musb)) {
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+			__gpio_mask_irq(OTG_HOTPLUG_PIN); // otg's host mode not support hotplug
+#endif
+			__gpio_as_irq_rise_edge(GPIO_OTG_ID_PIN);
+		}
+	}
+}
+
+static void otg_id_pin_stable_func(unsigned long data)
+{
+	struct musb *musb = (struct musb *)data;
+
+	do_otg_id_pin_state(musb);
+}
+
+static irqreturn_t jz_musb_otg_id_irq(int irq, void *data)
+{
+	mod_timer(&otg_id_pin_stable_timer, GPIO_OTG_STABLE_JIFFIES + jiffies);
+
+	return IRQ_HANDLED;
+}
+
+static int otg_id_pin_setup(struct musb *musb)
+{
+	int rv;
+
+	/* Update OTG ID PIN state. */
+	do_otg_id_pin_state(musb);
+	setup_timer(&otg_id_pin_stable_timer, otg_id_pin_stable_func, (unsigned long)musb);
+
+	rv = request_irq(GPIO_OTG_ID_IRQ, jz_musb_otg_id_irq,
+				IRQF_DISABLED, "otg-id-irq", musb);
+	if (rv) {
+		pr_err("Failed to request OTG_ID_IRQ.\n");
+		return rv;
+	}
+
+	return rv;
+}
+
+static void otg_id_pin_cleanup(struct musb *musb)
+{
+	free_irq(GPIO_OTG_ID_IRQ, "otg-id-irq");
+	del_timer(&otg_id_pin_stable_timer);
+}
+
+/* ---------------------------------------------------------------- */
+
+static int __init jz_musb_platform_init(struct musb *musb)
+{
+	int ret = 0;
+
+	usb_nop_xceiv_register();
+	musb->xceiv = usb_get_transceiver();
+	if (!musb->xceiv) {
+		pr_err("HS USB OTG: no transceiver configured\n");
+		return -ENODEV;
+	}
+
+	musb->b_dma_share_usb_irq = 1;
+
+	jz_musb_init_regs(musb);
+
+	/* host mode and otg(host) depend on the id pin */
+	if (is_host_enabled(musb)) {
+		ret = otg_id_pin_setup(musb);
+		if (ret)
+			usb_nop_xceiv_unregister();
+	}
+
+	return ret;
+}
+
+static int jz_musb_platform_exit(struct musb *musb)
+{
+	jz_musb_phy_disable();
+
+	if (is_host_enabled(musb))
+		otg_id_pin_cleanup(musb);
+
+	usb_nop_xceiv_unregister();
+
+	return 0;
+}
+
+static void jz_musb_platform_enable(struct musb *musb)
+{
+	jz_musb_phy_enable();
+}
+
+static void jz_musb_platform_disable(struct musb *musb)
+{
+	jz_musb_phy_disable();
+}
+
+static const struct musb_platform_ops jz_musb_ops = {
+	.init		= jz_musb_platform_init,
+	.exit		= jz_musb_platform_exit,
+
+	.enable		= jz_musb_platform_enable,
+	.disable	= jz_musb_platform_disable,
+
+	.set_vbus	= jz_musb_set_vbus,
+};
+
+static u64 jz_musb_dmamask = DMA_BIT_MASK(32);
+
+static int __init jz_musb_probe(struct platform_device *pdev)
+{
+	struct musb_hdrc_platform_data	*pdata = pdev->dev.platform_data;
+	struct platform_device		*musb;
+	struct jz_musb_glue		*glue;
+
+	int				ret = -ENOMEM;
+
+	glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+	if (!glue) {
+		dev_err(&pdev->dev, "failed to allocate glue context\n");
+		goto err0;
+	}
+
+	musb = platform_device_alloc("musb-hdrc", -1);
+	if (!musb) {
+		dev_err(&pdev->dev, "failed to allocate musb device\n");
+		goto err1;
+	}
+
+	musb->dev.parent		= &pdev->dev;
+	musb->dev.dma_mask		= &jz_musb_dmamask;
+	musb->dev.coherent_dma_mask	= jz_musb_dmamask;
+
+	glue->dev			= &pdev->dev;
+	glue->musb			= musb;
+
+	pdata->platform_ops		= &jz_musb_ops;
+
+	platform_set_drvdata(pdev, glue);
+
+	ret = platform_device_add_resources(musb, pdev->resource,
+			pdev->num_resources);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add resources\n");
+		goto err2;
+	}
+
+	ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add platform_data\n");
+		goto err2;
+	}
+
+	ret = platform_device_add(musb);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register musb device\n");
+		goto err2;
+	}
+
+	return 0;
+
+err2:
+	platform_device_put(musb);
+
+err1:
+	kfree(glue);
+
+err0:
+	return ret;
+}
+
+static int __exit jz_musb_remove(struct platform_device *pdev)
+{
+	struct jz_musb_glue		*glue = platform_get_drvdata(pdev);
+
+	platform_device_del(glue->musb);
+	platform_device_put(glue->musb);
+	kfree(glue);
+
+	return 0;
+}
+
+static struct platform_driver jz_musb_driver = {
+	.remove		= __exit_p(jz_musb_remove),
+	.driver		= {
+		.name	= "musb-jz",
+	},
+};
+
+static int __init jz_musb_init(void)
+{
+	return platform_driver_probe(&jz_musb_driver, jz_musb_probe);
+}
+subsys_initcall(jz_musb_init);
+
+static void __exit jz_musb_exit(void)
+{
+	platform_driver_unregister(&jz_musb_driver);
+}
+module_exit(jz_musb_exit);
+
+MODULE_DESCRIPTION("JZ4770 MUSB Glue Layer");
+MODULE_AUTHOR("River <zwang@ingenic.cn>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index db3dff8..fccc112 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -120,6 +120,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:" MUSB_DRIVER_NAME);
 
+extern void uh_alive(void);
 
 /*-------------------------------------------------------------------------*/
 
@@ -1499,26 +1500,37 @@ static int __devinit musb_core_init(u16 musb_type, struct musb *musb)
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \
-	defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
+	defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) || \
+	defined(CONFIG_MACH_JZ4770)
 
 static irqreturn_t generic_interrupt(int irq, void *__hci)
 {
 	unsigned long	flags;
-	irqreturn_t	retval = IRQ_NONE;
 	struct musb	*musb = __hci;
 
+	irqreturn_t rv, rv_dma, rv_usb;
+	rv = rv_dma = rv_usb = IRQ_NONE;
+
 	spin_lock_irqsave(&musb->lock, flags);
 
+#if defined(CONFIG_USB_INVENTRA_DMA)
+	if (musb->b_dma_share_usb_irq)
+		rv_dma = musb_call_dma_controller_irq(irq, musb);
+#endif
+
 	musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
 	musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
 	musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
 
 	if (musb->int_usb || musb->int_tx || musb->int_rx)
-		retval = musb_interrupt(musb);
+		rv_usb = musb_interrupt(musb);
 
 	spin_unlock_irqrestore(&musb->lock, flags);
 
-	return retval;
+	rv = (rv_dma == IRQ_HANDLED || rv_usb == IRQ_HANDLED) ?
+		IRQ_HANDLED : IRQ_NONE;
+
+	return rv;
 }
 
 #else
@@ -1538,6 +1550,9 @@ irqreturn_t musb_interrupt(struct musb *musb)
 	u8		devctl, power;
 	int		ep_num;
 	u32		reg;
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+	uh_alive();
+#endif
 
 	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
 	power = musb_readb(musb->mregs, MUSB_POWER);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index f4a40f0..78f171e 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -451,6 +451,8 @@ struct musb {
 	 */
 	unsigned                double_buffer_not_ok:1;
 
+	unsigned int b_dma_share_usb_irq;
+
 	struct musb_hdrc_config	*config;
 
 #ifdef MUSB_CONFIG_PROC_FS
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 3a97c4e..e8ebef0 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -159,6 +159,10 @@ dma_channel_status(struct dma_channel *c)
  * Controllers manage dma channels.
  */
 struct dma_controller {
+	/* Added by River - For DMA IRQ Sharing. */
+	u8			int_hsdma;
+	/* End added. */
+
 	int			(*start)(struct dma_controller *);
 	int			(*stop)(struct dma_controller *);
 	struct dma_channel	*(*channel_alloc)(struct dma_controller *,
@@ -183,4 +187,5 @@ dma_controller_create(struct musb *, void __iomem *);
 
 extern void dma_controller_destroy(struct dma_controller *);
 
+extern irqreturn_t musb_call_dma_controller_irq(int irq, struct musb *musb);
 #endif	/* __MUSB_DMA_H__ */
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 95918da..35b10cd 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -45,6 +45,9 @@
 
 #include "musb_core.h"
 
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+#include "vbus_hotplug.c"
+#endif
 
 /* MUSB PERIPHERAL status 3-mar-2006:
  *
@@ -1886,10 +1889,21 @@ int __devinit musb_gadget_setup(struct musb *musb)
 	}
 	status = usb_add_gadget_udc(musb->controller, &musb->g);
 	if (status)
-		goto err;
+		goto err0;
+
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+	status = musb_gadget_hotplug_setup(musb);
+	if (status != 0)
+		goto err1;
+#endif
 
 	return 0;
-err:
+
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+err1:
+	usb_del_gadget_udc(&musb->g);
+#endif
+err0:
 	musb->g.dev.parent = NULL;
 	device_unregister(&musb->g.dev);
 	return status;
@@ -1902,6 +1916,67 @@ void musb_gadget_cleanup(struct musb *musb)
 		device_unregister(&musb->g.dev);
 }
 
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver);
+
+static int jz_musb_vbus_hotplug_event(struct notifier_block *n,
+		unsigned long val, void *data)
+{
+	struct musb *musb = the_gadget;
+
+	unsigned long flags;
+
+	int state = *((int *)data);
+
+	D("Called.\n");
+
+	if (!musb || !musb->gadget_driver)
+		return 0;
+
+	switch (val) {
+	case UH_NOTIFY_CABLE_STATE:
+		switch (state) {
+		case UH_CABLE_STATE_OFFLINE:
+		case UH_CABLE_STATE_POWER:
+			D("OFFLINE.\n");
+
+			spin_lock_irqsave(&musb->lock, flags);
+
+			musb_gadget_vbus_draw(&musb->g, 0);
+			musb->xceiv->state = OTG_STATE_UNDEFINED;
+			stop_activity(musb, musb->gadget_driver);
+			musb->is_active = 0;
+
+			spin_unlock_irqrestore(&musb->lock, flags);
+
+			break;
+
+		case UH_CABLE_STATE_USB:
+			D("ONLINE.\n");
+
+			spin_lock_irqsave(&musb->lock, flags);
+
+			musb->xceiv->gadget = &musb->g;
+			musb->xceiv->state = OTG_STATE_B_IDLE;
+			musb->is_active = 1;
+
+			if (!is_otg_enabled(musb))
+				musb_start(musb);
+
+			spin_unlock_irqrestore(&musb->lock, flags);
+
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static struct notifier_block jz_musb_vbus_hotplug_nb = {
+	.notifier_call = jz_musb_vbus_hotplug_event,
+};
+#endif
+
 /*
  * Register the gadget driver. Used by gadget drivers when
  * registering themselves with the controller.
@@ -1934,6 +2009,9 @@ static int musb_gadget_start(struct usb_gadget *g,
 	spin_lock_irqsave(&musb->lock, flags);
 	musb->is_active = 1;
 
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+	uh_register_notifier(&jz_musb_vbus_hotplug_nb);
+#else
 	otg_set_peripheral(otg, &musb->g);
 	musb->xceiv->state = OTG_STATE_B_IDLE;
 
@@ -1946,6 +2024,7 @@ static int musb_gadget_start(struct usb_gadget *g,
 
 	if (!is_otg_enabled(musb))
 		musb_start(musb);
+#endif
 
 	spin_unlock_irqrestore(&musb->lock, flags);
 
@@ -2035,6 +2114,9 @@ static int musb_gadget_stop(struct usb_gadget *g,
 	if (musb->xceiv->last_event == USB_EVENT_NONE)
 		pm_runtime_get_sync(musb->controller);
 
+#ifdef CONFIG_USB_MUSB_PERIPHERAL_HOTPLUG
+	uh_unregister_notifier(&jz_musb_vbus_hotplug_nb);
+#endif
 	/*
 	 * REVISIT always use otg_set_peripheral() here too;
 	 * this needs to shut down the OTG engine.
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 57a6085..8becf11 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -257,7 +257,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
 
 	irqreturn_t retval = IRQ_NONE;
 
-	unsigned long flags;
+	unsigned long flags = 0;
 
 	u8 bchannel;
 	u8 int_hsdma;
@@ -265,9 +265,13 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
 	u32 addr, count;
 	u16 csr;
 
-	spin_lock_irqsave(&musb->lock, flags);
 
-	int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
+	if (!musb->b_dma_share_usb_irq) {
+		spin_lock_irqsave(&musb->lock, flags);
+
+		int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
+	} else
+		int_hsdma = controller->controller.int_hsdma;
 
 #ifdef CONFIG_BLACKFIN
 	/* Clear DMA interrupt flags */
@@ -362,7 +366,8 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
 
 	retval = IRQ_HANDLED;
 done:
-	spin_unlock_irqrestore(&musb->lock, flags);
+	if (!musb->b_dma_share_usb_irq)
+		spin_unlock_irqrestore(&musb->lock, flags);
 	return retval;
 }
 
@@ -388,10 +393,27 @@ dma_controller_create(struct musb *musb, void __iomem *base)
 	struct platform_device *pdev = to_platform_device(dev);
 	int irq = platform_get_irq_byname(pdev, "dma");
 
-	if (irq == 0) {
-		dev_err(dev, "No DMA interrupt line!\n");
-		return NULL;
+	u8 nr_dma_channel;
+
+	/* Modified by River - Get DMA Channels from RAMINFO. */
+	nr_dma_channel = musb_readb(musb->mregs, MUSB_RAMINFO);
+	nr_dma_channel >>= 4;
+
+	/* Modified by River - DMA Share IRQ with USB. */
+	if (musb->b_dma_share_usb_irq) {
+		irq = 0;
+
+		dev_info(dev, "DMA IRQ: Shared. DMA Channels: %d.\n", nr_dma_channel);
+	}else{
+		irq = platform_get_irq(pdev, 1);
+
+		if (irq == 0) {
+			dev_err(dev, "No DMA interrupt line!\n");
+			return NULL;
+		}
+		dev_info(dev, "DMA IRQ: %d. DMA Channels: %d.\n", irq, nr_dma_channel);
 	}
+	/* End modified */
 
 	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
 	if (!controller)
@@ -408,15 +430,39 @@ dma_controller_create(struct musb *musb, void __iomem *base)
 	controller->controller.channel_program = dma_channel_program;
 	controller->controller.channel_abort = dma_channel_abort;
 
-	if (request_irq(irq, dma_controller_irq, 0,
-			dev_name(musb->controller), &controller->controller)) {
-		dev_err(dev, "request_irq %d failed!\n", irq);
-		dma_controller_destroy(&controller->controller);
-
-		return NULL;
+	if (irq) {
+		if (request_irq(irq, dma_controller_irq, 0,
+				dev_name(musb->controller),
+				&controller->controller)) {
+			dev_err(dev, "request_irq %d failed!\n", irq);
+			dma_controller_destroy(&controller->controller);
+			return NULL;
+		}
 	}
 
 	controller->irq = irq;
 
 	return &controller->controller;
 }
+
+/* Added by river - For DMA IRQ Sharing */
+static u8 dma_controller_fetch_intr(struct dma_controller *c)
+{
+	struct musb_dma_controller *mc = container_of(c, struct musb_dma_controller, controller);
+
+	c->int_hsdma = musb_readb(mc->base, MUSB_HSDMA_INTR);
+
+	return c->int_hsdma;
+}
+
+irqreturn_t musb_call_dma_controller_irq(int irq, struct musb *musb)
+{
+	if (!musb->b_dma_share_usb_irq)
+		return IRQ_NONE;
+
+	if (dma_controller_fetch_intr(musb->dma_controller))
+		return dma_controller_irq(irq, (void *)musb->dma_controller);
+
+	return IRQ_NONE;
+}
+/* End added */
diff --git a/drivers/usb/musb/vbus_hotplug.c b/drivers/usb/musb/vbus_hotplug.c
new file mode 100644
index 0000000..c54afce
--- /dev/null
+++ b/drivers/usb/musb/vbus_hotplug.c
@@ -0,0 +1,779 @@
+/*
+ * Ingenic USB Device Controller Hotplug Driver.
+ *
+ * Author: River Wang <zwang@ingenic.cn>
+ */
+
+/*
+How to use
+
+1. Basic usage:
+All controlling interfaces are locacated in /sys/devices/platform/jz4740_udc/.
+[uh_cable]: It indicates the status of the cable: offline/power/usb.
+
+2. Asychronous notification:
+You can use a non-blocking PF_NETLINK socket to receive the uevent sent by udc_houplug. DRIVER & UDC_HOTPLUG_CABLE_STATE varible in uevent can be used to get the status of UDC cable.
+
+3. Notification mode & Gadget loading:
+There are two notification modes can be configured by [uh_notify_mode]: auto/manual.
+Auto: When a USB cable with active signals is plugged in, the udc_hotplug driver will broadcast this event to jz4740_udc & userspace app, and jz4740_udc will try to activate the current gadget.
+
+This mode is used when userspace APP only wants to get the cable status notification.
+
+Manual: The event will only be broadcast to userspace APP. When APP decides to how to handle this event, It can make it with [uh_notify]:
+auto: The udc_hotplug driver broadcast the latest cable status to jz4740_udc.
+offline/power/usb:....
+
+This mode is recommended when multiple gadgets are used. Userspace APP is notified by the uevent, load the specific gadget module by insmod, then set [uh_notify] to broadcat the event at last.
+*/
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/wait.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/kobject.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-jz4770/jz4770.h>
+
+#define D(msg,   fmt...)
+//#define D(msg, fmt...)  \
+	printk(KERN_ERR JZ_VH_PFX": %s(): "msg, __func__, ##fmt)
+
+#define JZ_VH_PFX "jz_vbus_hotplug"
+
+#define NR_UDC_WAIT_INTR_LOOP	(5 * 1000 * 1000)
+
+#define DEFAULT_KEEP_ALIVE_TIMER_INTERVAL     (1 * HZ)
+#define DEFAULT_KEEP_ALIVE_COUNTER_LIMIT      2
+
+
+typedef enum {
+	UH_NOTIFY_CABLE_STATE = 0,
+}uh_notify_type_t;
+
+typedef enum {
+	UH_CABLE_STATE_OFFLINE = 0,
+	UH_CABLE_STATE_POWER,
+	UH_CABLE_STATE_USB,
+}uh_cable_state_t;
+
+typedef enum {
+	UH_THREAD_STATE_IDLE = 0,
+	UH_THREAD_STATE_START,
+	UH_THREAD_STATE_BUSY,
+	UH_THREAD_STATE_DONE,
+}uh_thread_state_t;
+
+/* UDC Flag bits */
+enum {
+	BIT_UH_ENABLE,
+
+	/* State changed ?*/
+	BIT_CABLE_STATE_CHANGE,
+
+	BIT_DO_DETECT,
+	BIT_DO_NOTIFY,
+
+	/* Keep alive */
+	BIT_KEEP_ALIVE,
+	BIT_KEEP_ALIVE_STOP,
+};
+
+#define DO_DETECT (1 << BIT_DO_DETECT)
+#define DO_NOTIFY (1 << BIT_DO_NOTIFY)
+#define DO_ALL ( DO_DETECT | DO_NOTIFY)
+
+struct uh_data {
+	unsigned long flags;
+
+	/* Notifier */
+	struct blocking_notifier_head notifier_head;
+
+	/* Thread */
+	struct task_struct *kthread;
+
+	int b_notify_mode;
+
+	/* Wait queue */
+	wait_queue_head_t kthread_wq;	/* Kernel thread sleep here. */
+	wait_queue_head_t timer_wq;	/* Wake up when timer is finished. */
+	wait_queue_head_t finish_wq;	/* Wake up when thread is finished. */
+
+	uh_thread_state_t thread_state;
+	uh_cable_state_t cable_state;
+	uh_cable_state_t cable_detect_state;
+
+	struct timer_list stable_timer;
+	struct timer_list keep_alive_timer; /* Keep alive */
+
+	unsigned long keep_alive_counter_limit;
+	unsigned long keep_alive_timer_interval;
+	unsigned long keep_alive_counter;
+
+	int gpio_irq;
+	int gpio_pin;
+
+	struct platform_device *pdev;
+};
+
+static struct uh_data *g_puh_data = NULL;
+
+static inline void uh_start_work(struct uh_data *uh, int work)
+{
+	if (work & DO_DETECT) {
+		set_bit(BIT_DO_DETECT, &uh->flags);
+	}
+
+	if (work & DO_NOTIFY) {
+		set_bit(BIT_DO_NOTIFY, &uh->flags);
+	}
+
+	mod_timer(&uh->stable_timer, 1 + jiffies);
+
+	return;
+}
+
+static inline void set_cable_state(struct uh_data *uh, uh_cable_state_t state)
+{
+	if (uh->cable_state != state) {
+		D("Cable state: %d -> %d.\n", uh->cable_state, state);
+
+		uh->cable_state = state;
+		set_bit(BIT_CABLE_STATE_CHANGE, &uh->flags);
+	}
+
+	return;
+}
+
+static void uh_stable_timer_func(unsigned long data)
+{
+	struct uh_data *uh = (struct uh_data *)data;
+
+	D("Called.\n");
+
+	if (!test_bit(BIT_UH_ENABLE, &uh->flags))
+		return;
+
+	uh->thread_state = UH_THREAD_STATE_START;
+
+	/* Start. */
+	wake_up_process(uh->kthread);
+
+	return;
+}
+
+/* Do cable detection */
+static void cable_detect(struct uh_data *uh)
+{
+	if (__gpio_get_pin(uh->gpio_pin)) {
+		D("Cable online.\n");
+
+		uh->cable_detect_state = UH_CABLE_STATE_POWER;
+
+	}else {
+		D("Cable offline.\n");
+
+		clear_bit(BIT_KEEP_ALIVE, &uh->flags);
+
+		uh->cable_detect_state = UH_CABLE_STATE_OFFLINE;
+	}
+
+	return;
+}
+
+/* USB is active ? */
+static int usb_is_active(void)
+{
+	unsigned volatile long timeout = NR_UDC_WAIT_INTR_LOOP;
+	unsigned long frame_no = REG16(USB_REG_FRAME);
+
+	/*
+	   Some power charger may cause fake SOF,
+	   We must handle this situation.
+					- River.
+	*/
+
+	int counter = 7;
+
+	while (timeout && counter) {
+		if (frame_no != REG16(USB_REG_FRAME)) {
+			if (!--counter)
+				break;
+
+			/* Wait next frame. */
+			frame_no = REG16(USB_REG_FRAME);
+		}
+
+		timeout --;
+	}
+
+	D("timout: %lu, counter: %d.\n", timeout, counter);
+
+	return timeout ? 1 : 0;
+}
+
+/* Really do USB detection */
+static int do_usb_detect(struct uh_data *uh)
+{
+	int rv;
+
+	D("Called.\n");
+
+	__intc_mask_irq(IRQ_OTG);
+
+	/* Now enable PHY to start detect */
+	__cpm_enable_otg_phy();
+
+	/* Clear IRQs */
+	REG16(USB_REG_INTRINE) = 0;
+	REG16(USB_REG_INTROUTE) = 0;
+	REG8(USB_REG_INTRUSBE) = 0;
+
+	/* disable UDC IRQs first */
+	REG16(USB_REG_INTRINE) = 0;
+	REG16(USB_REG_INTROUTE) = 0;
+	REG8(USB_REG_INTRUSBE) = 0;
+
+	/* Disable DMA */
+	REG32(USB_REG_CNTL1) = 0;
+	REG32(USB_REG_CNTL2) = 0;
+
+	/* Enable HS Mode */
+	REG8(USB_REG_POWER) |= USB_POWER_HSENAB;
+	/* Enable soft connect */
+	REG8(USB_REG_POWER) |= USB_POWER_SOFTCONN;
+
+	rv = usb_is_active();
+
+	/* Detect finish ,clean every thing */
+	/* Disconnect from usb */
+	REG8(USB_REG_POWER) &= ~USB_POWER_SOFTCONN;
+
+	/* Disable the USB PHY */
+	__cpm_suspend_otg_phy();
+
+	/* Clear IRQs */
+	REG16(USB_REG_INTRINE) = 0;
+	REG16(USB_REG_INTROUTE) = 0;
+	REG8(USB_REG_INTRUSBE) = 0;
+
+	__intc_ack_irq(IRQ_OTG);
+	__intc_unmask_irq(IRQ_OTG);
+
+	return rv;
+}
+
+/* Do USB bus protocol detection */
+static void usb_detect(struct uh_data *uh)
+{
+	int rv = 0;
+
+	D("Called.\n");
+
+	/* If the cable has already been offline, we just pass the real USB detection. */
+	if (uh->cable_detect_state != UH_CABLE_STATE_OFFLINE) {
+		D("Do real detection.\n");
+		rv = do_usb_detect(uh);
+	}else{
+		D("No need to do real detection.\n");
+	}
+
+	if (rv) {
+		/* Online. */
+		uh->cable_detect_state = UH_CABLE_STATE_USB;
+	}else{
+		/* No USB Signal. */
+		if (uh->cable_detect_state == UH_CABLE_STATE_POWER) {
+			/* TODO: Wait USB alive again. */
+		}
+	}
+
+	return;
+}
+
+static void do_wait(struct uh_data *uh)
+{
+	D("Called.\n");
+
+	wait_event(uh->kthread_wq, uh->thread_state == UH_THREAD_STATE_START);
+
+	uh->thread_state = UH_THREAD_STATE_BUSY;
+
+	return;
+}
+
+/* Called from kernel thread */
+static void do_detect(struct uh_data *uh)
+{
+	D("Called.\n");
+
+	if (!test_and_clear_bit(BIT_DO_DETECT, &uh->flags))
+		return;
+
+	D("Do detect.\n");
+
+	if(__gpio_get_pin(uh->gpio_pin)) {
+		cable_detect(uh);
+		usb_detect(uh);
+
+		set_cable_state(uh, uh->cable_detect_state);
+	}else{
+		set_cable_state(uh, UH_CABLE_STATE_OFFLINE);
+	}
+
+	return;
+}
+
+static void __do_notify(struct uh_data *uh)
+{
+	D("Called.\n");
+
+	if (test_and_clear_bit(BIT_CABLE_STATE_CHANGE, &uh->flags)) {
+		D("Kick notifier chain.\n");
+
+		blocking_notifier_call_chain(&uh->notifier_head,
+			UH_NOTIFY_CABLE_STATE, &uh->cable_state);
+
+
+		D("Send uevent to userspace.\n");
+
+		switch (uh->cable_state) {
+			case UH_CABLE_STATE_USB:
+				kobject_uevent(&uh->pdev->dev.kobj, KOBJ_ADD);
+				break;
+
+			case UH_CABLE_STATE_POWER:
+				kobject_uevent(&uh->pdev->dev.kobj, KOBJ_CHANGE);
+				break;
+
+			case UH_CABLE_STATE_OFFLINE:
+				kobject_uevent(&uh->pdev->dev.kobj, KOBJ_REMOVE);
+				break;
+		}
+	}
+
+	return;
+}
+
+static inline void do_notify(struct uh_data *uh)
+{
+	if (!uh->b_notify_mode)	/* Auto nofity mode. */
+		set_bit(BIT_DO_NOTIFY, &uh->flags);
+
+	if (test_and_clear_bit(BIT_DO_NOTIFY, &uh->flags))
+		__do_notify(uh);
+
+	return;
+}
+
+static inline void do_done(struct uh_data *uh)
+{
+	D("Done.\n");
+
+	uh->thread_state = UH_THREAD_STATE_IDLE;
+
+	wake_up(&uh->finish_wq);
+
+	return;
+}
+
+/* Kernel thread */
+static int uh_thread(void *data)
+{
+	struct uh_data *uh = (struct uh_data *)data;
+
+	while (!kthread_should_stop()) {
+		do_wait(uh);
+
+		if (kthread_should_stop())
+			break;
+
+		do_detect(uh);
+
+		do_notify(uh);
+
+		do_done(uh);
+	}
+
+	D("Exit.\n");
+
+	return 0;
+}
+
+static irqreturn_t uh_irq(int irq, void *dev_id)
+{
+	struct uh_data *uh = (struct uh_data *)dev_id;
+
+	D("Called.\n");
+
+	uh_start_work(uh, DO_DETECT);
+
+	return IRQ_HANDLED;
+}
+
+static void uh_init_gpio(struct uh_data *uh)
+{
+        /* get current pin level */
+	__gpio_disable_pull(uh->gpio_pin);
+        __gpio_as_input(uh->gpio_pin);
+
+	udelay(1);
+
+	/* Because of every plug IN/OUT action will casue more than one interrupt,
+	   So whether rising trigger or falling trigger method can both start the detection.
+         */
+
+	__gpio_as_irq_rise_edge(uh->gpio_pin);
+
+	return;
+}
+
+static void uh_keep_alive_timer_func(unsigned long data)
+{
+	struct uh_data *uh = (struct uh_data *)data;
+
+//	D("Timer running.\n");
+
+	/* Decrease the counter. */
+	if (test_bit(BIT_KEEP_ALIVE, &uh->flags)
+			&& !(--uh->keep_alive_counter)) {
+
+		if (!usb_is_active()) {
+			D("Timeout.\n");
+
+			clear_bit(BIT_KEEP_ALIVE, &uh->flags);
+
+			if (uh->cable_state == UH_CABLE_STATE_USB)
+				set_cable_state(uh, UH_CABLE_STATE_POWER);
+
+			uh_start_work(uh, DO_NOTIFY);
+		}
+	}
+
+	/* Set next active time. */
+	if (test_bit(BIT_KEEP_ALIVE, &uh->flags)) {
+		mod_timer(&uh->keep_alive_timer, uh->keep_alive_timer_interval + jiffies);
+	}else{
+		D("Timer will stop.\n");
+
+		set_bit(BIT_KEEP_ALIVE_STOP, &uh->flags);
+		wake_up(&uh->timer_wq);
+	}
+
+	return;
+}
+
+static void uh_set_counter(unsigned long timer_interval_in_jiffies, unsigned long counter_limit)
+{
+	struct uh_data *uh = g_puh_data;
+
+	uh->keep_alive_timer_interval = timer_interval_in_jiffies;
+	uh->keep_alive_counter_limit = counter_limit;
+
+	uh->keep_alive_counter = uh->keep_alive_counter_limit;
+
+	return;
+}
+
+static void uh_disable(void)
+{
+	struct uh_data *uh = g_puh_data;
+
+	/* Disable the source of input. */
+	clear_bit(BIT_UH_ENABLE, &uh->flags);
+
+	if (test_and_clear_bit(BIT_KEEP_ALIVE, &uh->flags))
+		/* Wait timer stop. */
+		wait_event(uh->timer_wq, test_and_clear_bit(BIT_KEEP_ALIVE_STOP, &uh->flags));
+
+	/* Wait thread idle. */
+	wait_event(uh->finish_wq, uh->thread_state == UH_THREAD_STATE_IDLE);
+
+	return;
+}
+
+static void uh_enable(void)
+{
+	struct uh_data *uh = g_puh_data;
+
+	set_bit(BIT_UH_ENABLE, &uh->flags);
+
+	return;
+}
+
+void uh_alive(void)
+{
+	struct uh_data *uh = g_puh_data;
+
+	if (!test_bit(BIT_UH_ENABLE, &uh->flags))
+		return;
+
+        /* Reset counter */
+	uh->keep_alive_counter = uh->keep_alive_counter_limit;
+
+	/* We are alive. */
+	if (!test_bit(BIT_KEEP_ALIVE, &uh->flags)) {
+		D("Active timer.\n");
+
+		/* Active timer. */
+		set_bit(BIT_KEEP_ALIVE, &uh->flags);
+		clear_bit(BIT_KEEP_ALIVE_STOP, &uh->flags);
+
+		mod_timer(&uh->keep_alive_timer, 3 + jiffies);
+	}
+
+	return;
+}
+EXPORT_SYMBOL(uh_alive);
+
+int uh_register_notifier(struct notifier_block *n)
+{
+	struct uh_data *uh = g_puh_data;
+
+	D("Register notifier: 0x%p.\n", (void *)n);
+
+	BUG_ON(!n->notifier_call);
+
+	/* Notify in auto mode. */
+	if (!uh->b_notify_mode)
+		n->notifier_call(n, UH_NOTIFY_CABLE_STATE, &uh->cable_state);
+
+	return blocking_notifier_chain_register(&uh->notifier_head, n);
+}EXPORT_SYMBOL(uh_register_notifier);
+
+int uh_unregister_notifier(struct notifier_block *n)
+{
+	struct uh_data *uh = g_puh_data;
+
+	int rv;
+
+	D("Unregister notifier: 0x%p.\n", (void *)n);
+
+	/* Wait all stop. */
+	uh_disable();
+
+	rv = blocking_notifier_chain_unregister(&uh->notifier_head, n);
+
+	uh_enable();
+
+	uh_start_work(uh, DO_DETECT);	/* Update status. */
+
+	return rv;
+}EXPORT_SYMBOL(uh_unregister_notifier);
+
+static ssize_t show_cable(struct device *device, struct device_attribute *attr,
+			char *buf)
+{
+	struct uh_data *uh = g_puh_data;
+
+	char *s = NULL;
+
+	switch (uh->cable_state) {
+		case UH_CABLE_STATE_OFFLINE:
+			s = "offline";
+			break;
+
+		case UH_CABLE_STATE_POWER:
+			s = "power";
+			break;
+
+		case UH_CABLE_STATE_USB:
+			s = "usb";
+			break;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t show_notify_mode(struct device *device, struct device_attribute *attr,
+			char *buf)
+{
+	struct uh_data *uh = g_puh_data;
+
+	char *s = uh->b_notify_mode ? "manual" : "auto";
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t store_notify_mode(struct device *device, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct uh_data *uh = g_puh_data;
+
+	if (!strncmp(buf, "auto", 4)) {
+		uh->b_notify_mode = 0;
+	}else if (!strncmp(buf, "manual", 6)) {
+		uh->b_notify_mode = 1;
+	}
+
+	return count;
+}
+
+
+static ssize_t store_notify(struct device *device, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct uh_data *uh = g_puh_data;
+
+	if (!strncmp(buf, "auto", 4)) {
+		uh_start_work(uh, DO_ALL);
+	}
+
+	return count;
+}
+
+static struct device_attribute uh_sysfs_attrs[] = {
+	__ATTR(uh_cable, S_IRUGO|S_IWUSR, show_cable, NULL),
+	__ATTR(uh_notify_mode, S_IRUGO|S_IWUSR, show_notify_mode, store_notify_mode),
+	__ATTR(uh_notify, S_IRUGO|S_IWUSR, NULL, store_notify),
+};
+
+static int uh_register_attr(struct platform_device *pdev)
+{
+	int i, error = 0;
+
+	for (i = 0; i < ARRAY_SIZE(uh_sysfs_attrs); i++) {
+		error = device_create_file(&pdev->dev, &uh_sysfs_attrs[i]);
+
+		if (error)
+			break;
+	}
+
+	if (error) {
+		while (--i >= 0)
+			device_remove_file(&pdev->dev, &uh_sysfs_attrs[i]);
+	}
+
+	return 0;
+}
+
+static void uh_unregister_attr(struct platform_device *pdev)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(uh_sysfs_attrs); i++)
+		device_remove_file(&pdev->dev, &uh_sysfs_attrs[i]);
+}
+
+static int uh_setup(struct platform_device *pdev, int gpio_irq, int gpio_pin)
+{
+	struct uh_data *uh;
+
+	unsigned long status = 0;
+
+        int rv;
+
+	g_puh_data = (struct uh_data *)kzalloc(sizeof(struct uh_data), GFP_KERNEL);
+	if (!g_puh_data) {
+		printk(KERN_ERR JZ_VH_PFX": Failed to allocate memory.\n");
+		return -ENOMEM;
+	}
+
+	uh = g_puh_data;
+
+	uh->pdev = pdev;
+	uh->gpio_irq = gpio_irq;
+	uh->gpio_pin = gpio_pin;
+
+	set_bit(1, &status);
+
+	init_waitqueue_head(&uh->kthread_wq);
+	init_waitqueue_head(&uh->timer_wq);
+	init_waitqueue_head(&uh->finish_wq);
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&uh->notifier_head);
+
+	setup_timer(&uh->keep_alive_timer, uh_keep_alive_timer_func, (unsigned long)uh);
+	setup_timer(&uh->stable_timer, uh_stable_timer_func, (unsigned long)uh);
+
+	uh->kthread = kthread_run(uh_thread, uh, "kuhd");
+	if (IS_ERR(uh->kthread)) {
+		printk(KERN_ERR JZ_VH_PFX": Failed to create UDC hotplug monitor thread.\n");
+		rv = PTR_ERR(uh->kthread);
+		goto err;
+	}
+
+	set_bit(2, &status);
+
+	uh_init_gpio(uh);
+
+	rv = uh_register_attr(pdev);
+	if (rv) {
+		printk(KERN_ERR JZ_VH_PFX": Failed to register UDC sysfs interface.\n");
+		goto err;
+	}
+
+	set_bit(3, &status);
+
+        rv = request_irq(uh->gpio_irq, uh_irq, 0, JZ_VH_PFX, uh);
+        if (rv) {
+                printk(KERN_ERR JZ_VH_PFX": Could not get udc hotplug irq %d\n", uh->gpio_irq);
+		goto err;
+        }
+
+	uh_enable();
+
+	uh_set_counter(DEFAULT_KEEP_ALIVE_TIMER_INTERVAL, DEFAULT_KEEP_ALIVE_COUNTER_LIMIT);
+
+	uh_start_work(uh, DO_DETECT);
+
+	printk(KERN_ERR JZ_VH_PFX": Registered.\n");
+
+	return 0;
+
+err:
+	if (test_bit(3, &status)) {
+		uh_unregister_attr(pdev);
+	}
+
+	if (test_bit(2, &status)) {
+		uh->thread_state = UH_THREAD_STATE_START;
+		kthread_stop(uh->kthread);
+	}
+
+	if (test_bit(1, &status)) {
+		kfree(g_puh_data);
+	}
+
+	return rv;
+}
+
+static int musb_gadget_hotplug_setup(struct musb *musb)
+{
+	struct platform_device *pdev = (struct platform_device *)
+		container_of(musb->controller, struct platform_device, dev);
+	int rv;
+
+	rv = uh_setup(pdev, OTG_HOTPLUG_IRQ, OTG_HOTPLUG_PIN);
+	if (rv)
+		printk("uh_setup failed.\n");
+
+	return rv;
+}
+
+static void uh_cleanup(struct platform_device *pdev)
+{
+	struct uh_data *uh = g_puh_data;
+
+	uh_disable();
+
+        free_irq(uh->gpio_irq, uh);
+
+	/* Let our thread to exit. */
+	uh->thread_state = UH_THREAD_STATE_START;
+        kthread_stop(uh->kthread);
+
+	uh_unregister_attr(pdev);
+        kfree(uh);
+
+        return;
+}
+
