From 282bed958b1d830c5f612e3ba19bc83b84116e19 Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 27 Apr 2017 16:24:34 +0100 Subject: [PATCH] dwc_otg: make nak_holdoff work as intended with empty queues 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 | 4 +++- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 10 ++++++---- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 4 ++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c index 0eb335f4bc8ec3..4852d08d542f66 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -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); diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c index b2618c15d1f1f0..608e036be2c948 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c @@ -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; diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c index c2dff94e8e6edd..85a6d431ca54b4 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c @@ -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);