dwc_otg: fix split transaction data toggle handling around dequeues
authorP33M <p33m@github.com>
Tue, 2 May 2017 15:31:15 +0000 (16:31 +0100)
committerRaspbian kernel package updater <root@raspbian.org>
Sun, 8 Oct 2017 01:08:03 +0000 (01:08 +0000)
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
drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c

index 4852d08d542f66880244d3dac3bbd35e7f4feb4e..37c9d7d38287b616fcd335e80aa4110521fa98ca 100644 (file)
@@ -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)
index 608e036be2c9484465ab836de70129335d3d2d96..718a1accc0c219a1764ce53d291de6a2b6f93608 100644 (file)
@@ -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);