From 34833d215c1400b5987c7d949a95e4ba4ab9defe Mon Sep 17 00:00:00 2001 From: P33M Date: Tue, 2 May 2017 16:31:15 +0100 Subject: [PATCH] dwc_otg: fix split transaction data toggle handling around dequeues See https://github.com/raspberrypi/linux/issues/1709 Fix several issues regarding endpoint state when URBs are dequeued - If the HCD is disconnected, flush FIQ-enabled channels properly - Save the data toggle state for bulk endpoints if the last transfer from an endpoint where URBs were dequeued returned a data packet - Reset hc->start_pkt_count properly in assign_and_init_hc() --- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 10 ++++++++-- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 19 ++++++++++++++++--- 2 files changed, 24 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 4852d08d542f..37c9d7d38287 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -193,8 +193,13 @@ static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) * It is possible that the channel has already halted * but not yet been through the IRQ handler. */ - dwc_otg_hc_halt(hcd->core_if, qh->channel, - DWC_OTG_HC_XFER_URB_DEQUEUE); + if (fiq_fsm_enable && (hcd->fiq_state->channel[qh->channel->hc_num].fsm != FIQ_PASSTHROUGH)) { + qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE; + qh->channel->halt_pending = 1; + } else { + dwc_otg_hc_halt(hcd->core_if, qh->channel, + DWC_OTG_HC_XFER_URB_DEQUEUE); + } if(microframe_schedule) hcd->available_host_channels++; qh->channel = NULL; @@ -1270,6 +1275,7 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) if (qh->do_split) { uint32_t hub_addr, port_addr; hc->do_split = 1; + hc->start_pkt_count = 1; hc->xact_pos = qtd->isoc_split_pos; /* We don't need to do complete splits anymore */ // if(fiq_fsm_enable) 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 608e036be2c9..718a1accc0c2 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c @@ -2378,12 +2378,20 @@ void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) dwc_otg_qh_t *qh = hc->qh; dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; + hctsiz_data_t hctsiz = hcd->fiq_state->channel[num].hctsiz_copy; int hostchannels = 0; 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. */ + if (st->fsm == FIQ_NP_SPLIT_DONE && hcint.b.xfercomp && qh->ep_type == UE_BULK) { + if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { + qh->data_toggle = DWC_OTG_HC_PID_DATA1; + } else { + qh->data_toggle = DWC_OTG_HC_PID_DATA0; + } + } release_channel(hcd, hc, NULL, hc->halt_status); return; } @@ -2641,10 +2649,15 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) hc = dwc_otg_hcd->hc_ptr_array[num]; hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { - /* We are responding to a channel disable. Driver - * state is cleared - our qtd has gone away. + /* A dequeue was issued for this transfer. Our QTD has gone away + * but in the case of a FIQ transfer, the transfer would have run + * to completion. */ - release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status); + if (fiq_fsm_enable && dwc_otg_hcd->fiq_state->channel[num].fsm != FIQ_PASSTHROUGH) { + dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num); + } else { + release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status); + } return 1; } qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); -- 2.30.2