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 ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to