[cns3xxx]: various dwc (OTG) driver fixups
authorkaloz <kaloz@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Wed, 12 Mar 2014 13:37:40 +0000 (13:37 +0000)
committerkaloz <kaloz@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Wed, 12 Mar 2014 13:37:40 +0000 (13:37 +0000)
Signed-off-by: Tim Harvey <tharvey@gateworks.com>
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@39892 3c298f89-4303-0410-b956-a3cf2f4a3e73

target/linux/cns3xxx/files/drivers/usb/dwc/otg_attr.c
target/linux/cns3xxx/files/drivers/usb/dwc/otg_cil.c
target/linux/cns3xxx/files/drivers/usb/dwc/otg_cil.h
target/linux/cns3xxx/files/drivers/usb/dwc/otg_hcd.c
target/linux/cns3xxx/files/drivers/usb/dwc/otg_hcd.h
target/linux/cns3xxx/files/drivers/usb/dwc/otg_hcd_intr.c
target/linux/cns3xxx/files/drivers/usb/dwc/otg_hcd_queue.c
target/linux/cns3xxx/files/drivers/usb/dwc/otg_plat.h

index 343acd2..45d0678 100644 (file)
@@ -721,9 +721,10 @@ static ssize_t hcddump_show( struct device *_dev,
                             char *buf)
 {
 #ifndef DWC_DEVICE_ONLY
                             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" );
 }
 #endif
        return sprintf( buf, "HCD Dump\n" );
 }
index 42983af..668d4d2 100644 (file)
@@ -66,7 +66,7 @@
 #include "otg_regs.h"
 #include "otg_cil.h"
 #include "otg_pcd.h"
 #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
 
 /**
  * This function is called to initialize the DWC_otg CSR data
@@ -1156,12 +1156,13 @@ void dwc_otg_core_host_init(dwc_otg_core_if_t *core_if)
                DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i);
                do {
                        hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
                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;
                        }
                        {
                                DWC_ERROR("%s: Unable to clear halt on channel %d\n",
                                          __func__, i);
                                break;
                        }
+                       udelay(100);
                }
                while (hcchar.b.chen);
        }
                }
                while (hcchar.b.chen);
        }
@@ -1211,6 +1212,8 @@ void dwc_otg_hc_init(dwc_otg_core_if_t *core_if, dwc_hc_t *hc)
        hc_intr_mask.b.chhltd = 1;
        if (core_if->dma_enable) {
                hc_intr_mask.b.ahberr = 1;
        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;
                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 *core_if, dwc_hc_t *hc)
  * @param hc Host channel to halt.
  * @param halt_status Reason for halting the channel.
  */
  * @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)
 {
                         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 *core_if,
        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_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;
 
        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 *core_if,
 
        hc->halt_status = halt_status;
 
 
        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;
        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_core_if_t *core_if, dwc_hc_t *hc)
        dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32);
 
        DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
        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);
 
        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_core_if_t *core_if, dwc_hc_t *hc)
        /* Set host channel enable after all other setup is complete. */
        hcchar.b.chen = 1;
        hcchar.b.chdis = 0;
        /* 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;
        dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
 
        hc->xfer_started = 1;
@@ -1786,7 +1797,7 @@ void dwc_otg_hc_start_transfer(dwc_otg_core_if_t *core_if, dwc_hc_t *hc)
        }
 
 #ifdef DEBUG
        }
 
 #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;
        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_core_if_t *core_if, dwc_hc_t *hc)
                hcchar.b.chen = 1;
                hcchar.b.chdis = 0;
                DWC_DEBUGPL(DBG_HCDV, "  IN xfer: hcchar = 0x%08x\n", hcchar.d32);
                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;
                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_t *core_if, dwc_hc_t *hc)
        hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
        hcchar.b.chen = 1;
        hcchar.b.chdis = 0;
        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);
 }
 
        dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
 }
 
@@ -2089,9 +2108,10 @@ void dwc_otg_ep_activate(dwc_otg_core_if_t *core_if, dwc_ep_t *ep)
                        if(core_if->dma_desc_enable) {
                                diepmsk.b.bna = 1;
                        }
                        if(core_if->dma_desc_enable) {
                                diepmsk.b.bna = 1;
                        }
+
 /*
                        if(core_if->dma_enable) {
 /*
                        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);
                        }
 */
                        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_if_t *core_if,
                        dwc_read_reg32(&global_regs->gnptxsts));
                        break;
                }
                        dwc_read_reg32(&global_regs->gnptxsts));
                        break;
                }
+               udelay(1);
        }
        while (greset.b.txfflsh == 1);
 
        }
        while (greset.b.txfflsh == 1);
 
@@ -3599,6 +3620,7 @@ void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t *core_if)
                                greset.d32);
                        break;
                }
                                greset.d32);
                        break;
                }
+               udelay(1);
        }
        while (greset.b.rxfflsh == 1);
 
        }
        while (greset.b.rxfflsh == 1);
 
@@ -3640,6 +3662,7 @@ void dwc_otg_core_reset(dwc_otg_core_if_t *core_if)
                                greset.d32);
                        break;
                }
                                greset.d32);
                        break;
                }
+               udelay(1);
        }
        while (greset.b.csftrst == 1);
 
        }
        while (greset.b.csftrst == 1);
 
index 463214b..d1a4993 100644 (file)
@@ -45,6 +45,8 @@
 #include "linux/timer.h"
 #endif
 
 #include "linux/timer.h"
 #endif
 
+struct dwc_otg_hcd;
+
 /**
  * @file
  * This file contains the interface to the Core Interface Layer.
 /**
  * @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
         */
        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.
 
        /**
         * Specifies the maximum speed of operation in host and device mode.
@@ -876,7 +878,7 @@ extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t *core_if, dwc_ep
  */
 /**@{*/
 extern void dwc_otg_hc_init(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
  */
 /**@{*/
 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);
                                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);
index e51b8c8..5f33fa5 100644 (file)
@@ -176,8 +176,10 @@ static void kill_urbs_in_qh_list(dwc_otg_hcd_t *hcd, struct list_head *qh_list)
                     qtd_item = qh->qtd_list.next) {
                        qtd = list_entry(qtd_item, dwc_otg_qtd_t, qtd_list_entry);
                        if (qtd->urb != NULL) {
                     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);
                                dwc_otg_hcd_complete_urb(hcd, qtd->urb,
                                                         -ETIMEDOUT);
+                               SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
                        }
                        dwc_otg_hcd_qtd_remove_and_free(hcd, qtd);
                }
                        }
                        dwc_otg_hcd_qtd_remove_and_free(hcd, qtd);
                }
@@ -589,6 +591,7 @@ static void hcd_reinit(dwc_otg_hcd_t *hcd)
        hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
        hcd->non_periodic_channels = 0;
        hcd->periodic_channels = 0;
        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
 
        /*
         * Put all channels in the free channel list and clean up channel
@@ -853,10 +856,10 @@ static void dump_channel_info(dwc_otg_hcd_t *hcd,
 //OTG host require the DMA addr is DWORD-aligned,
 //patch it if the buffer is not DWORD-aligned
 inline
 //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)
 
        if((!urb->transfer_buffer)||!urb->transfer_dma||urb->transfer_dma==0xffffffff)
-               return;
+               return 0;
 
        if(((u32)urb->transfer_buffer)& 0x3){
                /*
 
        if(((u32)urb->transfer_buffer)& 0x3){
                /*
@@ -881,11 +884,12 @@ void hcd_check_and_patch_dma_addr(struct urb *urb){
                                kfree(urb->aligned_transfer_buffer);
                        }
                        urb->aligned_transfer_buffer=kmalloc(urb->aligned_transfer_buffer_length,GFP_KERNEL|GFP_DMA|GFP_ATOMIC);
                                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");
                        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;
                        //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 urb *urb){
                        dma_sync_single_for_device(NULL,urb->transfer_dma,urb->transfer_buffer_length,DMA_TO_DEVICE);
                }
        }
                        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_hcd *hcd,
        int retval = 0;
        dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
        dwc_otg_qtd_t *qtd;
        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");
 #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_hcd *hcd,
 #endif
        if (!dwc_otg_hcd->flags.b.port_connect_status) {
                /* No longer connected. */
 #endif
        if (!dwc_otg_hcd->flags.b.port_connect_status) {
                /* No longer connected. */
+               SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
                return -ENODEV;
        }
 
                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");
        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;
        }
 
                return -ENOMEM;
        }
 
@@ -934,7 +953,7 @@ int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd,
                          "Error status %d\n", retval);
                dwc_otg_hcd_qtd_free(qtd);
        }
                          "Error status %d\n", retval);
                dwc_otg_hcd_qtd_free(qtd);
        }
-
+       SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
        return retval;
 }
 
        return retval;
 }
 
@@ -948,6 +967,7 @@ int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd,
        dwc_otg_qtd_t *urb_qtd;
        dwc_otg_qh_t *qh;
        struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
        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");
 
 
        DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n");
 
@@ -958,10 +978,6 @@ int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd,
        urb_qtd = (dwc_otg_qtd_t *)urb->hcpriv;
        qh = (dwc_otg_qh_t *)ep->hcpriv;
 
        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");
 #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_hcd *hcd,
        }
 #endif
 
        }
 #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) {
                /* 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_hcd *hcd,
                         * written to halt the channel since the core is in
                         * device mode.
                         */
                         * 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);
                }
        }
                                        DWC_OTG_HC_XFER_URB_DEQUEUE);
                }
        }
@@ -992,22 +1008,28 @@ int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd,
         * schedule if it has any remaining QTDs.
         */
        dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd, urb_qtd);
         * 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 {
                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);
                        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;
        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);
        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_hcd_t *hcd, dwc_otg_qh_t *qh)
        dwc_otg_qtd_t   *qtd;
        struct urb      *urb;
 
        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);
 
        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);
 
        /* 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;
 
        qh->channel = hc;
        qh->qtd_in_process = qtd;
 
@@ -2202,16 +2228,24 @@ static void assign_and_init_hc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
 dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *hcd)
 {
        struct list_head                *qh_ptr;
 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;
        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
 
 
 #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)) {
        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_select_transactions(dwc_otg_hcd_t *hcd)
         * schedule. Some free host channels may not be used if they are
         * reserved for periodic transfers.
         */
         * 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;
        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;
 
 
-               if (ret_val == DWC_OTG_TRANSACTION_NONE) {
-                       ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
-               } else {
-                       ret_val = DWC_OTG_TRANSACTION_ALL;
+                       /* In the first pass, ignore NAK'd retransmit
+                       * alltogether, to give them lower priority. */
+                       if (!include_nakd && qh->nak_frame != 0xffff)
+                               continue;
+
+                       /*
+                       * 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;
 }
 
 /**
 
        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
  * 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_t *hcd,
                /* Don't queue a request if the channel has been halted. */
                retval = 0;
        } else if (hc->halt_on_queue) {
                /* 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) {
                retval = 0;
        } else if (hc->do_ping) {
                if (!hc->xfer_started) {
@@ -2446,12 +2583,12 @@ static void process_periodic_channels(dwc_otg_hcd_t *hcd)
        dwc_otg_host_global_regs_t *host_regs;
        host_regs = hcd->core_if->host_if->host_global_regs;
 
        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);
 #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);
                    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
 
                    tx_status.b.ptxfspcavail);
 #endif
 
@@ -2586,7 +2723,12 @@ void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *hcd,
  */
 void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *hcd, struct urb *urb, int status)
 {
  */
 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
 #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),
        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_hcd_t *hcd, struct urb *urb, int status)
                memcpy(urb->transfer_buffer,urb->aligned_transfer_buffer,urb->actual_length);
        }
 
                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;
        urb->status = status;
        urb->hcpriv = NULL;
+       SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
        usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
        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_t *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("  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);
                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_t *hcd)
                DWC_PRINT("    xact_pos: %d\n", hc->xact_pos);
                DWC_PRINT("    requests: %d\n", hc->requests);
                DWC_PRINT("    qh: %p\n", hc->qh);
                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;
                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_t *hcd)
        }
        DWC_PRINT("  non_periodic_channels: %d\n", hcd->non_periodic_channels);
        DWC_PRINT("  periodic_channels: %d\n", hcd->periodic_channels);
        }
        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);
        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);
index 935f353..cef2879 100644 (file)
@@ -194,6 +194,11 @@ typedef struct dwc_otg_qh {
         */
        uint16_t                sched_frame;
 
         */
        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;
 
        /** (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;
 
        /**
        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
         * 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_transactions(dwc_otg_hcd_t *hcd,
                                           dwc_otg_transaction_type_e tr_type);
 extern void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *_hcd, struct urb *urb,
                                     int status);
                                           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 */
 /** @} */
 
 /** @name Interrupt Handler Functions */
@@ -612,6 +634,40 @@ static inline uint16_t dwc_micro_frame_num(uint16_t frame)
        return frame & 0x7;
 }
 
        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
 #ifdef DEBUG
 /**
  * Macro to sample the remaining PHY clocks left in the current frame. This
index e63b863..532ff7a 100644 (file)
@@ -65,14 +65,14 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t *dwc_otg_hcd)
 # ifndef DEBUG_SOF
                if (gintsts.d32 != DWC_SOF_INTR_MASK)
 # endif
 # 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
 #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");
 #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_hcd_t *dwc_otg_hcd)
                if (gintsts.d32 != DWC_SOF_INTR_MASK)
 # endif
                {
                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_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
                                    dwc_read_reg32(&global_regs->gintmsk));
                }
 #endif
@@ -115,7 +115,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t *dwc_otg_hcd)
 # ifndef DEBUG_SOF
        if (gintsts.d32 != DWC_SOF_INTR_MASK)
 # endif
 # ifndef DEBUG_SOF
        if (gintsts.d32 != DWC_SOF_INTR_MASK)
 # endif
-               DWC_DEBUGPL(DBG_HCD, "\n");
+               DWC_DEBUGPL(DBG_HCD_FLOOD, "\n");
 #endif
 
        }
 #endif
 
        }
@@ -534,7 +534,7 @@ static int update_urb_state_xfer_comp(dwc_hc_t *hc,
                                                     DWC_OTG_HC_XFER_COMPLETE,
                                                     &short_read);
 
                                                     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;
                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(dwc_hc_t *hc,
                            __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);
                            __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);
                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 *hcd,
 {
        int continue_split = 0;
        dwc_otg_qtd_t *qtd;
 {
        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);
 
 
        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) {
        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 *hcd,
 
        qh->channel = NULL;
        qh->qtd_in_process = NULL;
 
        qh->channel = NULL;
        qh->qtd_in_process = NULL;
-       spin_unlock(&hcd->lock);
+
        dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
        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_t *hcd,
        dwc_otg_hc_cleanup(hcd->core_if, hc);
        list_add_tail(&hc->hc_list_entry, &hcd->free_hc_list);
 
        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--;
        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:
                break;
 
        default:
@@ -779,6 +791,9 @@ static void release_channel(dwc_otg_hcd_t *hcd,
                break;
        }
 
                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) {
        /* 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 *hcd,
        }
 
        /* Slave mode processing... */
        }
 
        /* 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};
 
        if (hc->halt_on_queue) {
                gintmsk_data_t gintmsk = {.d32 = 0};
@@ -1085,6 +1100,7 @@ static void update_urb_state_xfer_intr(dwc_hc_t *hc,
                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, "  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);
                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_otg_hcd_t *hcd,
 {
        DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
                    "NAK Received--\n", hc->hc_num);
 {
        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
 
        /*
         * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
@@ -1410,7 +1443,7 @@ static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t *hcd,
         * 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.
         */
         * 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;
 
        disable_hc_int(hc_regs, ahberr);
        return 1;
@@ -1515,13 +1548,28 @@ static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t *hcd,
                                         dwc_otg_qtd_t *qtd)
 {
        DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
                                         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;
                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);
        }
 
        disable_hc_int(hc_regs, datatglerr);
@@ -1583,6 +1631,8 @@ static inline int halt_status_ok(dwc_otg_hcd_t *hcd,
                         "hcchar 0x%08x, trying to halt again\n",
                         __func__, hcchar.d32);
                clear_hc_int(hc_regs, chhltd);
                         "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;
                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(dwc_otg_hcd_t *hcd,
                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)) {
                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 {
                        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 {
                }
        } 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 ||
        }
 
        if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
@@ -1666,6 +1749,8 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t *hcd,
                 * that started with a PING. The xacterr takes precedence.
                 */
                handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
                 * 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) {
                        /*
        } else if (!out_nak_enh) {
                if (hcint.b.nyet) {
                        /*
@@ -1767,6 +1852,10 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t *dwc_otg_hcd, uint32_t num)
        DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num);
 
        hc = dwc_otg_hcd->hc_ptr_array[num];
        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);
 
        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_otg_hcd_t *dwc_otg_hcd, uint32_t num)
        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));
        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) {
        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_otg_hcd_t *dwc_otg_hcd, uint32_t num)
        if (hcint.b.nak) {
                retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
        }
        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) {
                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_otg_hcd_t *dwc_otg_hcd, uint32_t num)
        if (hcint.b.datatglerr) {
                retval |= handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
        }
        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;
 }
 
        return retval;
 }
index 7395d1d..6e7b53c 100644 (file)
@@ -211,6 +211,8 @@ void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb)
                    usb_pipeendpoint(urb->pipe),
                    usb_pipein(urb->pipe) == USB_DIR_IN ? "IN" : "OUT");
 
                    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";
        switch(urb->dev->speed) {
        case USB_SPEED_LOW:
                speed = "low";
@@ -453,7 +455,26 @@ static int schedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
        int status;
        struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
        int frame;
        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) {
        status = find_uframe(hcd, qh);
        frame = -1;
        if (status == 0) {
@@ -483,6 +504,8 @@ static int schedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
        /* Always start in the inactive schedule. */
        list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive);
 
        /* 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;
 
        /* Update claimed usecs per (micro)frame. */
        hcd->periodic_usecs += qh->usecs;
 
@@ -553,6 +576,9 @@ static void deschedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
        int i;
 
        list_del_init(&qh->qh_list_entry);
        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++) {
        /* 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_t *hcd, dwc_otg_qh_t *qh)
  */
 void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_next_periodic_split)
 {
  */
 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)) {
        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_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_n
                        }
                }
        }
                        }
                }
        }
-
-       SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
 }
 
 /**
 }
 
 /**
@@ -759,22 +780,22 @@ int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *qtd,
 {
        struct usb_host_endpoint *ep;
        dwc_otg_qh_t *qh;
 {
        struct usb_host_endpoint *ep;
        dwc_otg_qh_t *qh;
-       unsigned long flags;
        int retval = 0;
 
        struct urb *urb = qtd->urb;
 
        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.
         */
        /*
         * 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) {
        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;
                        goto done;
                }
                ep->hcpriv = qh;
@@ -783,11 +804,11 @@ int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *qtd,
        retval = dwc_otg_hcd_qh_add(dwc_otg_hcd, qh);
        if (retval == 0) {
                list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
        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:
        }
 
  done:
-       SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
-
        return retval;
 }
 
        return retval;
 }
 
index 1744a1f..7b9ae57 100644 (file)
@@ -202,6 +202,7 @@ static inline uint32_t SET_DEBUG_LEVEL( const uint32_t new )
 /** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
  *  mode. */
 #define DBG_HCD_URB    (0x800)
 /** 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)
 
 /** When debug level has any bit set, display debug messages */
 #define DBG_ANY                (0xFF)