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()
This commit is contained in:
P33M
2017-05-02 16:31:15 +01:00
committed by popcornmix
parent 9a38d7c6e9
commit 97b95063ed
2 changed files with 24 additions and 5 deletions

View 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 * It is possible that the channel has already halted
* but not yet been through the IRQ handler. * but not yet been through the IRQ handler.
*/ */
dwc_otg_hc_halt(hcd->core_if, qh->channel, if (fiq_fsm_enable && (hcd->fiq_state->channel[qh->channel->hc_num].fsm != FIQ_PASSTHROUGH)) {
DWC_OTG_HC_XFER_URB_DEQUEUE); 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) if(microframe_schedule)
hcd->available_host_channels++; hcd->available_host_channels++;
qh->channel = NULL; 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) { if (qh->do_split) {
uint32_t hub_addr, port_addr; uint32_t hub_addr, port_addr;
hc->do_split = 1; hc->do_split = 1;
hc->start_pkt_count = 1;
hc->xact_pos = qtd->isoc_split_pos; hc->xact_pos = qtd->isoc_split_pos;
/* We don't need to do complete splits anymore */ /* We don't need to do complete splits anymore */
// if(fiq_fsm_enable) // if(fiq_fsm_enable)

View 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_qh_t *qh = hc->qh;
dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; 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; 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; int hostchannels = 0;
fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm);
hostchannels = hcd->available_host_channels; hostchannels = hcd->available_host_channels;
if (hc->halt_pending) { if (hc->halt_pending) {
/* Dequeue: The FIQ was allowed to complete the transfer but state has been cleared. */ /* 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); release_channel(hcd, hc, NULL, hc->halt_status);
return; 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 = dwc_otg_hcd->hc_ptr_array[num];
hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
/* We are responding to a channel disable. Driver /* A dequeue was issued for this transfer. Our QTD has gone away
* state is cleared - 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; return 1;
} }
qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);