Signed-off-by: Tim Harvey <thar...@gateworks.com>
---
 .../cns3xxx/patches-3.8/400-dwc_host_fixups.patch  | 1182 ++++++++++++++++++++
 1 file changed, 1182 insertions(+)
 create mode 100644 target/linux/cns3xxx/patches-3.8/400-dwc_host_fixups.patch

diff --git a/target/linux/cns3xxx/patches-3.8/400-dwc_host_fixups.patch 
b/target/linux/cns3xxx/patches-3.8/400-dwc_host_fixups.patch
new file mode 100644
index 0000000..4b2105e
--- /dev/null
+++ b/target/linux/cns3xxx/patches-3.8/400-dwc_host_fixups.patch
@@ -0,0 +1,1182 @@
+--- a/drivers/usb/dwc/otg_attr.c
++++ b/drivers/usb/dwc/otg_attr.c
+@@ -721,9 +721,10 @@ static ssize_t hcddump_show( struct devi
+                            char *buf)
+ {
+ #ifndef DWC_DEVICE_ONLY
+-      struct platform_device *pdev = container_of(_dev, struct 
platform_device, dev); \
+-      dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev);         \
+-      dwc_otg_hcd_dump_state(otg_dev->hcd);
++      struct platform_device *pdev = container_of(_dev, struct 
platform_device, dev);
++      struct usb_hcd *hcd = platform_get_drvdata(pdev);
++      dwc_otg_hcd_t *otg_dev = hcd_to_dwc_otg_hcd(hcd);
++      dwc_otg_hcd_dump_state(otg_dev);
+ #endif
+       return sprintf( buf, "HCD Dump\n" );
+ }
+--- a/drivers/usb/dwc/otg_cil.c
++++ b/drivers/usb/dwc/otg_cil.c
+@@ -66,7 +66,7 @@
+ #include "otg_regs.h"
+ #include "otg_cil.h"
+ #include "otg_pcd.h"
+-
++#include "otg_hcd.h"
+ 
+ /**
+  * This function is called to initialize the DWC_otg CSR data
+@@ -1156,12 +1156,13 @@ void dwc_otg_core_host_init(dwc_otg_core
+               DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i);
+               do {
+                       hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+-                      if (++count > 1000)
++                      if (++count > 200)
+                       {
+                               DWC_ERROR("%s: Unable to clear halt on channel 
%d\n",
+                                         __func__, i);
+                               break;
+                       }
++                      udelay(100);
+               }
+               while (hcchar.b.chen);
+       }
+@@ -1211,6 +1212,8 @@ void dwc_otg_hc_init(dwc_otg_core_if_t *
+       hc_intr_mask.b.chhltd = 1;
+       if (core_if->dma_enable) {
+               hc_intr_mask.b.ahberr = 1;
++              /* Always record the first nak interrupt for bulk
++               * packets. */
+               if (hc->error_state && !hc->do_split &&
+                       hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+                       hc_intr_mask.b.ack = 1;
+@@ -1375,7 +1378,7 @@ void dwc_otg_hc_init(dwc_otg_core_if_t *
+  * @param hc Host channel to halt.
+  * @param halt_status Reason for halting the channel.
+  */
+-void dwc_otg_hc_halt(dwc_otg_core_if_t *core_if,
++void dwc_otg_hc_halt(dwc_otg_hcd_t *hcd,
+                        dwc_hc_t *hc,
+                        dwc_otg_halt_status_e halt_status)
+ {
+@@ -1385,6 +1388,7 @@ void dwc_otg_hc_halt(dwc_otg_core_if_t *
+       dwc_otg_hc_regs_t               *hc_regs;
+       dwc_otg_core_global_regs_t      *global_regs;
+       dwc_otg_host_global_regs_t      *host_global_regs;
++      dwc_otg_core_if_t *core_if = hcd->core_if;
+ 
+       hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+       global_regs = core_if->core_global_regs;
+@@ -1477,6 +1481,9 @@ void dwc_otg_hc_halt(dwc_otg_core_if_t *
+ 
+       hc->halt_status = halt_status;
+ 
++      if (!hc->halt_on_queue && !hc->halt_pending && hc->qh->nak_frame != 
0xffff)
++              hcd->nakking_channels--;
++
+       if (hcchar.b.chen) {
+               hc->halt_pending = 1;
+               hc->halt_on_queue = 0;
+@@ -1744,9 +1751,9 @@ void dwc_otg_hc_start_transfer(dwc_otg_c
+       dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32);
+ 
+       DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+-      DWC_DEBUGPL(DBG_HCDV, "  Xfer Size: %d\n", hctsiz.b.xfersize);
+-      DWC_DEBUGPL(DBG_HCDV, "  Num Pkts: %d\n", hctsiz.b.pktcnt);
+-      DWC_DEBUGPL(DBG_HCDV, "  Start PID: %d\n", hctsiz.b.pid);
++      DWC_DEBUGPL(DBG_HCDV, "  Xfer Size: %d\n", hctsiz.b.xfersize);
++      DWC_DEBUGPL(DBG_HCDV, "  Num Pkts: %d\n", hctsiz.b.pktcnt);
++      DWC_DEBUGPL(DBG_HCDV, "  Start PID: %d\n", hctsiz.b.pid);
+ 
+       if (core_if->dma_enable) {
+               dwc_write_reg32(&hc_regs->hcdma, (uint32_t)hc->xfer_buff);
+@@ -1774,6 +1781,10 @@ void dwc_otg_hc_start_transfer(dwc_otg_c
+       /* Set host channel enable after all other setup is complete. */
+       hcchar.b.chen = 1;
+       hcchar.b.chdis = 0;
++
++      /* Memory Barrier before enabling channel ensure the channel is setup 
correct */
++      mb();
++
+       dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+ 
+       hc->xfer_started = 1;
+@@ -1786,7 +1797,7 @@ void dwc_otg_hc_start_transfer(dwc_otg_c
+       }
+ 
+ #ifdef DEBUG
+-      /* Start a timer for this transfer. */
++      /* Start a timer for this transfer */
+       core_if->hc_xfer_timer[hc->hc_num].function = hc_xfer_timeout;
+       core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
+       core_if->hc_xfer_info[hc->hc_num].hc = hc;
+@@ -1844,6 +1855,10 @@ int dwc_otg_hc_continue_transfer(dwc_otg
+               hcchar.b.chen = 1;
+               hcchar.b.chdis = 0;
+               DWC_DEBUGPL(DBG_HCDV, "  IN xfer: hcchar = 0x%08x\n", 
hcchar.d32);
++
++              /* Memory Barrier before enabling channel ensure the channel is 
setup correct */
++              mb();
++
+               dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+               hc->requests++;
+               return 1;
+@@ -1891,6 +1906,10 @@ void dwc_otg_hc_do_ping(dwc_otg_core_if_
+       hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+       hcchar.b.chen = 1;
+       hcchar.b.chdis = 0;
++
++      /* Memory Barrier before enabling channel ensure the channel is setup 
correct */
++      mb();
++
+       dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+ }
+ 
+@@ -2089,9 +2108,10 @@ void dwc_otg_ep_activate(dwc_otg_core_if
+                       if(core_if->dma_desc_enable) {
+                               diepmsk.b.bna = 1;
+                       }
++
+ /*
+                       if(core_if->dma_enable) {
+-                              doepmsk.b.nak = 1;
++                              diepmsk.b.nak = 1;
+                       }
+ */
+                       
dwc_write_reg32(&dev_if->dev_global_regs->diepeachintmsk[ep->num], diepmsk.d32);
+@@ -3567,6 +3587,7 @@ void dwc_otg_flush_tx_fifo(dwc_otg_core_
+                       dwc_read_reg32(&global_regs->gnptxsts));
+                       break;
+               }
++              udelay(1);
+       }
+       while (greset.b.txfflsh == 1);
+ 
+@@ -3599,6 +3620,7 @@ void dwc_otg_flush_rx_fifo(dwc_otg_core_
+                               greset.d32);
+                       break;
+               }
++              udelay(1);
+       }
+       while (greset.b.rxfflsh == 1);
+ 
+@@ -3640,6 +3662,7 @@ void dwc_otg_core_reset(dwc_otg_core_if_
+                               greset.d32);
+                       break;
+               }
++              udelay(1);
+       }
+       while (greset.b.csftrst == 1);
+ 
+--- a/drivers/usb/dwc/otg_cil.h
++++ b/drivers/usb/dwc/otg_cil.h
+@@ -45,6 +45,8 @@
+ #include "linux/timer.h"
+ #endif
+ 
++struct dwc_otg_hcd;
++
+ /**
+  * @file
+  * This file contains the interface to the Core Interface Layer.
+@@ -402,7 +404,7 @@ typedef struct dwc_otg_core_params
+        */
+       int32_t dma_burst_size;  /* Translate this to GAHBCFG values */
+ //#define dwc_param_dma_burst_size_default 32
+-#define dwc_param_dma_burst_size_default 1
++#define dwc_param_dma_burst_size_default 32
+ 
+       /**
+        * Specifies the maximum speed of operation in host and device mode.
+@@ -876,7 +878,7 @@ extern void dwc_otg_iso_ep_start_buf_tra
+  */
+ /**@{*/
+ extern void dwc_otg_hc_init(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+-extern void dwc_otg_hc_halt(dwc_otg_core_if_t *_core_if,
++extern void dwc_otg_hc_halt(struct dwc_otg_hcd *_hcd,
+                               dwc_hc_t *_hc,
+                               dwc_otg_halt_status_e _halt_status);
+ extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+--- a/drivers/usb/dwc/otg_hcd.c
++++ b/drivers/usb/dwc/otg_hcd.c
+@@ -176,8 +176,10 @@ static void kill_urbs_in_qh_list(dwc_otg
+                    qtd_item = qh->qtd_list.next) {
+                       qtd = list_entry(qtd_item, dwc_otg_qtd_t, 
qtd_list_entry);
+                       if (qtd->urb != NULL) {
++                              SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
+                               dwc_otg_hcd_complete_urb(hcd, qtd->urb,
+                                                        -ETIMEDOUT);
++                              SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
+                       }
+                       dwc_otg_hcd_qtd_remove_and_free(hcd, qtd);
+               }
+@@ -589,6 +591,7 @@ static void hcd_reinit(dwc_otg_hcd_t *hc
+       hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
+       hcd->non_periodic_channels = 0;
+       hcd->periodic_channels = 0;
++      hcd->nakking_channels = 0;
+ 
+       /*
+        * Put all channels in the free channel list and clean up channel
+@@ -853,10 +856,10 @@ static void dump_channel_info(dwc_otg_hc
+ //OTG host require the DMA addr is DWORD-aligned,
+ //patch it if the buffer is not DWORD-aligned
+ inline
+-void hcd_check_and_patch_dma_addr(struct urb *urb){
++int hcd_check_and_patch_dma_addr(struct urb *urb){
+ 
+       
if((!urb->transfer_buffer)||!urb->transfer_dma||urb->transfer_dma==0xffffffff)
+-              return;
++              return 0;
+ 
+       if(((u32)urb->transfer_buffer)& 0x3){
+               /*
+@@ -881,11 +884,12 @@ void hcd_check_and_patch_dma_addr(struct
+                               kfree(urb->aligned_transfer_buffer);
+                       }
+                       
urb->aligned_transfer_buffer=kmalloc(urb->aligned_transfer_buffer_length,GFP_KERNEL|GFP_DMA|GFP_ATOMIC);
+-                      urb->aligned_transfer_dma=dma_map_single(NULL,(void 
*)(urb->aligned_transfer_buffer),(urb->aligned_transfer_buffer_length),DMA_FROM_DEVICE);
+                       if(!urb->aligned_transfer_buffer){
+                               DWC_ERROR("Cannot alloc required buffer!!\n");
+-                              BUG();
++                              //BUG();
++                              return -1;
+                       }
++                      urb->aligned_transfer_dma=dma_map_single(NULL,(void 
*)(urb->aligned_transfer_buffer),(urb->aligned_transfer_buffer_length),DMA_FROM_DEVICE);
+                       //printk(" new allocated aligned_buf=%.8x 
aligned_buf_len=%d\n", (u32)urb->aligned_transfer_buffer, 
urb->aligned_transfer_buffer_length);
+               }
+               urb->transfer_dma=urb->aligned_transfer_dma;
+@@ -894,6 +898,7 @@ void hcd_check_and_patch_dma_addr(struct
+                       
dma_sync_single_for_device(NULL,urb->transfer_dma,urb->transfer_buffer_length,DMA_TO_DEVICE);
+               }
+       }
++      return 0;
+ }
+ 
+ 
+@@ -910,7 +915,15 @@ int dwc_otg_hcd_urb_enqueue(struct usb_h
+       int retval = 0;
+       dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+       dwc_otg_qtd_t *qtd;
++      unsigned long flags;
++
++      SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, flags);
+ 
++      if (urb->hcpriv != NULL) {
++              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
++              return -ENOMEM;
++
++      }
+ #ifdef DEBUG
+       if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+               dump_urb_info(urb, "dwc_otg_hcd_urb_enqueue");
+@@ -918,13 +931,19 @@ int dwc_otg_hcd_urb_enqueue(struct usb_h
+ #endif
+       if (!dwc_otg_hcd->flags.b.port_connect_status) {
+               /* No longer connected. */
++              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
+               return -ENODEV;
+       }
+ 
+-      hcd_check_and_patch_dma_addr(urb);
++      if (hcd_check_and_patch_dma_addr(urb)) {
++              DWC_ERROR("Unable to check and patch dma addr\n");
++              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
++              return -ENOMEM;
++      }
+       qtd = dwc_otg_hcd_qtd_create(urb);
+       if (qtd == NULL) {
+               DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n");
++              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
+               return -ENOMEM;
+       }
+ 
+@@ -934,7 +953,7 @@ int dwc_otg_hcd_urb_enqueue(struct usb_h
+                         "Error status %d\n", retval);
+               dwc_otg_hcd_qtd_free(qtd);
+       }
+-
++      SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
+       return retval;
+ }
+ 
+@@ -948,6 +967,7 @@ int dwc_otg_hcd_urb_dequeue(struct usb_h
+       dwc_otg_qtd_t *urb_qtd;
+       dwc_otg_qh_t *qh;
+       struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
++      int rc;
+ 
+       DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n");
+ 
+@@ -958,10 +978,6 @@ int dwc_otg_hcd_urb_dequeue(struct usb_h
+       urb_qtd = (dwc_otg_qtd_t *)urb->hcpriv;
+       qh = (dwc_otg_qh_t *)ep->hcpriv;
+ 
+-      if (urb_qtd == NULL) {
+-              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
+-              return 0;
+-      }
+ #ifdef DEBUG
+       if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+               dump_urb_info(urb, "dwc_otg_hcd_urb_dequeue");
+@@ -971,7 +987,7 @@ int dwc_otg_hcd_urb_dequeue(struct usb_h
+       }
+ #endif
+ 
+-      if (urb_qtd == qh->qtd_in_process) {
++      if (qh && urb_qtd == qh->qtd_in_process) {
+               /* The QTD is in process (it has been assigned to a channel). */
+ 
+               if (dwc_otg_hcd->flags.b.port_connect_status) {
+@@ -982,7 +998,7 @@ int dwc_otg_hcd_urb_dequeue(struct usb_h
+                        * written to halt the channel since the core is in
+                        * device mode.
+                        */
+-                      dwc_otg_hc_halt(dwc_otg_hcd->core_if, qh->channel,
++                      dwc_otg_hc_halt(dwc_otg_hcd, qh->channel,
+                                       DWC_OTG_HC_XFER_URB_DEQUEUE);
+               }
+       }
+@@ -992,22 +1008,28 @@ int dwc_otg_hcd_urb_dequeue(struct usb_h
+        * schedule if it has any remaining QTDs.
+        */
+       dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd, urb_qtd);
+-      if (urb_qtd == qh->qtd_in_process) {
+-              /* Note that dwc_otg_hcd_qh_deactivate() locks the spin_lock 
again */
+-              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
++      if (qh && urb_qtd == qh->qtd_in_process) {
+               dwc_otg_hcd_qh_deactivate(dwc_otg_hcd, qh, 0);
+               qh->channel = NULL;
+               qh->qtd_in_process = NULL;
+       } else {
+-              if (list_empty(&qh->qtd_list))
++              if (qh && list_empty(&qh->qtd_list)) {
+                       dwc_otg_hcd_qh_remove(dwc_otg_hcd, qh);
+-              SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
++              }
+       }
+ 
++
++      rc = usb_hcd_check_unlink_urb(hcd, urb, status);
++
++      if (!rc) {
++              usb_hcd_unlink_urb_from_ep(hcd, urb);
++      }
+       urb->hcpriv = NULL;
++      SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
+ 
+-      /* Higher layer software sets URB status. */
+-      usb_hcd_giveback_urb(hcd, urb, status);
++      if (!rc) {
++              usb_hcd_giveback_urb(hcd, urb, status);
++      }
+       if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+               DWC_PRINT("Called usb_hcd_giveback_urb()\n");
+               DWC_PRINT("  urb->status = %d\n", urb->status);
+@@ -2035,15 +2057,19 @@ static void assign_and_init_hc(dwc_otg_h
+       dwc_otg_qtd_t   *qtd;
+       struct urb      *urb;
+ 
+-      DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, hcd, qh);
+-
++      DWC_DEBUGPL(DBG_HCD_FLOOD, "%s(%p,%p)\n", __func__, hcd, qh);
+       hc = list_entry(hcd->free_hc_list.next, dwc_hc_t, hc_list_entry);
+ 
++      qtd = list_entry(qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry);
++      urb = qtd->urb;
++
++      if (!urb){
++              return;
++      }
++
+       /* Remove the host channel from the free list. */
+       list_del_init(&hc->hc_list_entry);
+ 
+-      qtd = list_entry(qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry);
+-      urb = qtd->urb;
+       qh->channel = hc;
+       qh->qtd_in_process = qtd;
+ 
+@@ -2202,16 +2228,24 @@ static void assign_and_init_hc(dwc_otg_h
+ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *hcd)
+ {
+       struct list_head                *qh_ptr;
+-      dwc_otg_qh_t                    *qh;
++      dwc_otg_qh_t                    *qh = NULL;
+       int                             num_channels;
+       dwc_otg_transaction_type_e      ret_val = DWC_OTG_TRANSACTION_NONE;
++      uint16_t      cur_frame = 
dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
++      unsigned long flags;
++      int include_nakd, channels_full;
++      /* This condition has once been observed, but the cause was
++       * never determined. Check for it here, to collect debug data if
++       * it occurs again. */
++      WARN_ON_ONCE(hcd->non_periodic_channels < 0);
++      check_nakking(hcd, __FUNCTION__,  "start");
+ 
+ #ifdef DEBUG_SOF
+       DWC_DEBUGPL(DBG_HCD, "  Select Transactions\n");
+ #endif
+ 
+-      spin_lock(&hcd->lock);
+-      /* Process entries in the periodic ready list. */
++      SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
++              /* Process entries in the periodic ready list. */
+       qh_ptr = hcd->periodic_sched_ready.next;
+       while (qh_ptr != &hcd->periodic_sched_ready &&
+              !list_empty(&hcd->free_hc_list)) {
+@@ -2234,37 +2268,140 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
+        * schedule. Some free host channels may not be used if they are
+        * reserved for periodic transfers.
+        */
+-      qh_ptr = hcd->non_periodic_sched_inactive.next;
+       num_channels = hcd->core_if->core_params->host_channels;
+-      while (qh_ptr != &hcd->non_periodic_sched_inactive &&
+-             (hcd->non_periodic_channels <
+-              num_channels - hcd->periodic_channels) &&
+-             !list_empty(&hcd->free_hc_list)) {
+ 
+-              qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+-              assign_and_init_hc(hcd, qh);
++      /* Go over the queue twice: Once while not including nak'd
++       * entries, one while including them. This is so a retransmit of
++       * an entry that has received a nak is scheduled only after all
++       * new entries.
++       */
++      channels_full = 0;
++      for (include_nakd = 0; include_nakd < 2 && !channels_full; 
++include_nakd) {
++          qh_ptr = hcd->non_periodic_sched_inactive.next;
++        while (qh_ptr != &hcd->non_periodic_sched_inactive) {
++                      qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry);
++                      qh_ptr = qh_ptr->next;
+ 
+-              /*
+-               * Move the QH from the non-periodic inactive schedule to the
+-               * non-periodic active schedule.
+-               */
+-              qh_ptr = qh_ptr->next;
+-              list_move(&qh->qh_list_entry, &hcd->non_periodic_sched_active);
++                      /* If a nak'd frame is in the queue for 100ms, forget
++                      * about its nak status, to prevent the situation where
++                      * a nak'd frame never gets resubmitted because there
++                      * are continously non-nakking tranfsfers available.
++                      */
++                      if (qh->nak_frame != 0xffff &&
++                      dwc_frame_num_gt(cur_frame, qh->nak_frame + 800))
++                              qh->nak_frame = 0xffff;
++
++                      /* In the first pass, ignore NAK'd retransmit
++                      * alltogether, to give them lower priority. */
++                      if (!include_nakd && qh->nak_frame != 0xffff)
++                              continue;
+ 
+-              if (ret_val == DWC_OTG_TRANSACTION_NONE) {
+-                      ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
+-              } else {
+-                      ret_val = DWC_OTG_TRANSACTION_ALL;
++                      /*
++                      * Check to see if this is a NAK'd retransmit, in which 
case ignore for retransmission
++                      * we hold off on bulk retransmissions to reduce NAK 
interrupt overhead for
++                      * cheeky devices that just hold off using NAKs
++                      */
++                      if (dwc_full_frame_num(qh->nak_frame) == 
dwc_full_frame_num(dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd))))
++                              continue;
++
++                      /* Ok, we found a candidate for scheduling. Is there a
++                      * free channel? */
++                      if (hcd->non_periodic_channels >=
++                      num_channels - hcd->periodic_channels ||
++                      list_empty(&hcd->free_hc_list)) {
++                              channels_full = 1;
++                              break;
++                      }
++
++                      /* When retrying a NAK'd transfer, we give it a fair
++                      * chance of completing again. */
++                      qh->nak_frame = 0xffff;
++                      assign_and_init_hc(hcd, qh);
++
++                      /*
++                      * Move the QH from the non-periodic inactive schedule 
to the
++                      * non-periodic active schedule.
++                      */
++                      list_move(&qh->qh_list_entry, 
&hcd->non_periodic_sched_active);
++
++                      if (ret_val == DWC_OTG_TRANSACTION_NONE) {
++                              ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
++                      } else {
++                              ret_val = DWC_OTG_TRANSACTION_ALL;
++                      }
++
++                      hcd->non_periodic_channels++;
+               }
++              if (hcd->core_if->dma_enable && channels_full &&
++                        hcd->periodic_channels + hcd->nakking_channels >= 
num_channels) {
++                      /* There are items queued, but all channels are either
++                       * reserved for periodic or have received NAKs. This
++                       * means that it could take an indefinite amount of time
++                       * before a channel is actually freed (since in DMA
++                       * mode, the hardware takes care of retries), so we take
++                       * action here by forcing a nakking channel to halt to
++                       * give other transfers a chance to run. */
++                      dwc_otg_qtd_t *qtd = list_entry(qh->qtd_list.next, 
dwc_otg_qtd_t, qtd_list_entry);
++                      struct urb *urb = qtd->urb;
++                      dwc_hc_t *hc = dwc_otg_halt_nakking_channel(hcd);
++
++                      if (hc)
++                        DWC_DEBUGPL(DBG_HCD "Out of Host Channels for 
non-periodic transfer - Halting channel %d (dev %d ep%d%s) to service qh %p 
(dev %d ep%d%s)\n", hc->hc_num, hc->dev_addr, hc->ep_num, (hc->ep_is_in ? "in" 
: "out"), qh, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), 
(usb_pipein(urb->pipe) != 0) ? "in" : "out");
+ 
+-              hcd->non_periodic_channels++;
++              }
+       }
+-      spin_unlock(&hcd->lock);
++
++      SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
+ 
+       return ret_val;
+ }
+ 
+ /**
++ * Halt a bulk channel that is blocking on NAKs to free up space.
++ *
++ * This will decrement hcd->nakking_channels immediately, but
++ * hcd->non_periodic_channels is not decremented until the channel is
++ * actually halted.
++ *
++ * Returns the halted channel.
++ */
++dwc_hc_t *dwc_otg_halt_nakking_channel(dwc_otg_hcd_t *hcd) {
++ int num_channels, i;
++ uint16_t cur_frame;
++
++ cur_frame = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
++ num_channels = hcd->core_if->core_params->host_channels;
++
++ for (i = 0; i < num_channels; i++) {
++   int channel = (hcd->last_channel_halted + 1 + i) % num_channels;
++   dwc_hc_t *hc = hcd->hc_ptr_array[channel];
++   if (hc->xfer_started
++       && !hc->halt_on_queue
++       && !hc->halt_pending
++       && hc->qh->nak_frame != 0xffff) {
++     dwc_otg_hc_halt(hcd, hc, DWC_OTG_HC_XFER_NAK);
++     /* Store the last channel halted to
++      * fairly rotate the channel to halt.
++      * This prevent the scenario where there
++      * are three blocking endpoints and only
++      * two free host channels, where the
++      * blocking endpoint that gets hc 3 will
++      * never be halted, while the other two
++      * endpoints will be fighting over the
++      * other host channel. */
++     hcd->last_channel_halted = channel;
++     /* Update nak_frame, so this frame is
++      * kept at low priority for a period of
++      * time starting now. */
++     hc->qh->nak_frame = cur_frame;
++     return hc;
++   }
++ }
++ dwc_otg_hcd_dump_state(hcd);
++ return NULL;
++}
++
++/**
+  * Attempts to queue a single transaction request for a host channel
+  * associated with either a periodic or non-periodic transfer. This function
+  * assumes that there is space available in the appropriate request queue. For
+@@ -2298,7 +2435,7 @@ static int queue_transaction(dwc_otg_hcd
+               /* Don't queue a request if the channel has been halted. */
+               retval = 0;
+       } else if (hc->halt_on_queue) {
+-              dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
++              dwc_otg_hc_halt(hcd, hc, hc->halt_status);
+               retval = 0;
+       } else if (hc->do_ping) {
+               if (!hc->xfer_started) {
+@@ -2446,12 +2583,12 @@ static void process_periodic_channels(dw
+       dwc_otg_host_global_regs_t *host_regs;
+       host_regs = hcd->core_if->host_if->host_global_regs;
+ 
+-      DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n");
++      DWC_DEBUGPL(DBG_HCD_FLOOD, "Queue periodic transactions\n");
+ #ifdef DEBUG
+       tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts);
+-      DWC_DEBUGPL(DBG_HCDV, "  P Tx Req Queue Space Avail (before queue): 
%d\n",
++      DWC_DEBUGPL(DBG_HCD_FLOOD, "  P Tx Req Queue Space Avail (before 
queue): %d\n",
+                   tx_status.b.ptxqspcavail);
+-      DWC_DEBUGPL(DBG_HCDV, "  P Tx FIFO Space Avail (before queue): %d\n",
++      DWC_DEBUGPL(DBG_HCD_FLOOD, "  P Tx FIFO Space Avail (before queue): 
%d\n",
+                   tx_status.b.ptxfspcavail);
+ #endif
+ 
+@@ -2586,7 +2723,12 @@ void dwc_otg_hcd_queue_transactions(dwc_
+  */
+ void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *hcd, struct urb *urb, int status)
+ {
++      unsigned long           flags;
++
++      SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
++
+ #ifdef DEBUG
++
+       if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+               DWC_PRINT("%s: urb %p, device %d, ep %d %s, status=%d\n",
+                         __func__, urb, usb_pipedevice(urb->pipe),
+@@ -2609,10 +2751,12 @@ void dwc_otg_hcd_complete_urb(dwc_otg_hc
+               
memcpy(urb->transfer_buffer,urb->aligned_transfer_buffer,urb->actual_length);
+       }
+ 
+-
++      usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+       urb->status = status;
+       urb->hcpriv = NULL;
++      SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
+       usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
++
+ }
+ 
+ /*
+@@ -2674,7 +2818,7 @@ void dwc_otg_hcd_dump_state(dwc_otg_hcd_
+       DWC_PRINT("  Num channels: %d\n", num_channels);
+       for (i = 0; i < num_channels; i++) {
+               dwc_hc_t *hc = hcd->hc_ptr_array[i];
+-              DWC_PRINT("  Channel %d:\n", i);
++              DWC_PRINT("  Channel %d: %p\n", i, hc);
+               DWC_PRINT("    dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+                         hc->dev_addr, hc->ep_num, hc->ep_is_in);
+               DWC_PRINT("    speed: %d\n", hc->speed);
+@@ -2696,6 +2840,8 @@ void dwc_otg_hcd_dump_state(dwc_otg_hcd_
+               DWC_PRINT("    xact_pos: %d\n", hc->xact_pos);
+               DWC_PRINT("    requests: %d\n", hc->requests);
+               DWC_PRINT("    qh: %p\n", hc->qh);
++              if (hc->qh)
++                      DWC_PRINT("    nak_frame: %x\n", hc->qh->nak_frame);
+               if (hc->xfer_started) {
+                       hfnum_data_t hfnum;
+                       hcchar_data_t hcchar;
+@@ -2735,6 +2881,8 @@ void dwc_otg_hcd_dump_state(dwc_otg_hcd_
+       }
+       DWC_PRINT("  non_periodic_channels: %d\n", hcd->non_periodic_channels);
+       DWC_PRINT("  periodic_channels: %d\n", hcd->periodic_channels);
++      DWC_PRINT("  nakking_channels: %d\n", hcd->nakking_channels);
++      DWC_PRINT("  last_channel_halted: %d\n", hcd->last_channel_halted);
+       DWC_PRINT("  periodic_usecs: %d\n", hcd->periodic_usecs);
+       np_tx_status.d32 = 
dwc_read_reg32(&hcd->core_if->core_global_regs->gnptxsts);
+       DWC_PRINT("  NP Tx Req Queue Space Avail: %d\n", 
np_tx_status.b.nptxqspcavail);
+--- a/drivers/usb/dwc/otg_hcd.h
++++ b/drivers/usb/dwc/otg_hcd.h
+@@ -194,6 +194,11 @@ typedef struct dwc_otg_qh {
+        */
+       uint16_t                sched_frame;
+ 
++      /*
++       * Frame a NAK was received on this queue head, used to minimise NAK 
retransmission
++       */
++      uint16_t nak_frame;
++
+       /** (micro)frame at which last start split was initialized. */
+       uint16_t                start_split_frame;
+ 
+@@ -328,6 +333,21 @@ typedef struct dwc_otg_hcd {
+       struct list_head        free_hc_list;
+ 
+       /**
++       * The number of bulk channels in the active schedule that do
++       * not have a halt pending or queued but received at least one
++       * nak and thus are probably blocking a host channel.
++       *
++       * This number is included in non_perodic_channels as well.
++       */
++      int nakking_channels;
++
++      /**
++       * The number of the last host channel that was halted to free
++       * up a host channel.
++       */
++      int last_channel_halted;
++
++      /**
+        * Number of host channels assigned to periodic transfers. Currently
+        * assuming that there is a dedicated host channel for each periodic
+        * transaction and at least one host channel available for
+@@ -452,6 +472,8 @@ extern void dwc_otg_hcd_queue_transactio
+                                          dwc_otg_transaction_type_e tr_type);
+ extern void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *_hcd, struct urb *urb,
+                                    int status);
++extern dwc_hc_t *dwc_otg_halt_nakking_channel(dwc_otg_hcd_t *hcd);
++
+ /** @} */
+ 
+ /** @name Interrupt Handler Functions */
+@@ -612,6 +634,40 @@ static inline uint16_t dwc_micro_frame_n
+       return frame & 0x7;
+ }
+ 
++/* Perform some sanity checks on nakking / non_perodic channel states. */
++static inline int check_nakking(struct dwc_otg_hcd *hcd, const char *func, 
const char* context) {
++#ifdef DEBUG
++      int nakking = 0, non_periodic = 0, i;
++      int num_channels = hcd->core_if->core_params->host_channels;
++      for (i = 0; i < num_channels; i++) {
++              dwc_hc_t *hc = hcd->hc_ptr_array[i];
++              if (hc->xfer_started
++                  && (hc->ep_type == DWC_OTG_EP_TYPE_BULK
++                      || hc->ep_type == DWC_OTG_EP_TYPE_CONTROL)) {
++                              non_periodic++;
++              }
++              if (hc->xfer_started
++                  && !hc->halt_on_queue
++                  && !hc->halt_pending
++                  && hc->qh->nak_frame != 0xffff) {
++                      nakking++;
++              }
++      }
++
++      if (nakking != hcd->nakking_channels
++          || nakking > hcd->non_periodic_channels
++          || non_periodic != hcd->non_periodic_channels) {
++              printk("%s/%s: Inconsistent nakking state\n", func, context);
++              printk("non_periodic: %d, real %d, nakking: %d, real %d\n", 
hcd->non_periodic_channels, non_periodic, hcd->nakking_channels, nakking);
++              dwc_otg_hcd_dump_state(hcd);
++              WARN_ON(1);
++              return 1;
++      }
++#endif
++      return 0;
++}
++
++
+ #ifdef DEBUG
+ /**
+  * Macro to sample the remaining PHY clocks left in the current frame. This
+--- a/drivers/usb/dwc/otg_hcd_intr.c
++++ b/drivers/usb/dwc/otg_hcd_intr.c
+@@ -65,14 +65,14 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
+ # ifndef DEBUG_SOF
+               if (gintsts.d32 != DWC_SOF_INTR_MASK)
+ # endif
+-                      DWC_DEBUGPL(DBG_HCD, "\n");
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "\n");
+ #endif
+ 
+ #ifdef DEBUG
+ # ifndef DEBUG_SOF
+               if (gintsts.d32 != DWC_SOF_INTR_MASK)
+ # endif
+-                      DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Interrupt Detected 
gintsts&gintmsk=0x%08x\n", gintsts.d32);
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "DWC OTG HCD Interrupt 
Detected gintsts&gintmsk=0x%08x\n", gintsts.d32);
+ #endif
+               if (gintsts.b.usbreset) {
+                       DWC_PRINT("Usb Reset In Host Mode\n");
+@@ -103,10 +103,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
+               if (gintsts.d32 != DWC_SOF_INTR_MASK)
+ # endif
+               {
+-                      DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Finished Servicing 
Interrupts\n");
+-                      DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n",
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "DWC OTG HCD Finished 
Servicing Interrupts\n");
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "DWC OTG HCD 
gintsts=0x%08x\n",
+                                   dwc_read_reg32(&global_regs->gintsts));
+-                      DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n",
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "DWC OTG HCD 
gintmsk=0x%08x\n",
+                                   dwc_read_reg32(&global_regs->gintmsk));
+               }
+ #endif
+@@ -115,7 +115,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
+ # ifndef DEBUG_SOF
+       if (gintsts.d32 != DWC_SOF_INTR_MASK)
+ # endif
+-              DWC_DEBUGPL(DBG_HCD, "\n");
++              DWC_DEBUGPL(DBG_HCD_FLOOD, "\n");
+ #endif
+ 
+       }
+@@ -534,7 +534,7 @@ static int update_urb_state_xfer_comp(dw
+                                                    DWC_OTG_HC_XFER_COMPLETE,
+                                                    &short_read);
+ 
+-      if (short_read || urb->actual_length == urb->transfer_buffer_length) {
++      if (short_read || urb->actual_length >= urb->transfer_buffer_length) {
+               xfer_done = 1;
+               if (short_read && (urb->transfer_flags & URB_SHORT_NOT_OK)) {
+                       urb->status = -EREMOTEIO;
+@@ -551,6 +551,7 @@ static int update_urb_state_xfer_comp(dw
+                           __func__, (hc->ep_is_in ? "IN" : "OUT"), 
hc->hc_num);
+               DWC_DEBUGPL(DBG_HCDV, "  hc->xfer_len %d\n", hc->xfer_len);
+               DWC_DEBUGPL(DBG_HCDV, "  hctsiz.xfersize %d\n", 
hctsiz.b.xfersize);
++              DWC_DEBUGPL(DBG_HCDV, "  urb %p\n", urb);
+               DWC_DEBUGPL(DBG_HCDV, "  urb->transfer_buffer_length %d\n",
+                           urb->transfer_buffer_length);
+               DWC_DEBUGPL(DBG_HCDV, "  urb->actual_length %d\n", 
urb->actual_length);
+@@ -603,10 +604,12 @@ static void deactivate_qh(dwc_otg_hcd_t
+ {
+       int continue_split = 0;
+       dwc_otg_qtd_t *qtd;
++      unsigned long flags;
+ 
+       DWC_DEBUGPL(DBG_HCDV, "  %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd);
+ 
+-      spin_lock(&hcd->lock);
++      SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
++
+       qtd = list_entry(qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry);
+ 
+       if (qtd->complete_split) {
+@@ -623,8 +626,9 @@ static void deactivate_qh(dwc_otg_hcd_t
+ 
+       qh->channel = NULL;
+       qh->qtd_in_process = NULL;
+-      spin_unlock(&hcd->lock);
++
+       dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
++      SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
+ }
+ 
+ /**
+@@ -764,10 +768,18 @@ static void release_channel(dwc_otg_hcd_
+       dwc_otg_hc_cleanup(hcd->core_if, hc);
+       list_add_tail(&hc->hc_list_entry, &hcd->free_hc_list);
+ 
++      if (!hc->halt_on_queue && !hc->halt_pending && hc->qh->nak_frame != 
0xffff)
++              hcd->nakking_channels--;
++
+       switch (hc->ep_type) {
+       case DWC_OTG_EP_TYPE_CONTROL:
+       case DWC_OTG_EP_TYPE_BULK:
+               hcd->non_periodic_channels--;
++
++              /* This condition has once been observed, but the cause
++               * was never determined. Check for it here, to collect
++               * debug data if it occurs again. */
++              WARN_ON_ONCE(hcd->non_periodic_channels < 0);
+               break;
+ 
+       default:
+@@ -779,6 +791,9 @@ static void release_channel(dwc_otg_hcd_
+               break;
+       }
+ 
++      if (halt_status != DWC_OTG_HC_XFER_NAK)
++              hc->qh->nak_frame = 0xffff;
++
+       /* Try to queue more transfers now that there's a free channel. */
+       tr_type = dwc_otg_hcd_select_transactions(hcd);
+       if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+@@ -807,7 +822,7 @@ static void halt_channel(dwc_otg_hcd_t *
+       }
+ 
+       /* Slave mode processing... */
+-      dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
++      dwc_otg_hc_halt(hcd, hc, halt_status);
+ 
+       if (hc->halt_on_queue) {
+               gintmsk_data_t gintmsk = {.d32 = 0};
+@@ -1085,6 +1100,7 @@ static void update_urb_state_xfer_intr(d
+               DWC_DEBUGPL(DBG_HCDV, "  hctsiz.pktcnt %d\n", hctsiz.b.pktcnt);
+               DWC_DEBUGPL(DBG_HCDV, "  hc->max_packet %d\n", hc->max_packet);
+               DWC_DEBUGPL(DBG_HCDV, "  bytes_transferred %d\n", 
bytes_transferred);
++              DWC_DEBUGPL(DBG_HCDV, "  urb %p\n", urb);
+               DWC_DEBUGPL(DBG_HCDV, "  urb->actual_length %d\n", 
urb->actual_length);
+               DWC_DEBUGPL(DBG_HCDV, "  urb->transfer_buffer_length %d\n",
+                           urb->transfer_buffer_length);
+@@ -1103,6 +1119,23 @@ static int32_t handle_hc_nak_intr(dwc_ot
+ {
+       DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+                   "NAK Received--\n", hc->hc_num);
++      /*
++      * When we get bulk NAKs then remember this so we holdoff on this qh 
until
++      * the beginning of the next frame
++      */
++      switch (usb_pipetype(qtd->urb->pipe)) {
++              case PIPE_BULK:
++                      /* xfer_started can be 0 when a halted interrupt
++                       * occurs with the nak flag set, then first the
++                       * halted handler runs and then this nak
++                       * handler. In this case, also don't update
++                       * nak_frame, since the qh might already be
++                       * assigned to another host channel. */
++                      if (!hc->halt_on_queue && !hc->halt_pending && 
hc->xfer_started && hc->qh->nak_frame == 0xffff)
++                              hcd->nakking_channels++;
++                      if (hc->xfer_started)
++                              hc->qh->nak_frame = 
dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
++      }
+ 
+       /*
+        * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+@@ -1410,7 +1443,7 @@ static int32_t handle_hc_ahberr_intr(dwc
+        * Force a channel halt. Don't call halt_channel because that won't
+        * write to the HCCHARn register in DMA mode to force the halt.
+        */
+-      dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
++      dwc_otg_hc_halt(hcd, hc, DWC_OTG_HC_XFER_AHB_ERR);
+ 
+       disable_hc_int(hc_regs, ahberr);
+       return 1;
+@@ -1515,13 +1548,28 @@ static int32_t handle_hc_datatglerr_intr
+                                        dwc_otg_qtd_t *qtd)
+ {
+       DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+-                  "Data Toggle Error--\n", hc->hc_num);
++              "Data Toggle Error on %s transfer--\n",
++              hc->hc_num, (hc->ep_is_in ? "IN" : "OUT"));
+ 
+-      if (hc->ep_is_in) {
++      /* Data toggles on split transactions cause the hc to halt.
++       * restart transfer */
++      if (hc->qh->do_split) {
++              qtd->error_count++;
++              save_data_toggle(hc, hc_regs, qtd);
++              update_urb_state_xfer_intr(hc, hc_regs,
++                      qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
++              halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
++      } else if (hc->ep_is_in) {
++              /* An unmasked data toggle error on a non-split DMA transaction 
++is
++               * for the sole purpose of resetting error counts. Disable other
++               * interrupts unmasked for the same reason.
++               */
++              if (hcd->core_if->dma_enable) {
++                      disable_hc_int(hc_regs, ack);
++                      disable_hc_int(hc_regs, nak);
++              }
+               qtd->error_count = 0;
+-      } else {
+-              DWC_ERROR("Data Toggle Error on OUT transfer,"
+-                        "channel %d\n", hc->hc_num);
+       }
+ 
+       disable_hc_int(hc_regs, datatglerr);
+@@ -1583,6 +1631,8 @@ static inline int halt_status_ok(dwc_otg
+                        "hcchar 0x%08x, trying to halt again\n",
+                        __func__, hcchar.d32);
+               clear_hc_int(hc_regs, chhltd);
++              if (hc->halt_pending && !hc->halt_on_queue && hc->qh->nak_frame 
!= 0xffff)
++                      hcd->nakking_channels++;
+               hc->halt_pending = 0;
+               halt_channel(hcd, hc, qtd, hc->halt_status);
+               return 0;
+@@ -1612,13 +1662,46 @@ static void handle_hc_chhltd_intr_dma(dw
+               if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in &&
+                   (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+                    hc->ep_type == DWC_OTG_EP_TYPE_BULK)) {
+-                      DWC_DEBUGPL(DBG_HCD, "OUT NAK enhancement enabled\n");
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "OUT NAK enhancement 
enabled\n");
+                       out_nak_enh = 1;
+               } else {
+-                      DWC_DEBUGPL(DBG_HCD, "OUT NAK enhancement disabled, not 
HS Ctrl/Bulk OUT EP\n");
++                      DWC_DEBUGPL(DBG_HCD_FLOOD, "OUT NAK enhancement 
disabled, not HS Ctrl/Bulk OUT EP\n");
+               }
+       } else {
+-              DWC_DEBUGPL(DBG_HCD, "OUT NAK enhancement disabled, no core 
support\n");
++              DWC_DEBUGPL(DBG_HCD_FLOOD, "OUT NAK enhancement disabled, no 
core support\n");
++      }
++
++      if (hc->halt_status == DWC_OTG_HC_XFER_NAK) {
++              /* The channel was nakking and halted to free up the
++               * channel for another transfer. If this channel has
++               * already received data, we need to skip that amount on
++               * the next try.
++               */
++              update_urb_state_xfer_intr(hc, hc_regs, qtd->urb,
++                                                 qtd, DWC_OTG_HC_XFER_NAK);
++
++              save_data_toggle(hc, hc_regs, qtd);
++
++              /* It turns out that sometimes a channel is halted just
++               * as it receives its last packet. This causes the
++               * to trigger a channel halted interrupt without a
++               * transfer complete flag, even though the transfer is
++               * actually complete. If we don't handle that here, the
++               * qtd will be resubmitted and since bulk in can't have
++               * empty packets, this will cause one full packet of
++               * "extra" data to be transfered. So we check here to
++               * see if the transfer is complete and handle that
++               * accordingly.
++               */
++              if (usb_pipebulk(qtd->urb->pipe) &&
++                  usb_pipein(qtd->urb->pipe) &&
++                  qtd->urb->actual_length == 
qtd->urb->transfer_buffer_length) {
++                      dwc_otg_hcd_complete_urb(hcd, qtd->urb, 0);
++                      complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, 
DWC_OTG_HC_XFER_URB_COMPLETE);
++              } else {
++                      release_channel(hcd, hc, qtd, hc->halt_status);
++              }
++              return;
+       }
+ 
+       if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+@@ -1666,6 +1749,8 @@ static void handle_hc_chhltd_intr_dma(dw
+                * that started with a PING. The xacterr takes precedence.
+                */
+               handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
++      } else if (hcint.b.datatglerr) {
++              handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
+       } else if (!out_nak_enh) {
+               if (hcint.b.nyet) {
+                       /*
+@@ -1767,6 +1852,10 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
+       DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num);
+ 
+       hc = dwc_otg_hcd->hc_ptr_array[num];
++
++      check_nakking(dwc_otg_hcd, __FUNCTION__,  "start");
++
++
+       hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
+       qtd = list_entry(hc->qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry);
+ 
+@@ -1774,6 +1863,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
+       hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk);
+       DWC_DEBUGPL(DBG_HCDV, "  hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 
0x%08x\n",
+                   hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32));
++
+       hcint.d32 = hcint.d32 & hcintmsk.d32;
+ 
+       if (!dwc_otg_hcd->core_if->dma_enable) {
+@@ -1803,7 +1893,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
+       if (hcint.b.nak) {
+               retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+       }
+-      if (hcint.b.ack) {
++      if (hcint.b.ack && !hcint.b.chhltd) {
+               retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+       }
+       if (hcint.b.nyet) {
+@@ -1821,6 +1911,11 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
+       if (hcint.b.datatglerr) {
+               retval |= handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, 
qtd);
+       }
++      if (check_nakking(dwc_otg_hcd, __FUNCTION__,  "end")) {
++              DWC_WARN("--Host Channel Interrupt--, Channel %d\n", num);
++              DWC_WARN("  hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 
0x%08x\n",
++                  hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32));
++      }
+ 
+       return retval;
+ }
+--- a/drivers/usb/dwc/otg_hcd_queue.c
++++ b/drivers/usb/dwc/otg_hcd_queue.c
+@@ -211,6 +211,8 @@ void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *
+                   usb_pipeendpoint(urb->pipe),
+                   usb_pipein(urb->pipe) == USB_DIR_IN ? "IN" : "OUT");
+ 
++      qh->nak_frame = 0xffff;
++
+       switch(urb->dev->speed) {
+       case USB_SPEED_LOW:
+               speed = "low";
+@@ -453,7 +455,26 @@ static int schedule_periodic(dwc_otg_hcd
+       int status;
+       struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
+       int frame;
++      int num_channels;
++
++      num_channels = hcd->core_if->core_params->host_channels;
+ 
++      if ((hcd->periodic_channels < num_channels - 1)) {
++        if (hcd->periodic_channels + hcd->nakking_channels >= num_channels) {
++          /* All non-periodic channels are nakking? Halt
++           * one to make room (as long as there is at
++           * least one channel for non-periodic transfers,
++           * all the blocking non-periodics can time-share
++           * that one channel. */
++          dwc_hc_t *hc = dwc_otg_halt_nakking_channel(hcd);
++          if (hc)
++            DWC_DEBUGPL(DBG_HCD, "Out of Host Channels for periodic transfer 
- Halting channel %d (dev %d ep%d%s)\n", hc->hc_num, hc->dev_addr, hc->ep_num, 
(hc->ep_is_in ? "in" : "out"));
++        }
++        /* It could be that all channels are currently occupied,
++         * but in that case one will be freed up soon (either
++         * because it completed or because it was forced to halt
++         * above). */
++      }
+       status = find_uframe(hcd, qh);
+       frame = -1;
+       if (status == 0) {
+@@ -483,6 +504,8 @@ static int schedule_periodic(dwc_otg_hcd
+       /* Always start in the inactive schedule. */
+       list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive);
+ 
++      hcd->periodic_channels++;
++
+       /* Update claimed usecs per (micro)frame. */
+       hcd->periodic_usecs += qh->usecs;
+ 
+@@ -553,6 +576,9 @@ static void deschedule_periodic(dwc_otg_
+       int i;
+ 
+       list_del_init(&qh->qh_list_entry);
++
++      hcd->periodic_channels--;
++
+       /* Update claimed usecs per (micro)frame. */
+       hcd->periodic_usecs -= qh->usecs;
+       for (i = 0; i < 8; i++) {
+@@ -628,9 +654,6 @@ void dwc_otg_hcd_qh_remove (dwc_otg_hcd_
+  */
+ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int 
sched_next_periodic_split)
+ {
+-      unsigned long flags;
+-      SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
+-
+       if (dwc_qh_is_non_per(qh)) {
+               dwc_otg_hcd_qh_remove(hcd, qh);
+               if (!list_empty(&qh->qtd_list)) {
+@@ -690,8 +713,6 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h
+                       }
+               }
+       }
+-
+-      SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
+ }
+ 
+ /**
+@@ -759,22 +780,22 @@ int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *
+ {
+       struct usb_host_endpoint *ep;
+       dwc_otg_qh_t *qh;
+-      unsigned long flags;
+       int retval = 0;
+ 
+       struct urb *urb = qtd->urb;
+ 
+-      SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, flags);
+-
+       /*
+        * Get the QH which holds the QTD-list to insert to. Create QH if it
+        * doesn't exist.
+        */
++      usb_hcd_link_urb_to_ep(dwc_otg_hcd_to_hcd(dwc_otg_hcd), urb);
+       ep = dwc_urb_to_endpoint(urb);
+       qh = (dwc_otg_qh_t *)ep->hcpriv;
+       if (qh == NULL) {
+               qh = dwc_otg_hcd_qh_create (dwc_otg_hcd, urb);
+               if (qh == NULL) {
++                      
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(dwc_otg_hcd), urb);
++                      retval = -ENOMEM;
+                       goto done;
+               }
+               ep->hcpriv = qh;
+@@ -783,11 +804,11 @@ int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *
+       retval = dwc_otg_hcd_qh_add(dwc_otg_hcd, qh);
+       if (retval == 0) {
+               list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
++      } else {
++              usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(dwc_otg_hcd), 
urb);
+       }
+ 
+  done:
+-      SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
+-
+       return retval;
+ }
+ 
+--- a/drivers/usb/dwc/otg_plat.h
++++ b/drivers/usb/dwc/otg_plat.h
+@@ -202,6 +202,7 @@ static inline uint32_t SET_DEBUG_LEVEL(
+ /** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in 
host
+  *  mode. */
+ #define DBG_HCD_URB   (0x800)
++#define DBG_HCD_FLOOD (0x1)
+ 
+ /** When debug level has any bit set, display debug messages */
+ #define DBG_ANY               (0xFF)
-- 
1.8.4.2
_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

Reply via email to