svn commit: r265358 - head/sys/dev/usb/controller
Ganbold Tsagaankhuu
ganbold at gmail.com
Wed May 14 11:05:17 UTC 2014
Hans
On Mon, May 5, 2014 at 7:50 PM, Hans Petter Selasky <hselasky at freebsd.org>wrote:
> 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 ***
>
>
Somehow some of recent dwc_otg changes make Radxa Rock board unable to find
usb flash and mount rootfs.
Part of boot log:
http://pastebin.com/tprWJapH
Now I'm running latest head but no dwc_otg changes (r265357), which works
in my case.
Ganbold
More information about the svn-src-head
mailing list