diasble host2.0 if no device connect, fix a pcd disconnect bug
authoryangkai <yk@rock-chips.com>
Wed, 20 Jun 2012 09:12:48 +0000 (17:12 +0800)
committeryangkai <yk@rock-chips.com>
Wed, 20 Jun 2012 09:12:48 +0000 (17:12 +0800)
drivers/usb/dwc_otg/dwc_otg_cil_intr.c
drivers/usb/dwc_otg/dwc_otg_hcd.c
drivers/usb/dwc_otg/dwc_otg_hcd.h
drivers/usb/dwc_otg/dwc_otg_hcd_intr.c
drivers/usb/dwc_otg/dwc_otg_pcd.c
drivers/usb/dwc_otg/dwc_otg_pcd_intr.c

index 9558706e89e17fb48b7705a15d28676686ede3ac..ec8b88875ce1a950d7fb259c43ad214dcb2ea21b 100755 (executable)
@@ -193,6 +193,7 @@ int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *_core_if)
                                        dctl.d32 = dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dctl );
                                        dctl.b.sftdiscon = 1;
                                        dwc_write_reg32( &_core_if->dev_if->dev_global_regs->dctl, dctl.d32 );
+                                       dwc_modify_reg32( &global_regs->gahbcfg, 0, 1); // disable usb global int
                         DWC_PRINT("********session end intr,soft disconnect************************\n");
                 }
                 _core_if->otg_dev->pcd->vbus_status = 0;
index 378f395f35f6044ba2d474df48a551190fd04c52..d42eb76e3bbd88a64020bceb2f129946d7ea3324 100755 (executable)
@@ -69,6 +69,8 @@ static int dwc_otg_hcd_suspend(struct usb_hcd *hcd)
        return 0;
     }
     hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0);
+    if((!dwc_otg_hcd->host_enabled)||(!hprt0.b.prtena))
+        return 0;
     DWC_PRINT("%s suspend, HPRT0:0x%x\n",hcd->self.bus_name,hprt0.d32);
     if(hprt0.b.prtconnsts)  // usb device connected
     {
@@ -126,6 +128,8 @@ static int dwc_otg_hcd_resume(struct usb_hcd *hcd)
        DWC_PRINT("%s, usb device mode\n", __func__);
        return 0;
     }
+    if(!dwc_otg_hcd->host_enabled)
+        return 0;
     #ifndef CONFIG_DWC_REMOTE_WAKEUP
     clk_enable(core_if->otg_dev->phyclk);
     clk_enable(core_if->otg_dev->ahbclk);
@@ -145,6 +149,8 @@ static int dwc_otg_hcd_resume(struct usb_hcd *hcd)
     dwc_write_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
         
     hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0);
+    if(!hprt0.b.prtena)
+        return 0;
     DWC_PRINT("%s resume, HPRT0:0x%x\n",hcd->self.bus_name,hprt0.d32);
     if(hprt0.b.prtconnsts)
     {
@@ -638,7 +644,81 @@ static struct tasklet_struct reset_tasklet = {
        .func = reset_tasklet_func,
        .data = 0,
 };
+#ifdef CONFIG_ARCH_RK30
+static void dwc_otg_hcd_enable(struct work_struct *work)
+{
+    dwc_otg_hcd_t *dwc_otg_hcd;
+    dwc_otg_core_if_t *_core_if;
 
+    dwc_otg_hcd = container_of(work, dwc_otg_hcd_t, host_enable_work.work);
+    _core_if = dwc_otg_hcd->core_if;
+    
+       if(dwc_otg_hcd->host_enabled == dwc_otg_hcd->host_setenable){
+//        DWC_PRINT("%s, enable flag %d\n", __func__, dwc_otg_hcd->host_setenable);
+           return;
+       }
+           
+       dwc_otg_hcd->host_enabled = dwc_otg_hcd->host_setenable;
+       if(dwc_otg_hcd->host_setenable == 0)    // enable -> disable
+       {
+           DWC_PRINT("disable host controller\n");
+           #if 1
+        if (_core_if->hcd_cb && _core_if->hcd_cb->disconnect) {
+                _core_if->hcd_cb->disconnect( _core_if->hcd_cb->p );
+        }
+        #endif
+        if (_core_if->hcd_cb && _core_if->hcd_cb->stop) {
+                _core_if->hcd_cb->stop( _core_if->hcd_cb->p );
+        }
+        if (_core_if->hcd_cb && _core_if->hcd_cb->suspend) {
+                _core_if->hcd_cb->suspend( _core_if->hcd_cb->p, 0);
+        }
+        udelay(3);
+//        clk_disable(otg_dev->phyclk);
+//        clk_disable(otg_dev->ahbclk);
+       }
+       else if(dwc_otg_hcd->host_setenable == 1)
+       {
+           DWC_PRINT("enable host controller\n");
+//        clk_enable(otg_dev->phyclk);
+//        clk_enable(otg_dev->ahbclk);
+        if (_core_if->hcd_cb && _core_if->hcd_cb->suspend) {
+                _core_if->hcd_cb->suspend( _core_if->hcd_cb->p, 1);
+        }
+        mdelay(5);
+        if (_core_if->hcd_cb && _core_if->hcd_cb->start) {
+                _core_if->hcd_cb->start( _core_if->hcd_cb->p );
+        }
+       }
+    
+}
+static void dwc_otg_hcd_connect_detect(unsigned long pdata)
+{
+    dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *)pdata;
+    dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+    unsigned int usbgrf_status = *(unsigned int*)(USBGRF_SOC_STATUS0);
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+//    DWC_PRINT("%s %p, grfstatus 0x%x\n", __func__, dwc_otg_hcd, usbgrf_status& (7<<22));
+    if(usbgrf_status & (7<<22)){
+    // usb device connected
+        dwc_otg_hcd->host_setenable = 1;
+    }
+    else{
+    // no device, suspend host
+        if((dwc_read_reg32(core_if->host_if->hprt0) & 1) == 0)
+            dwc_otg_hcd->host_setenable = 0;
+    
+    }
+    schedule_delayed_work(&dwc_otg_hcd->host_enable_work, jiffies);
+//    dwc_otg_hcd->connect_detect_timer.expires = jiffies + (HZ<<1); /* 1 s */
+    mod_timer(&dwc_otg_hcd->connect_detect_timer,jiffies + (HZ<<1)); 
+       local_irq_restore(flags);
+       return;
+}
+#endif
 /**
  * Initializes the HCD. This function allocates memory for and initializes the
  * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
@@ -1113,7 +1193,7 @@ int __devinit host20_hcd_init(struct device *dev)
        }
        
         /* Initialize the Connection timeout timer. */
-        init_timer( &dwc_otg_hcd->conn_timer );
+        //init_timer( &dwc_otg_hcd->conn_timer );
 
        /* Initialize reset tasklet. */
        host20_reset_tasklet.data = (unsigned long) dwc_otg_hcd;
@@ -1151,7 +1231,14 @@ int __devinit host20_hcd_init(struct device *dev)
                goto error3;
        }
     
-        
+#ifdef CONFIG_ARCH_RK30        
+    dwc_otg_hcd->connect_detect_timer.function = dwc_otg_hcd_connect_detect;
+    dwc_otg_hcd->connect_detect_timer.data = (unsigned long)(dwc_otg_hcd);
+    init_timer( &dwc_otg_hcd->connect_detect_timer);
+    mod_timer(&dwc_otg_hcd->connect_detect_timer, jiffies+(HZ<<3)); 
+    
+    INIT_DELAYED_WORK(&dwc_otg_hcd->host_enable_work, dwc_otg_hcd_enable);
+#endif        
        return 0;
 
        /* Error conditions */
@@ -1167,6 +1254,7 @@ int __devinit host20_hcd_init(struct device *dev)
 }
 #endif
 
+
 /**
  * Removes the HCD.
  * Frees memory and resources associated with the HCD and deregisters the bus.
index 43ed83c227c52fc82125be98dc8a7c89c85785f3..811db624374fb669c310eb63fead6ae5fd01b35f 100755 (executable)
@@ -379,6 +379,9 @@ typedef struct dwc_otg_hcd {
        /* Tasket to do a reset */
        struct tasklet_struct   *reset_tasklet;
 
+    struct timer_list  connect_detect_timer;
+    struct delayed_work        host_enable_work;
+    
        spinlock_t global_lock;
 
 #ifdef DEBUG
@@ -402,6 +405,7 @@ typedef struct dwc_otg_hcd {
 
     /** Flag to indicate whether host controller is enabled. */
     uint8_t host_enabled;
+    uint8_t host_setenable;
 
 } dwc_otg_hcd_t;
 
index ea4e1581188ffd68813ef68134f7d842f0f7a90b..fe383025c7c31f5e8ae26feff47f938e41b0f942 100755 (executable)
@@ -1830,7 +1830,7 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t *_hcd,
                handle_hc_ack_intr(_hcd, _hc, _hc_regs, _qtd);
        } else if(hcint.b.datatglerr){
             DWC_PRINT("%s, DATA toggle error, Channel %d\n",__func__, _hc->hc_num);
-             save_data_toggle(_hc, _hc_regs, _qtd);    //hzb,设备的第一个USB数据包的data toggle不符合USB协议
+             save_data_toggle(_hc, _hc_regs, _qtd);
              halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
                                clear_hc_int(_hc_regs,chhltd);
        } else {
index db676878088c32ea60d91021d8e35fb3d6be8b67..c89715f0e4dce123566d69c63e7f9cf1723e58df 100755 (executable)
@@ -329,8 +329,8 @@ static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep,
         /* yk@rk
          * ep0 -- tx fifo 0
          * ep1 -- tx fifo 1
-         * ep3 -- tx fifo 2
-         * ep5 -- tx fifo 3
+         * ep3 -- tx fifo 3
+         * ep5 -- tx fifo 2
          */
         if(ep->dwc_ep.num == 0)
                ep->dwc_ep.tx_fifo_num = 0;
@@ -338,6 +338,8 @@ static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep,
                 ep->dwc_ep.tx_fifo_num = 1;
         else if(ep->dwc_ep.num == 3)
                 ep->dwc_ep.tx_fifo_num = 3;
+        else if(ep->dwc_ep.num == 5)
+                ep->dwc_ep.tx_fifo_num = 2;
         else
            ep->dwc_ep.tx_fifo_num = (ep->dwc_ep.num>>1)+1 ; /* 1,3,5 */
 #endif
@@ -576,11 +578,9 @@ static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep,
        }
        
        ep = container_of(_ep, dwc_otg_pcd_ep_t, ep);
-       SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags);
        if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) 
        {
                DWC_WARN("%s, bad ep\n", __func__);
-                               SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags);
                return -EINVAL;
        }
        pcd = ep->pcd;
@@ -588,7 +588,6 @@ static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep,
        {
                DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed);
                DWC_WARN("%s, bogus device state\n", __func__);
-                               SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);
                return -ESHUTDOWN;
        }
 
@@ -625,6 +624,8 @@ static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep,
                                                : DMA_FROM_DEVICE);
                req->mapped = 0;
        }
+       SPIN_LOCK_IRQSAVE(&pcd->lock, flags);
+       
        _req->status = -EINPROGRESS;
        _req->actual = 0;
 
index ce6a44cdfe85b2a91527efb7447c7c9e8195414b..991ff028339f3fed9ace44e1c5bc9141fdb463fd 100755 (executable)
@@ -1053,6 +1053,10 @@ static inline void do_gadget_setup( dwc_otg_pcd_t *_pcd,
        {
                SPIN_UNLOCK(&_pcd->lock);
                ret = _pcd->driver->setup(&_pcd->gadget, _ctrl);
+               if(spin_is_locked(&_pcd->lock)){
+                   DWC_PRINT("%s warning: pcd->lock locked without unlock\n", __func__);
+                   SPIN_UNLOCK(&_pcd->lock);
+               }
                SPIN_LOCK(&_pcd->lock);
                if (ret < 0) 
                {
@@ -2161,6 +2165,10 @@ do { \
                        
                        /* Get EP pointer */       
                        ep = get_in_ep(_pcd, epnum);
+                       if(ep == NULL){
+                DWC_PRINT("%s epnum %d epintr %x\n", __func__, epnum, ep_intr);
+                break;
+                       }
                        dwc_ep = &ep->dwc_ep;
 
                        _diepctl = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl);