Merge tag 'v3.10.56' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / usb / host / xhci.c
index d8f640b12dd9d950e842892858a617b7fa97247e..c33a1f80c1c0a0d292bc152311dc1970610c30b7 100644 (file)
 
 #include "xhci.h"
 
+#ifdef CONFIG_MTK_XHCI
+#include <asm/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/xhci/xhci-mtk-scheduler.h>
+#include <linux/xhci/xhci-mtk-power.h>
+#include <linux/xhci/xhci-mtk.h>
+
+#ifdef CONFIG_USBIF_COMPLIANCE
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/kobject.h>
+#include <linux/miscdevice.h>
+
+static struct miscdevice mu3h_uevent_device = {
+         .minor = MISC_DYNAMIC_MINOR,
+         .name = "usbif_u3h_uevent",
+         .fops = NULL,
+};
+#endif
+#endif
+
 #define DRIVER_AUTHOR "Sarah Sharp"
 #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
 
@@ -38,6 +61,23 @@ static int link_quirk;
 module_param(link_quirk, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
 
+#ifdef CONFIG_USBIF_COMPLIANCE
+int usbif_u3h_send_event(char* event)
+{
+       char udev_event[128];
+       char *envp[] = {udev_event, NULL };
+       int ret ;
+
+       snprintf(udev_event, 128, "USBIF_EVENT=%s",event);
+       printk("usbif_u3h_send_event - sending event - %s in %s\n", udev_event, kobject_get_path(&mu3h_uevent_device.this_device->kobj, GFP_KERNEL));
+       ret = kobject_uevent_env(&mu3h_uevent_device.this_device->kobj, KOBJ_CHANGE, envp);
+       if (ret < 0)
+               printk("usbif_u3h_send_event sending failed with ret = %d, \n", ret);
+
+       return ret;
+}
+#endif
+
 /* TODO: copied from ehci-hcd.c - can this be refactored? */
 /*
  * xhci_handshake - spin reading hc until handshake completes or fails
@@ -140,6 +180,7 @@ static int xhci_start(struct xhci_hcd *xhci)
                                XHCI_MAX_HALT_USEC);
        if (!ret)
                xhci->xhc_state &= ~XHCI_STATE_HALTED;
+
        return ret;
 }
 
@@ -315,6 +356,9 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
        struct usb_hcd *hcd = xhci_to_hcd(xhci);
        struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
 
+       if (xhci->quirks & XHCI_PLAT)
+               return;
+
        xhci_free_irq(xhci);
 
        if (xhci->msix_entries) {
@@ -342,9 +386,14 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
 static int xhci_try_enable_msi(struct usb_hcd *hcd)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+       struct pci_dev  *pdev;
        int ret;
 
+       /* The xhci platform device has set up IRQs through usb_add_hcd. */
+       if (xhci->quirks & XHCI_PLAT)
+               return 0;
+
+       pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
        /*
         * Some Fresco Logic host controllers advertise MSI, but fail to
         * generate interrupts.  Don't even try to enable MSI.
@@ -386,16 +435,16 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
 
 #else
 
-static int xhci_try_enable_msi(struct usb_hcd *hcd)
+static inline int xhci_try_enable_msi(struct usb_hcd *hcd)
 {
        return 0;
 }
 
-static void xhci_cleanup_msix(struct xhci_hcd *xhci)
+static inline void xhci_cleanup_msix(struct xhci_hcd *xhci)
 {
 }
 
-static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+static inline void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
 {
 }
 
@@ -513,6 +562,7 @@ int xhci_init(struct usb_hcd *hcd)
        } else {
                xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n");
        }
+
        retval = xhci_mem_init(xhci, GFP_KERNEL);
        xhci_dbg(xhci, "Finished xhci_init\n");
 
@@ -585,6 +635,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
                xhci_halt(xhci);
                return -ENODEV;
        }
+
        xhci->shared_hcd->state = HC_STATE_RUNNING;
        xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
 
@@ -592,6 +643,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
                xhci_ring_cmd_db(xhci);
 
        xhci_dbg(xhci, "Finished xhci_run for USB3 roothub\n");
+
        return 0;
 }
 
@@ -738,10 +790,10 @@ void xhci_stop(struct usb_hcd *hcd)
                xhci_dbg(xhci, "%s: compliance mode recovery timer deleted\n",
                                __func__);
        }
-
+#ifndef CONFIG_MTK_XHCI
        if (xhci->quirks & XHCI_AMD_PLL_FIX)
                usb_amd_dev_put();
-
+#endif
        xhci_dbg(xhci, "// Disabling event ring interrupts\n");
        temp = xhci_readl(xhci, &xhci->op_regs->status);
        xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
@@ -952,7 +1004,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
  */
 int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 {
-       u32                     command, temp = 0;
+       u32                     command, temp = 0, status;
        struct usb_hcd          *hcd = xhci_to_hcd(xhci);
        struct usb_hcd          *secondary_hcd;
        int                     retval = 0;
@@ -1076,8 +1128,12 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 
  done:
        if (retval == 0) {
-               usb_hcd_resume_root_hub(hcd);
-               usb_hcd_resume_root_hub(xhci->shared_hcd);
+               /* Resume root hubs only when have pending events. */
+               status = readl(&xhci->op_regs->status);
+               if (status & STS_EINT) {
+                       usb_hcd_resume_root_hub(hcd);
+                       usb_hcd_resume_root_hub(xhci->shared_hcd);
+               }
        }
 
        /*
@@ -1171,9 +1227,6 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
        }
 
        xhci = hcd_to_xhci(hcd);
-       if (xhci->xhc_state & XHCI_STATE_HALTED)
-               return -ENODEV;
-
        if (check_virt_dev) {
                if (!udev->slot_id || !xhci->devs[udev->slot_id]) {
                        printk(KERN_DEBUG "xHCI %s called with unaddressed "
@@ -1189,6 +1242,9 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
                }
        }
 
+       if (xhci->xhc_state & XHCI_STATE_HALTED)
+               return -ENODEV;
+
        return 1;
 }
 
@@ -1578,6 +1634,11 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
        u32 drop_flag;
        u32 new_add_flags, new_drop_flags, new_slot_info;
        int ret;
+#ifdef CONFIG_MTK_XHCI
+       struct sch_ep *sch_ep = NULL;
+       int isTT;
+       int ep_type = 0;
+#endif
 
        ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
        if (ret <= 0)
@@ -1629,12 +1690,44 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
 
        xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
 
+#ifdef CONFIG_MTK_XHCI
+       slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[udev->slot_id]->out_ctx);
+       if((slot_ctx->tt_info & 0xff) > 0){
+               isTT = 1;
+       }
+       else{
+               isTT = 0;
+       }
+       if(usb_endpoint_xfer_int(&ep->desc)){
+               ep_type = USB_EP_INT;
+       }
+       else if(usb_endpoint_xfer_isoc(&ep->desc)){
+               ep_type = USB_EP_ISOC;
+       }
+       else if(usb_endpoint_xfer_bulk(&ep->desc)){
+               ep_type = USB_EP_BULK;
+       }
+       sch_ep = mtk_xhci_scheduler_remove_ep(udev->speed, usb_endpoint_dir_in(&ep->desc)
+               , isTT, ep_type, (mtk_u32 *)ep);
+       if(sch_ep != NULL){
+               kfree(sch_ep);
+       }
+       else{
+               xhci_warn(xhci, "[MTK]Doesn't find ep_sch instance when removing endpoint\n");
+       }
+#endif
+
        xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
                        (unsigned int) ep->desc.bEndpointAddress,
                        udev->slot_id,
                        (unsigned int) new_drop_flags,
                        (unsigned int) new_add_flags,
                        (unsigned int) new_slot_info);
+
+       #if defined(CONFIG_MTK_XHCI) && defined(CONFIG_USB_MTK_DUALMODE)
+       mtk_ep_count_dec();
+       #endif
+
        return 0;
 }
 
@@ -1664,6 +1757,16 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
        u32 new_add_flags, new_drop_flags, new_slot_info;
        struct xhci_virt_device *virt_dev;
        int ret = 0;
+#ifdef CONFIG_MTK_XHCI
+       struct xhci_ep_ctx *in_ep_ctx;
+       struct sch_ep *sch_ep;
+       int isTT;
+       int ep_type = 0;
+       int maxp = 0;
+       int burst = 0;
+       int mult = 0;
+       int interval = 0;
+#endif
 
        ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
        if (ret <= 0) {
@@ -1726,6 +1829,46 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
                return -ENOMEM;
        }
 
+#ifdef CONFIG_MTK_XHCI
+       in_ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
+       slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
+
+       if((slot_ctx->tt_info & 0xff) > 0){
+               isTT = 1;
+       }
+       else{
+               isTT = 0;
+       }
+       if(usb_endpoint_xfer_int(&ep->desc)){
+               ep_type = USB_EP_INT;
+       }
+       else if(usb_endpoint_xfer_isoc(&ep->desc)){
+               ep_type = USB_EP_ISOC;
+       }
+       else if(usb_endpoint_xfer_bulk(&ep->desc)){
+               ep_type = USB_EP_BULK;
+       }
+       if(udev->speed == USB_SPEED_FULL || udev->speed == USB_SPEED_HIGH
+               || udev->speed == USB_SPEED_LOW){
+               maxp = ep->desc.wMaxPacketSize & 0x7FF;
+               burst = ep->desc.wMaxPacketSize >> 11;
+               mult = 0;
+       }
+       else if(udev->speed == USB_SPEED_SUPER){
+               maxp = ep->desc.wMaxPacketSize & 0x7FF;
+               burst = ep->ss_ep_comp.bMaxBurst;
+               mult = ep->ss_ep_comp.bmAttributes & 0x3;
+       }
+       interval = (1 << ((in_ep_ctx->ep_info >> 16) & 0xff));
+       sch_ep = kmalloc(sizeof(struct sch_ep), GFP_KERNEL);
+       if(mtk_xhci_scheduler_add_ep(udev->speed, usb_endpoint_dir_in(&ep->desc),
+               isTT, ep_type, maxp, interval, burst, mult, (mtk_u32 *)ep
+               , (mtk_u32 *)in_ep_ctx, sch_ep) != SCH_SUCCESS){
+               xhci_err(xhci, "[MTK] not enough bandwidth\n");
+               return -ENOSPC;
+       }
+#endif
+
        ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
        new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
 
@@ -1755,6 +1898,11 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
                        (unsigned int) new_drop_flags,
                        (unsigned int) new_add_flags,
                        (unsigned int) new_slot_info);
+
+       #if defined(CONFIG_MTK_XHCI) && defined(CONFIG_USB_MTK_DUALMODE)
+       mtk_ep_count_inc();
+       #endif
+
        return 0;
 }
 
@@ -2587,15 +2735,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        if (command) {
                cmd_completion = command->completion;
                cmd_status = &command->status;
-               command->command_trb = xhci->cmd_ring->enqueue;
-
-               /* Enqueue pointer can be left pointing to the link TRB,
-                * we must handle that
-                */
-               if (TRB_TYPE_LINK_LE32(command->command_trb->link.control))
-                       command->command_trb =
-                               xhci->cmd_ring->enq_seg->next->trbs;
-
+               command->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
                list_add_tail(&command->cmd_list, &virt_dev->cmd_list);
        } else {
                cmd_completion = &virt_dev->cmd_completion;
@@ -2603,7 +2743,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        }
        init_completion(cmd_completion);
 
-       cmd_trb = xhci->cmd_ring->dequeue;
+       cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        if (!ctx_change)
                ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
                                udev->slot_id, must_succeed);
@@ -3388,14 +3528,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
 
        /* Attempt to submit the Reset Device command to the command ring */
        spin_lock_irqsave(&xhci->lock, flags);
-       reset_device_cmd->command_trb = xhci->cmd_ring->enqueue;
-
-       /* Enqueue pointer can be left pointing to the link TRB,
-        * we must handle that
-        */
-       if (TRB_TYPE_LINK_LE32(reset_device_cmd->command_trb->link.control))
-               reset_device_cmd->command_trb =
-                       xhci->cmd_ring->enq_seg->next->trbs;
+       reset_device_cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
 
        list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list);
        ret = xhci_queue_reset_device(xhci, slot_id);
@@ -3506,10 +3639,23 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        struct xhci_virt_device *virt_dev;
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+       struct device *dev = hcd->self.controller;
+#endif
        unsigned long flags;
        u32 state;
        int i, ret;
 
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+       /*
+        * We called pm_runtime_get_noresume when the device was attached.
+        * Decrement the counter here to allow controller to runtime suspend
+        * if no devices remain.
+        */
+       if (xhci->quirks & XHCI_RESET_ON_RESUME)
+               pm_runtime_put_noidle(dev);
+#endif
+
        ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
        /* If the host is halted due to driver unload, we still need to free the
         * device.
@@ -3581,13 +3727,14 @@ static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci)
 int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct device *dev = hcd->self.controller;
        unsigned long flags;
        int timeleft;
        int ret;
        union xhci_trb *cmd_trb;
 
        spin_lock_irqsave(&xhci->lock, flags);
-       cmd_trb = xhci->cmd_ring->dequeue;
+       cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
        if (ret) {
                spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3633,6 +3780,16 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
                goto disable_slot;
        }
        udev->slot_id = xhci->slot_id;
+
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+       /*
+        * If resetting upon resume, we can't put the controller into runtime
+        * suspend if there is a device attached.
+        */
+       if (xhci->quirks & XHCI_RESET_ON_RESUME)
+               pm_runtime_get_noresume(dev);
+#endif
+
        /* Is this a LS or FS device under a HS hub? */
        /* Hub or peripherial? */
        return 1;
@@ -3704,7 +3861,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
        xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
 
        spin_lock_irqsave(&xhci->lock, flags);
-       cmd_trb = xhci->cmd_ring->dequeue;
+       cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
                                        udev->slot_id);
        if (ret) {
@@ -4388,13 +4545,21 @@ static int xhci_change_max_exit_latency(struct xhci_hcd *xhci,
        int ret;
 
        spin_lock_irqsave(&xhci->lock, flags);
-       if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) {
+
+       virt_dev = xhci->devs[udev->slot_id];
+
+       /*
+        * virt_dev might not exists yet if xHC resumed from hibernate (S4) and
+        * xHC was re-initialized. Exit latency will be set later after
+        * hub_port_finish_reset() is done and xhci->devs[] are re-allocated
+        */
+
+       if (!virt_dev || max_exit_latency == virt_dev->current_mel) {
                spin_unlock_irqrestore(&xhci->lock, flags);
                return 0;
        }
 
        /* Attempt to issue an Evaluate Context command to change the MEL. */
-       virt_dev = xhci->devs[udev->slot_id];
        command = xhci->lpm_command;
        xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx);
        spin_unlock_irqrestore(&xhci->lock, flags);
@@ -4681,6 +4846,12 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
                return 0;
        }
 
+#ifdef CONFIG_MTK_XHCI
+       retval = mtk_xhci_ip_init(hcd, xhci);
+       if(retval)
+               goto error;
+#endif
+
        xhci->cap_regs = hcd->regs;
        xhci->op_regs = hcd->regs +
                HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
@@ -4697,6 +4868,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 
        get_quirks(dev, xhci);
 
+       /* In xhci controllers which follow xhci 1.0 spec gives a spurious
+        * success event after a short transfer. This quirk will ignore such
+        * spurious event.
+        */
+       if (xhci->hci_version > 0x96)
+               xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
+
        /* Make sure the HC is halted. */
        retval = xhci_halt(xhci);
        if (retval)
@@ -4723,6 +4901,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
        if (retval)
                goto error;
        xhci_dbg(xhci, "Called HCD init\n");
+
+    printk("%s(%d): do mtk_xhci_set\n", __func__, __LINE__);
+
        return 0;
 error:
        kfree(xhci);
@@ -4733,6 +4914,189 @@ MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_LICENSE("GPL");
 
+#ifdef CONFIG_USBIF_COMPLIANCE
+#ifndef CONFIG_USB_MTK_DUALMODE
+static int xhci_hcd_driver_init(void)
+{
+       int retval;
+
+       retval = xhci_register_pci();
+       if (retval < 0) {
+               printk(KERN_DEBUG "Problem registering PCI driver.");
+               return retval;
+       }
+
+    #ifdef CONFIG_MTK_XHCI
+    mtk_xhci_ip_init();
+    #endif
+
+       retval = xhci_register_plat();
+       if (retval < 0) {
+               printk(KERN_DEBUG "Problem registering platform driver.");
+               goto unreg_pci;
+       }
+
+    #ifdef CONFIG_MTK_XHCI
+    retval = xhci_attrs_init();
+    if(retval < 0){
+        printk(KERN_DEBUG "Problem creating xhci attributes.");
+        goto unreg_plat;
+    }
+
+    mtk_xhci_wakelock_init();
+    #endif
+
+       /*
+        * Check the compiler generated sizes of structures that must be laid
+        * out in specific ways for hardware access.
+        */
+       BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_slot_ctx) != 8*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_ep_ctx) != 8*32/8);
+       /* xhci_device_control has eight fields, and also
+        * embeds one xhci_slot_ctx and 31 xhci_ep_ctx
+        */
+       BUILD_BUG_ON(sizeof(struct xhci_stream_ctx) != 4*32/8);
+       BUILD_BUG_ON(sizeof(union xhci_trb) != 4*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 7*32/8);
+       BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
+       /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
+       BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+       return 0;
+
+#ifdef CONFIG_MTK_XHCI
+unreg_plat:
+    xhci_unregister_plat();
+#endif
+unreg_pci:
+       xhci_unregister_pci();
+       return retval;
+}
+
+static void xhci_hcd_driver_cleanup(void)
+{
+       xhci_unregister_pci();
+       xhci_unregister_plat();
+    xhci_attrs_exit();
+}
+#else
+static int xhci_hcd_driver_init(void)
+{
+       // init in mt_devs.c
+       mtk_xhci_eint_iddig_init();
+       mtk_xhci_switch_init();
+       //mtk_xhci_wakelock_init();
+       return 0;
+}
+
+static void xhci_hcd_driver_cleanup(void)
+{
+       mtk_xhci_eint_iddig_deinit() ;
+}
+
+#endif
+
+static int mu3h_normal_driver_on = 0 ;
+
+static int xhci_mu3h_proc_show(struct seq_file *seq, void *v)
+{
+        seq_printf(seq, "xhci_mu3h_proc_show, mu3h is %d (on:1, off:0)\n", mu3h_normal_driver_on);
+        return 0;
+}
+
+static int xhci_mu3h_proc_open(struct inode *inode, struct file *file)
+{
+    return single_open(file, xhci_mu3h_proc_show, inode->i_private);
+}
+
+static ssize_t xhci_mu3h_proc_write(struct file *file, const char __user *buf, size_t length, loff_t *ppos)
+{
+       int ret ;
+       char msg[32] ;
+       int result;
+
+       if (length >= sizeof(msg)) {
+               printk( "xhci_mu3h_proc_write length error, the error len is %d\n", (unsigned int)length);
+               return -EINVAL;
+       }
+       if (copy_from_user(msg, buf, length))
+               return -EFAULT;
+
+       msg[length] = 0 ;
+
+       printk("xhci_mu3h_proc_write: %s, current driver on/off: %d\n", msg, mu3h_normal_driver_on);
+
+       if ((msg[0] == '1') && (mu3h_normal_driver_on == 0)){
+               xhci_hcd_driver_init() ;
+               mu3h_normal_driver_on = 1 ;
+               printk("registe mu3h driver : m3h xhci driver\n");
+       }else if ((msg[0] == '0') && (mu3h_normal_driver_on == 1)){
+               xhci_hcd_driver_cleanup();
+               mu3h_normal_driver_on = 0 ;
+               printk("unregiste m3h xhci driver.\n");
+       }else{
+               printk("xhci_mu3h_proc_write write faile !\n");
+       }
+       return length;
+}
+
+static const struct file_operations mu3h_proc_fops = {
+       .owner = THIS_MODULE,
+       .open = xhci_mu3h_proc_open,
+       .write = xhci_mu3h_proc_write,
+       .read = seq_read,
+       .llseek = seq_lseek,
+
+};
+
+static int __init xhci_hcd_init(void)
+{
+       struct proc_dir_entry *prEntry;
+
+       printk(KERN_DEBUG "xhci_hcd_init");
+
+       // set xhci up at boot up
+       xhci_hcd_driver_init() ;
+       mtk_xhci_wakelock_init();
+       mu3h_normal_driver_on = 1;
+
+       // USBIF
+       prEntry = proc_create("mu3h_driver_init", 0666, NULL, &mu3h_proc_fops);
+       if (prEntry)
+       {
+               printk("create the mu3h init proc OK!\n") ;
+       }else{
+               printk("[ERROR] create the mu3h init proc FAIL\n") ;
+       }
+
+#ifdef CONFIG_MTK_XHCI
+
+       if (!misc_register(&mu3h_uevent_device)){
+               printk("create the mu3h_uevent_device uevent device OK!\n") ;
+
+       }else{
+               printk("[ERROR] create the mu3h_uevent_device uevent device fail\n") ;
+       }
+
+#endif
+
+       return 0 ;
+
+}
+module_init(xhci_hcd_init);
+
+static void __exit xhci_hcd_cleanup(void)
+{
+#ifdef CONFIG_MTK_XHCI
+       misc_deregister(&mu3h_uevent_device);
+#endif
+       printk(KERN_DEBUG "xhci_hcd_cleanup");
+}
+module_exit(xhci_hcd_cleanup);
+
+#else
+#ifndef CONFIG_USB_MTK_DUALMODE
 static int __init xhci_hcd_init(void)
 {
        int retval;
@@ -4747,6 +5111,17 @@ static int __init xhci_hcd_init(void)
                printk(KERN_DEBUG "Problem registering platform driver.");
                goto unreg_pci;
        }
+
+    #ifdef CONFIG_MTK_XHCI
+    retval = xhci_attrs_init();
+    if(retval < 0){
+        printk(KERN_DEBUG "Problem creating xhci attributes.");
+        goto unreg_plat;
+    }
+
+    mtk_xhci_wakelock_init();
+    #endif
+
        /*
         * Check the compiler generated sizes of structures that must be laid
         * out in specific ways for hardware access.
@@ -4765,6 +5140,11 @@ static int __init xhci_hcd_init(void)
        /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
        BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
        return 0;
+
+#ifdef CONFIG_MTK_XHCI
+unreg_plat:
+    xhci_unregister_plat();
+#endif
 unreg_pci:
        xhci_unregister_pci();
        return retval;
@@ -4775,5 +5155,23 @@ static void __exit xhci_hcd_cleanup(void)
 {
        xhci_unregister_pci();
        xhci_unregister_plat();
+    xhci_attrs_exit();
+}
+module_exit(xhci_hcd_cleanup);
+#else
+static int __init xhci_hcd_init(void)
+{
+    mtk_xhci_eint_iddig_init();
+    mtk_xhci_switch_init();
+    mtk_xhci_wakelock_init();
+       return 0;
+}
+module_init(xhci_hcd_init);
+
+static void __exit xhci_hcd_cleanup(void)
+{
 }
 module_exit(xhci_hcd_cleanup);
+
+#endif
+#endif