dwc_otg: make nak_holdoff work as intended with empty queues
authorP33M <p33m@github.com>
Thu, 27 Apr 2017 15:24:34 +0000 (16:24 +0100)
committerRaspbian kernel package updater <root@raspbian.org>
Sat, 31 Mar 2018 14:53:34 +0000 (15:53 +0100)
If URBs reading from non-periodic split endpoints were dequeued and
the last transfer from the endpoint was a NAK handshake, the resulting
qh->nak_frame value was stale which would result in unnecessarily long
polling intervals for the first subsequent transfer with a fresh URB.

Fixup qh->nak_frame in dwc_otg_hcd_urb_dequeue and also guard against
a case where a single URB is submitted to the endpoint, a NAK was
received on the transfer immediately prior to receiving data and the
device subsequently resubmits another URB past the qh->nak_frame interval.

Fixes https://github.com/raspberrypi/linux/issues/1709

drivers/usb/host/dwc_otg/dwc_otg_hcd.c
drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c

index 0eb335f4bc8ec30fa68f830fc3e382330302686e..4852d08d542f66880244d3dac3bbd35e7f4feb4e 100644 (file)
@@ -616,7 +616,7 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
                        if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) {
                                qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
                                qh->channel->halt_pending = 1;
-                               hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED;
+                               //hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED;
                        } else {
                                dwc_otg_hc_halt(hcd->core_if, qh->channel,
                                                DWC_OTG_HC_XFER_URB_DEQUEUE);
@@ -634,6 +634,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
                     hcd->core_if->dma_desc_enable?"DMA ":"");
        if (!hcd->core_if->dma_desc_enable) {
                uint8_t b = urb_qtd->in_process;
+               if (nak_holdoff && qh->do_split && dwc_qh_is_non_per(qh))
+                       qh->nak_frame = 0xFFFF;
                dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
                if (b) {
                        dwc_otg_hcd_qh_deactivate(hcd, qh, 0);
index b2618c15d1f1f0b062d4146672de5ae50e9e2d33..608e036be2c9484465ab836de70129335d3d2d96 100644 (file)
@@ -2382,15 +2382,17 @@ void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num)
        fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm);
 
        hostchannels = hcd->available_host_channels;
+       if (hc->halt_pending) {
+               /* Dequeue: The FIQ was allowed to complete the transfer but state has been cleared. */
+               release_channel(hcd, hc, NULL, hc->halt_status);
+               return;
+       }
        switch (st->fsm) {
        case FIQ_TEST:
                break;
 
        case FIQ_DEQUEUE_ISSUED:
-               /* hc_halt was called. QTD no longer exists. */
-               /* TODO: for a nonperiodic split transaction, need to issue a
-                * CLEAR_TT_BUFFER hub command if we were in the start-split phase.
-                */
+               /* Handled above, but keep for posterity */
                release_channel(hcd, hc, NULL, hc->halt_status);
                break;
 
index c2dff94e8e6edd22e4427aaa1eac7aad972cb6bd..85a6d431ca54b47dc10573aa72d1ad69d06f2e36 100644 (file)
@@ -793,6 +793,10 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
                        /* Add back to inactive non-periodic schedule. */
                        dwc_otg_hcd_qh_add(hcd, qh);
                        //hcd->fiq_state->kick_np_queues = 1;
+               } else {
+                       if(nak_holdoff && qh->do_split) {
+                               qh->nak_frame = 0xFFFF;
+                       }
                }
        } else {
                uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);