From owner-svn-src-all@FreeBSD.ORG Mon May 5 11:50:52 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id E58DA971; Mon, 5 May 2014 11:50:52 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id CFA741489; Mon, 5 May 2014 11:50:52 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s45BoqEw055516; Mon, 5 May 2014 11:50:52 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s45BoqID055514; Mon, 5 May 2014 11:50:52 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201405051150.s45BoqID055514@svn.freebsd.org> From: Hans Petter Selasky Date: Mon, 5 May 2014 11:50:52 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r265358 - head/sys/dev/usb/controller X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 05 May 2014 11:50:53 -0000 Author: hselasky Date: Mon May 5 11:50:52 2014 New Revision: 265358 URL: http://svnweb.freebsd.org/changeset/base/265358 Log: Improve DWC OTG USB host side support for isochronous FULL and HIGH speed data traffic going directly to a USB device or through a so-called USB transaction translator. Add checks that we are not overusing the TX FIFO. MFC after: 2 weeks Modified: head/sys/dev/usb/controller/dwc_otg.c head/sys/dev/usb/controller/dwc_otg.h head/sys/dev/usb/controller/dwc_otgreg.h Modified: head/sys/dev/usb/controller/dwc_otg.c ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.c Mon May 5 11:30:45 2014 (r265357) +++ head/sys/dev/usb/controller/dwc_otg.c Mon May 5 11:50:52 2014 (r265358) @@ -207,6 +207,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc * /* reset active endpoints */ sc->sc_active_rx_ep = 0; + /* reset TX size */ + sc->sc_tx_cur_size = 0; + + /* reset TT info */ + memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); + fifo_size /= 2; DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, @@ -215,19 +221,17 @@ dwc_otg_init_fifo(struct dwc_otg_softc * tx_start += fifo_size; + for (x = 0; x != sc->sc_host_ch_max; x++) { + /* enable all needed interrupts */ + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_DEFAULT_MASK); + } + DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ, ((fifo_size / 4) << 16) | (tx_start / 4)); - for (x = 0; x != sc->sc_host_ch_max; x++) { - /* enable interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), - HCINT_STALL | HCINT_BBLERR | - HCINT_XACTERR | - HCINT_NAK | HCINT_ACK | HCINT_NYET | - HCINT_CHHLTD | HCINT_FRMOVRUN | - HCINT_DATATGLERR); - } + /* store maximum TX FIFO size */ + sc->sc_tx_max_size = fifo_size; /* enable host channel interrupts */ DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, @@ -309,6 +313,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc * } else { /* reset active endpoints */ sc->sc_active_rx_ep = 0; + + /* reset TX size */ + sc->sc_tx_cur_size = 0; + + /* reset TT info */ + memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); } return (0); } @@ -376,9 +386,9 @@ dwc_otg_pull_down(struct dwc_otg_softc * static void dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc) { - if (sc->sc_irq_mask & GINTSTS_SOF) + if (sc->sc_irq_mask & GINTMSK_SOFMSK) return; - sc->sc_irq_mask |= GINTSTS_SOF; + sc->sc_irq_mask |= GINTMSK_SOFMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } @@ -506,6 +516,7 @@ dwc_otg_clear_hcint(struct dwc_otg_softc { uint32_t hcint; + /* clear all pending interrupts */ hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint); @@ -513,6 +524,10 @@ dwc_otg_clear_hcint(struct dwc_otg_softc sc->sc_chan_state[x].hcint = 0; } +/* + * This function waits until a DWC OTG host channel is ready to be + * used again: + */ static uint8_t dwc_otg_host_channel_wait(struct dwc_otg_td *td) { @@ -545,6 +560,9 @@ dwc_otg_host_channel_wait(struct dwc_otg sc->sc_chan_state[td->channel].allocated = 0; sc->sc_chan_state[x].allocated = 1; + sc->sc_chan_state[x].tx_size = + sc->sc_chan_state[td->channel].tx_size; + if (sc->sc_chan_state[td->channel].suspended) { sc->sc_chan_state[td->channel].suspended = 0; sc->sc_chan_state[x].suspended = 1; @@ -579,6 +597,7 @@ static uint8_t dwc_otg_host_channel_alloc(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; + uint32_t tx_size; uint8_t x; uint8_t max_channel; @@ -591,9 +610,25 @@ dwc_otg_host_channel_alloc(struct dwc_ot if ((td->hcchar & HCCHAR_EPNUM_MASK) == 0) { max_channel = 1; x = 0; + tx_size = td->max_packet_size; + if ((sc->sc_tx_cur_size + tx_size) > sc->sc_tx_max_size) { + DPRINTF("Too little FIFO space\n"); + return (1); /* too little FIFO */ + } } else { max_channel = sc->sc_host_ch_max; x = 1; + if ((td->hcchar & HCCHAR_EPDIR) == HCCHAR_EPDIR_OUT) { + tx_size = td->max_packet_size; + if (td->hcsplt != 0 && tx_size > HCSPLT_XACTLEN_MAX) + tx_size = HCSPLT_XACTLEN_MAX; + if ((sc->sc_tx_cur_size + tx_size) > sc->sc_tx_max_size) { + DPRINTF("Too little FIFO space\n"); + return (1); /* too little FIFO */ + } + } else { + tx_size = 0; + } } for (; x != max_channel; x++) { @@ -604,6 +639,10 @@ dwc_otg_host_channel_alloc(struct dwc_ot continue; sc->sc_chan_state[x].allocated = 1; + sc->sc_chan_state[x].tx_size = tx_size; + + /* keep track of used FIFO */ + sc->sc_tx_cur_size += tx_size; /* clear interrupts */ dwc_otg_clear_hcint(sc, x); @@ -663,6 +702,9 @@ dwc_otg_host_channel_free(struct dwc_otg sc->sc_chan_state[x].allocated = 0; sc->sc_chan_state[x].suspended = 0; + /* keep track of used FIFO */ + sc->sc_tx_cur_size -= sc->sc_chan_state[x].tx_size; + /* ack any pending messages */ if (sc->sc_last_rx_status != 0 && GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) { @@ -724,6 +766,8 @@ dwc_otg_host_setup_tx(struct dwc_otg_td switch (td->state) { case DWC_CHAN_ST_START: + if (!dwc_otg_host_channel_wait(td)) + break; goto send_pkt; case DWC_CHAN_ST_WAIT_ANE: @@ -731,6 +775,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto send_pkt; } if (hcint & (HCINT_ACK | HCINT_NYET)) { @@ -739,14 +784,17 @@ dwc_otg_host_setup_tx(struct dwc_otg_td td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; + td->tt_scheduled = 0; return (0); /* complete */ } break; + case DWC_CHAN_ST_WAIT_S_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto send_pkt; } if (hcint & (HCINT_ACK | HCINT_NYET)) { @@ -755,6 +803,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td goto send_cpkt; } break; + case DWC_CHAN_ST_WAIT_C_ANE: if (hcint & HCINT_NYET) { if (!dwc_otg_host_channel_wait(td)) @@ -765,6 +814,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto send_pkt; } if (hcint & HCINT_ACK) { @@ -776,8 +826,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_td return (0); /* complete */ } break; - case DWC_CHAN_ST_TX_PKT_SYNC: - goto send_pkt_sync; + + case DWC_CHAN_ST_WAIT_C_PKT: + if (!dwc_otg_host_channel_wait(td)) + break; + goto send_cpkt; + default: break; } @@ -789,19 +843,13 @@ send_pkt: return (0); /* complete */ } -send_pkt_sync: if (td->hcsplt != 0) { - uint32_t count; - - count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7; - /* check for not first microframe */ - if (count != 0) { - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* set state */ - td->state = DWC_CHAN_ST_TX_PKT_SYNC; - dwc_otg_host_channel_free(td); - return (1); /* busy */ + /* Wait for our turn, if TT transfer */ + if (td->tt_scheduled == 0 || + (sc->sc_last_frame_num & 7) < td->tt_start_slot) { + /* set return state */ + td->state = DWC_CHAN_ST_START; + goto tt_wait; } td->hcsplt &= ~HCSPLT_COMPSPLT; @@ -835,6 +883,16 @@ send_pkt_sync: return (1); /* busy */ send_cpkt: + /* Wait for our turn, if TT transfer */ + if (td->tt_scheduled == 0 || + (sc->sc_last_frame_num & 7) < td->tt_complete_slot) { + /* set return state */ + td->state = DWC_CHAN_ST_WAIT_C_PKT; + goto tt_wait; + } + /* wait until next slot before trying again */ + td->tt_complete_slot++; + td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; @@ -848,7 +906,14 @@ send_cpkt: /* must enable channel before writing data to FIFO */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); + return (1); /* busy */ +tt_wait: + /* enable SOF interrupt */ + dwc_otg_enable_sof_irq(sc); + + /* free allocated channel */ + dwc_otg_host_channel_free(td); return (1); /* busy */ } @@ -984,6 +1049,25 @@ not_complete: } static uint8_t +dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct dwc_otg_td *td) +{ + uint8_t delta; + + delta = sc->sc_tmr_val - td->tmr_val; + if (delta >= 128) + return (1); /* busy */ + + td->tmr_val = sc->sc_tmr_val + td->tmr_res; + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + return (0); +} + +static uint8_t dwc_otg_host_rate_check(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; @@ -992,31 +1076,30 @@ dwc_otg_host_rate_check(struct dwc_otg_t /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); + if (td->channel < DWC_OTG_MAX_CHANNELS && + sc->sc_chan_state[td->channel].suspended) + goto busy; + ep_type = ((td->hcchar & HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT); - if (sc->sc_chan_state[td->channel].suspended) - goto busy; - if (ep_type == UE_ISOCHRONOUS) { - if (td->tmr_val & 1) - td->hcchar |= HCCHAR_ODDFRM; - else - td->hcchar &= ~HCCHAR_ODDFRM; - td->tmr_val += td->tmr_res; - } else if (ep_type == UE_INTERRUPT) { - uint8_t delta; - delta = sc->sc_tmr_val - td->tmr_val; - if (delta >= 128) + /* non TT isochronous traffic */ + if ((td->tmr_val != 0) || + (sc->sc_last_frame_num & (td->tmr_res - 1))) { + /* enable SOF interrupt */ + dwc_otg_enable_sof_irq(sc); goto busy; - td->tmr_val = sc->sc_tmr_val + td->tmr_res; + } + td->tmr_val = 1; /* executed */ + td->toggle = 0; + + } else if (ep_type == UE_INTERRUPT) { + /* non TT interrupt traffic */ + return (dwc_otg_host_rate_check_interrupt(sc, td)); } else if (td->did_nak != 0) { goto busy; - } - - if (ep_type == UE_ISOCHRONOUS) { - td->toggle = 0; } else if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; @@ -1065,8 +1148,10 @@ dwc_otg_host_data_rx(struct dwc_otg_td * DPRINTF("CH=%d ERROR\n", td->channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { - td->error_any = 1; - return (0); /* complete */ + if (ep_type != UE_ISOCHRONOUS) { + td->error_any = 1; + return (0); /* complete */ + } } } @@ -1103,25 +1188,42 @@ dwc_otg_host_data_rx(struct dwc_otg_td * break; } - td->toggle ^= 1; - /* get the packet byte count */ count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); - /* verify the packet byte count */ - if (count != td->max_packet_size) { - if (count < td->max_packet_size) { - /* we have a short packet */ - td->short_pkt = 1; - td->got_short = 1; + /* check for isochronous transfer or high-speed bandwidth endpoint */ + if (ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) { + if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != GRXSTSRD_DPID_DATA0) { + td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE; } else { - /* invalid USB packet */ - td->error_any = 1; + td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; + + /* verify the packet byte count */ + if (count < td->max_packet_size) { + /* we have a short packet */ + td->short_pkt = 1; + td->got_short = 1; + } + } + td->toggle = 0; + } else { + /* verify the packet byte count */ + if (count != td->max_packet_size) { + if (count < td->max_packet_size) { + /* we have a short packet */ + td->short_pkt = 1; + td->got_short = 1; + } else { + /* invalid USB packet */ + td->error_any = 1; - /* release FIFO */ - dwc_otg_common_rx_ack(sc); - return (0); /* we are complete */ + /* release FIFO */ + dwc_otg_common_rx_ack(sc); + return (0); /* we are complete */ + } } + td->toggle ^= 1; + td->tt_scheduled = 0; } /* verify the packet byte count */ @@ -1144,7 +1246,6 @@ dwc_otg_host_data_rx(struct dwc_otg_td * break; default: - DPRINTF("OTHER\n"); break; } /* release FIFO */ @@ -1153,6 +1254,8 @@ dwc_otg_host_data_rx(struct dwc_otg_td * check_state: switch (td->state) { case DWC_CHAN_ST_START: + if (!dwc_otg_host_channel_wait(td)) + break; if (td->hcsplt != 0) goto receive_spkt; else @@ -1164,6 +1267,7 @@ check_state: break; td->did_nak = 1; + td->tt_scheduled = 0; if (td->hcsplt != 0) goto receive_spkt; else @@ -1171,11 +1275,13 @@ check_state: } if (!(hcint & HCINT_SOFTWARE_ONLY)) { if (hcint & HCINT_NYET) { - if (td->hcsplt != 0) { - if (!dwc_otg_host_channel_wait(td)) - break; - goto receive_pkt; + if (ep_type == UE_ISOCHRONOUS) { + /* we missed the service interval */ + return (0); /* complete */ } + if (!dwc_otg_host_channel_wait(td)) + break; + goto receive_pkt; } break; } @@ -1183,29 +1289,44 @@ check_state: if (!dwc_otg_host_channel_wait(td)) break; - /* check if we are complete */ - if ((td->remainder == 0) || (td->got_short != 0)) { - if (td->short_pkt) + if (ep_type == UE_ISOCHRONOUS) { + /* check if we are complete */ + if ((td->remainder == 0) || + (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) return (0); /* complete */ - /* - * Else need to receive a zero length - * packet. - */ - } - if (td->hcsplt != 0) - goto receive_spkt; - else goto receive_pkt; + } else { + /* check if we are complete */ + if ((td->remainder == 0) || (td->got_short != 0)) { + if (td->short_pkt) + return (0); /* complete */ + + /* + * Else need to receive a zero length + * packet. + */ + } + td->tt_scheduled = 0; + if (td->hcsplt != 0) + goto receive_spkt; + else + goto receive_pkt; + } } break; case DWC_CHAN_ST_WAIT_S_ANE: + /* + * NOTE: The DWC OTG hardware provides a fake ACK in + * case of interrupt and isochronous transfers: + */ if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto receive_spkt; } if (hcint & (HCINT_ACK | HCINT_NYET)) { @@ -1215,100 +1336,91 @@ check_state: } break; - case DWC_CHAN_ST_RX_PKT: + case DWC_CHAN_ST_WAIT_C_PKT: + if (!dwc_otg_host_channel_wait(td)) + break; goto receive_pkt; - case DWC_CHAN_ST_RX_SPKT: - goto receive_spkt; - - case DWC_CHAN_ST_RX_SPKT_SYNC: - goto receive_spkt_sync; - default: break; } goto busy; receive_pkt: - if (td->hcsplt != 0) { - count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7; - - /* check for even microframes */ - if (count == td->curr_frame) { - td->state = DWC_CHAN_ST_RX_PKT; - dwc_otg_host_channel_free(td); - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - goto busy; - } else if (count == 0) { - /* check for start split timeout */ - goto receive_spkt; + if (td->hcsplt != 0) { + /* Wait for our turn, if TT transfer */ + if (td->tt_scheduled == 0 || + (sc->sc_last_frame_num & 7) < td->tt_complete_slot) { + /* set return state */ + td->state = DWC_CHAN_ST_WAIT_C_PKT; + goto tt_wait; + } + /* wait until next slot before trying again */ + td->tt_complete_slot++; + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; } - - td->curr_frame = count; td->hcsplt |= HCSPLT_COMPSPLT; - } else if (dwc_otg_host_rate_check(td)) { - td->state = DWC_CHAN_ST_RX_PKT; + count = HCSPLT_XACTLEN_MAX; + } else if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN && + dwc_otg_host_rate_check(td)) { + td->state = DWC_CHAN_ST_START; dwc_otg_host_channel_free(td); goto busy; + } else { + count = td->max_packet_size; } - td->state = DWC_CHAN_ST_WAIT_ANE; /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), - (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) | + (count << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); + /* send ASAP */ + if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1)) + td->hcchar |= HCCHAR_ODDFRM; + else + td->hcchar &= ~HCCHAR_ODDFRM; + hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); - goto busy; receive_spkt: - if (dwc_otg_host_rate_check(td)) { - td->state = DWC_CHAN_ST_RX_SPKT; - dwc_otg_host_channel_free(td); - goto busy; - } - -receive_spkt_sync: - if (ep_type == UE_INTERRUPT || - ep_type == UE_ISOCHRONOUS) { - count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7; - td->curr_frame = count; - - /* check for non-zero microframe */ - if (count != 0) { - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* set state */ - td->state = DWC_CHAN_ST_RX_SPKT_SYNC; - dwc_otg_host_channel_free(td); - goto busy; - } - } else { - count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7; - td->curr_frame = count; - - /* check for two last frames */ - if (count >= 6) { - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* set state */ - td->state = DWC_CHAN_ST_RX_SPKT_SYNC; + /* Wait for our turn, if TT transfer */ + if (td->tt_scheduled == 0) { + if (ep_type == UE_INTERRUPT) { + td->state = DWC_CHAN_ST_START; dwc_otg_host_channel_free(td); goto busy; } + /* set return state */ + td->state = DWC_CHAN_ST_START; + goto tt_wait; + } + if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) { + /* set return state */ + td->state = DWC_CHAN_ST_START; + goto tt_wait; } + /* send ASAP */ + if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1)) + td->hcchar |= HCCHAR_ODDFRM; + else + td->hcchar &= ~HCCHAR_ODDFRM; + td->hcsplt &= ~HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_S_ANE; @@ -1324,7 +1436,14 @@ receive_spkt_sync: /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); + goto busy; + +tt_wait: + /* enable SOF interrupt */ + dwc_otg_enable_sof_irq(sc); + /* free allocated channel */ + dwc_otg_host_channel_free(td); busy: return (1); /* busy */ } @@ -1497,6 +1616,8 @@ dwc_otg_host_data_tx(struct dwc_otg_td * switch (td->state) { case DWC_CHAN_ST_START: + if (!dwc_otg_host_channel_wait(td)) + break; goto send_pkt; case DWC_CHAN_ST_WAIT_ANE: @@ -1504,6 +1625,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto send_pkt; } if (hcint & (HCINT_ACK | HCINT_NYET)) { @@ -1513,6 +1635,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle ^= 1; + td->tt_scheduled = 0; /* check remainder */ if (td->remainder == 0) { @@ -1527,11 +1650,13 @@ dwc_otg_host_data_tx(struct dwc_otg_td * goto send_pkt; } break; + case DWC_CHAN_ST_WAIT_S_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto send_pkt; } if (hcint & (HCINT_ACK | HCINT_NYET)) { @@ -1540,6 +1665,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * goto send_cpkt; } break; + case DWC_CHAN_ST_WAIT_C_ANE: if (hcint & HCINT_NYET) { if (!dwc_otg_host_channel_wait(td)) @@ -1550,6 +1676,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * if (!dwc_otg_host_channel_wait(td)) break; td->did_nak = 1; + td->tt_scheduled = 0; goto send_pkt; } if (hcint & HCINT_ACK) { @@ -1558,6 +1685,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle ^= 1; + td->tt_scheduled = 0; /* check remainder */ if (td->remainder == 0) { @@ -1570,64 +1698,204 @@ dwc_otg_host_data_tx(struct dwc_otg_td * } break; - case DWC_CHAN_ST_TX_PKT: - goto send_pkt; + case DWC_CHAN_ST_WAIT_C_PKT: + if (!dwc_otg_host_channel_wait(td)) + break; + goto send_cpkt; - case DWC_CHAN_ST_TX_PKT_SYNC: - goto send_pkt_sync; + case DWC_CHAN_ST_TX_WAIT_ISOC: - case DWC_CHAN_ST_TX_CPKT: - goto send_cpkt; + /* Check if isochronous OUT traffic is complete */ + if ((hcint & HCINT_ACK) == 0) + break; + + td->offset += td->tx_bytes; + td->remainder -= td->tx_bytes; + /* Update split token according to specification */ + if (td->hcsplt != 0) { + if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) + td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE; + } else if (td->max_packet_count > 1) { + td->tt_xactpos++; + } + + dwc_otg_host_channel_disable(sc, td->channel); + + if (td->remainder == 0) + return (0); /* complete */ + + td->state = DWC_CHAN_ST_TX_PKT_ISOC; + + /* FALLTHROUGH */ + + case DWC_CHAN_ST_TX_PKT_ISOC: + if (!dwc_otg_host_channel_wait(td)) + break; + + if (td->hcsplt != 0) { + if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) + goto tt_wait; + /* packets must be 125us apart */ + td->tt_start_slot++; + } + goto send_isoc_pkt; default: break; } goto busy; send_pkt: - if (dwc_otg_host_rate_check(td)) { - td->state = DWC_CHAN_ST_TX_PKT; + if (td->hcsplt != 0) { + /* Wait for our turn, if TT transfer */ + if (td->tt_scheduled == 0) { + if (ep_type == UE_INTERRUPT) { + td->state = DWC_CHAN_ST_START; + dwc_otg_host_channel_free(td); + goto busy; + } + /* set return state */ + td->state = DWC_CHAN_ST_START; + goto tt_wait; + } + if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) { + /* set return state */ + td->state = DWC_CHAN_ST_START; + goto tt_wait; + } + + /* packets must be 125us apart */ + td->tt_start_slot++; + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + } else if (dwc_otg_host_rate_check(td)) { + td->state = DWC_CHAN_ST_START; dwc_otg_host_channel_free(td); goto busy; } -send_pkt_sync: - if (td->hcsplt != 0) { - count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7; - /* check for first or last microframe */ - if (count == 7 || count == 0) { - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* set state */ - td->state = DWC_CHAN_ST_TX_PKT_SYNC; - dwc_otg_host_channel_free(td); - goto busy; + if (ep_type == UE_ISOCHRONOUS) { +send_isoc_pkt: + /* Isochronous OUT transfers don't have any ACKs */ + td->state = DWC_CHAN_ST_TX_WAIT_ISOC; + td->hcsplt &= ~HCSPLT_COMPSPLT; + if (td->hcsplt != 0) { + /* get maximum transfer length */ + count = td->remainder; + + /* Update split token according to specification */ + if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) { + if (count <= HCSPLT_XACTLEN_MAX) + td->tt_xactpos = HCSPLT_XACTPOS_ALL; + else + count = HCSPLT_XACTLEN_MAX; + } else if (td->tt_xactpos == HCSPLT_XACTPOS_MIDDLE) { + if (count <= HCSPLT_XACTLEN_MAX) + td->tt_xactpos = HCSPLT_XACTPOS_LAST; + else + count = HCSPLT_XACTLEN_MAX; + } + + /* Update transaction position */ + td->hcsplt &= ~HCSPLT_XACTPOS_MASK; + td->hcsplt |= ((uint32_t)td->tt_xactpos << HCSPLT_XACTPOS_SHIFT); + } else { + /* send one packet at a time */ + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } } + } else if (td->hcsplt != 0) { td->hcsplt &= ~HCSPLT_COMPSPLT; + + /* Wait for ACK/NAK/ERR from TT */ td->state = DWC_CHAN_ST_WAIT_S_ANE; + + /* send one packet at a time */ + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } } else { + /* Wait for ACK/NAK/STALL from device */ td->state = DWC_CHAN_ST_WAIT_ANE; - } - /* send one packet at a time */ - count = td->max_packet_size; - if (td->remainder < count) { - /* we have a short packet */ - td->short_pkt = 1; - count = td->remainder; + /* send one packet at a time */ + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } } - /* TODO: HCTSIZ_DOPNG */ + /* check for High-Speed multi-packets */ + if ((td->hcsplt == 0) && (td->max_packet_count > 1)) { + if (td->npkt == 0) { + if (td->remainder >= (3 * td->max_packet_size)) + td->npkt = 3; + else if (td->remainder >= (2 * td->max_packet_size)) + td->npkt = 2; + else + td->npkt = 1; - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), - (count << HCTSIZ_XFERSIZE_SHIFT) | - (1 << HCTSIZ_PKTCNT_SHIFT) | - (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : - (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); + if (td->npkt > td->max_packet_count) + td->npkt = td->max_packet_count; + + td->tt_xactpos = 1; /* overload */ + } + if (td->tt_xactpos == td->npkt) { + if (td->npkt == 1) { + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | + (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)); + } else if (td->npkt == 2) { + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | + (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT)); + } else { + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | + (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT)); + } + td->npkt = 0; + } else { + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | + (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT)); + } + } else { + /* TODO: HCTSIZ_DOPNG */ + + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | + (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : + (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); + } DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); + /* send ASAP */ + if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1)) + td->hcchar |= HCCHAR_ODDFRM; + else + td->hcchar &= ~HCCHAR_ODDFRM; + hcchar = td->hcchar; hcchar &= ~HCCHAR_EPDIR_IN; @@ -1651,18 +1919,20 @@ send_pkt_sync: /* store number of bytes transmitted */ td->tx_bytes = count; - goto busy; send_cpkt: - count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7; - /* check for first microframe */ - if (count == 0) { - /* send packet again */ - goto send_pkt; + /* Wait for our turn, if TT transfer */ + if (td->tt_scheduled == 0 || + (sc->sc_last_frame_num & 7) < td->tt_complete_slot) { + /* set return state */ + td->state = DWC_CHAN_ST_WAIT_C_PKT; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***