Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 May 2014 19:05:16 +0800
From:      Ganbold Tsagaankhuu <ganbold@gmail.com>
To:        Hans Petter Selasky <hselasky@freebsd.org>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org
Subject:   Re: svn commit: r265358 - head/sys/dev/usb/controller
Message-ID:  <CAGtf9xPgA0Urb7bZRoiRVFGBWCjHHFHU7wRYd_HrjBR2Jdj0cg@mail.gmail.com>
In-Reply-To: <201405051150.s45BoqID055514@svn.freebsd.org>
References:  <201405051150.s45BoqID055514@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Hans

On Mon, May 5, 2014 at 7:50 PM, Hans Petter Selasky <hselasky@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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAGtf9xPgA0Urb7bZRoiRVFGBWCjHHFHU7wRYd_HrjBR2Jdj0cg>