Author: hselasky
Date: Fri Sep 14 07:52:57 2012
New Revision: 240482
URL: http://svn.freebsd.org/changeset/base/240482

Log:
  DWC OTG improvements. Implement full support for SPLIT transactions, in other
  words FULL/LOW speed devices through HIGH speed HUBs. Improve support for
  suspend and resume in host mode.

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       Fri Sep 14 05:24:06 2012        
(r240481)
+++ head/sys/dev/usb/controller/dwc_otg.c       Fri Sep 14 07:52:57 2012        
(r240482)
@@ -510,7 +510,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot
        }
 
        for (; x != max_channel; x++) {
-               if (sc->sc_hcchar[x] == 0) {
+               if (sc->sc_chan_state[x].hcchar == 0) {
 
                        /* check if channel is enabled */
                        temp = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
@@ -519,7 +519,8 @@ dwc_otg_host_channel_alloc(struct dwc_ot
                                continue;
                        }
 
-                       sc->sc_hcchar[x] = td->hcchar;
+                       sc->sc_chan_state[x].hcchar = td->hcchar;
+                       sc->sc_chan_state[x].hcsplt = td->hcsplt;
 
                        DPRINTF("HCCHAR=0x%08x(0x%08x) HCSPLT=0x%08x\n",
                            td->hcchar, temp, td->hcsplt);
@@ -528,14 +529,14 @@ dwc_otg_host_channel_alloc(struct dwc_ot
                        temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
                        DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
 
-                       /* enable interrupts */
-                       DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
-                           HCINT_STALL | HCINT_BBLERR |
-                           HCINT_AHBERR | HCINT_XACTERR |
-                           HCINT_XFERCOMPL | HCINT_NAK);
+                       /* clear buffered interrupts */
+                       sc->sc_chan_state[x].hcint = 0;
+
+                       /* clear state */
+                       sc->sc_chan_state[x].state = 0;
 
-                       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), td->hcsplt);
-                       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0);
+                       /* we've requested SOF interrupt */
+                       sc->sc_chan_state[x].sof_requested = 1;
 
                        /* set channel */
                        td->channel = x;
@@ -558,8 +559,6 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
        struct usb_device_request req __aligned(4);
        struct dwc_otg_softc *sc;
        uint32_t temp;
-       uint32_t max_buffer;
-       uint8_t max_frames;
 
        if (dwc_otg_host_channel_alloc(td))
                return (1);             /* busy */
@@ -567,57 +566,103 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
        /* get pointer to softc */
        sc = DWC_OTG_PC2SC(td->pc);
 
-       temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-       DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
+       temp = sc->sc_chan_state[td->channel].hcint;
 
-       DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-           td->channel,
-           temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+       DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+           td->channel, sc->sc_chan_state[td->channel].state, temp,
+           DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
-       if (temp & HCINT_NAK)
-               td->did_nak = 1;
-
        if (temp & HCINT_STALL) {
                td->error_stall = 1;
                td->error_any = 1;
                return (0);             /* complete */
        }
 
-       if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) {
+       if (temp & (HCINT_BBLERR | HCINT_XACTERR)) {
                td->error_any = 1;
                return (0);             /* complete */
        }
 
-       if (temp & HCINT_XFERCOMPL)
-               td->did_complete = 1;
+       if (temp & HCINT_NAK) {
+               if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
+                       return (1);     /* busy */
+               td->sof_val += 1;
+       }
+
+       /* channel must be disabled before we can complete the transfer */
+
+       if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) {
+               uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+               if (hcchar & HCCHAR_CHENA) {
+                       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
+                           HCCHAR_CHENA | HCCHAR_CHDIS);
+               } else {
+                       sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD;
+               }
+       }
 
-       if (td->did_complete) {
-               if (td->did_nak == 0) {
+       switch (sc->sc_chan_state[td->channel].state) {
+       case DWC_CHAN_ST_START:
+               if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+                       sc->sc_chan_state[td->channel].hcsplt &=
+                           ~HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_S_ANE;
+               } else {
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_ANE;
+               }
+               goto send_pkt;
+       case DWC_CHAN_ST_WAIT_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto send_pkt;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
                        td->offset += td->tx_bytes;
                        td->remainder -= td->tx_bytes;
                        td->toggle = 1;
+                       sc->sc_chan_state[td->channel].hcint = 0;
+                       sc->sc_chan_state[td->channel].state = 0;
                        return (0);     /* complete */
-               } else {
-                       if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
-                               return (1);     /* busy */
-                       td->sof_val += 1;
                }
-       } else {
-               return (1);     /* busy */
+               break;
+       case DWC_CHAN_ST_WAIT_S_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto send_pkt;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
+                       sc->sc_chan_state[td->channel].hcsplt |= 
HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_C_ANE;
+                       goto send_cpkt;
+               }
+               break;
+       case DWC_CHAN_ST_WAIT_C_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto send_cpkt;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
+                       td->offset += td->tx_bytes;
+                       td->remainder -= td->tx_bytes;
+                       td->toggle = 1;
+                       sc->sc_chan_state[td->channel].hcint = 0;
+                       sc->sc_chan_state[td->channel].state = 0;
+                       return (0);     /* complete */
+               }
+               break;
+       default:
+               break;
        }
-       temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS);
-
-       DPRINTF("HPTXSTS=0x%08x\n", temp);
-
-       max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK);
-       max_frames = (temp & HPTXSTS_PTXQSPCAVAIL_MASK)
-           >> HPTXSTS_PTXQSPCAVAIL_SHIFT;
-
-       max_buffer = max_buffer - (max_buffer % td->max_packet_size);
-       if (max_buffer == 0 || max_frames == 0)
-               return (1);     /* busy */
+       return (1);             /* busy */
 
+send_pkt:
        if (sizeof(req) != td->remainder) {
                td->error_any = 1;
                return (0);             /* complete */
@@ -630,7 +675,10 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
            (1 << HCTSIZ_PKTCNT_SHIFT) |
            (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-       temp = sc->sc_hcchar[td->channel];
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+           sc->sc_chan_state[td->channel].hcsplt);
+
+       temp = sc->sc_chan_state[td->channel].hcchar;
        temp &= ~HCCHAR_EPDIR_IN;
 
        /* must enable channel before writing data to FIFO */
@@ -640,11 +688,30 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
        bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
            DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
 
-       /* reset statemachine */
-       td->did_complete = 0;
-       td->did_nak = 0;
+       /* store number of bytes transmitted */
        td->tx_bytes = sizeof(req);
 
+       /* clear interrupts */
+       sc->sc_chan_state[td->channel].hcint = 0;
+
+       return (1);     /* busy */
+
+send_cpkt:
+       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+           (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+           sc->sc_chan_state[td->channel].hcsplt);
+
+       temp = sc->sc_chan_state[td->channel].hcchar;
+       temp &= ~HCCHAR_EPDIR_IN;
+
+       /* must enable channel before writing data to FIFO */
+       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
+
+       /* clear interrupts */
+       sc->sc_chan_state[td->channel].hcint = 0;
+
        return (1);     /* busy */
 }
 
@@ -793,21 +860,15 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
        /* get pointer to softc */
        sc = DWC_OTG_PC2SC(td->pc);
 
-       temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-       DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
-
-       DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-           td->channel,
-           temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-           DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+       ep_type = ((sc->sc_chan_state[td->channel].hcchar &
+           HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
 
-       if (temp & HCINT_NAK) {
-               td->did_nak = 1;
+       temp = sc->sc_chan_state[td->channel].hcint;
 
-               /* disable channel - will generate a halted event */
-               DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-                   HCCHAR_CHENA | HCCHAR_CHDIS);
-       }
+       DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+           td->channel, sc->sc_chan_state[td->channel].state, temp,
+           DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+           DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
        if (temp & HCINT_STALL) {
                td->error_stall = 1;
@@ -815,11 +876,23 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
                return (0);             /* complete */
        }
 
-       if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) {
+       if (temp & (HCINT_BBLERR | HCINT_XACTERR)) {
                td->error_any = 1;
                return (0);             /* complete */
        }
 
+       /* channel must be disabled before we can complete the transfer */
+
+       if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) {
+               uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+               if (hcchar & HCCHAR_CHENA) {
+                       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
+                           HCCHAR_CHENA | HCCHAR_CHDIS);
+               } else {
+                       sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD;
+               }
+       }
+
        /* check endpoint status */
        if (sc->sc_last_rx_status == 0)
                goto not_complete;
@@ -828,16 +901,6 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
                goto not_complete;
 
        switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
-       case GRXSTSRH_HALTED:
-               DPRINTF("HALTED\n");
-               td->did_complete = 1;
-               break;
-       case GRXSTSRH_IN_COMPLETE:
-               DPRINTF("COMPLETE\n");
-               /* disable channel - will generate a halted event */
-               DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-                   HCCHAR_CHENA | HCCHAR_CHDIS);
-               break;
        case GRXSTSRH_IN_DATA:
 
                DPRINTF("DATA\n");
@@ -878,6 +941,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 
                td->remainder -= count;
                td->offset += count;
+               sc->sc_chan_state[td->channel].hcint |= HCINT_SOFTWARE_ONLY;
                break;
 
        default:
@@ -888,45 +952,102 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
        dwc_otg_common_rx_ack(sc);
 
 not_complete:
-       if (td->did_complete == 0)
+       if (temp & HCINT_SUSPEND_ONLY)
                return (1);     /* busy */
 
-       /* check if we are complete */
-       if ((td->remainder == 0) || (td->got_short != 0)) {
-               if (td->short_pkt) {
-                       /* we are complete */
-                       return (0);
-               }
-               /* else need to receive a zero length packet */
-       }
-
-       temp = sc->sc_hcchar[td->channel];
-
-       ep_type = ((temp & HCCHAR_EPTYPE_MASK) >>
-           HCCHAR_EPTYPE_SHIFT);
-
        if (ep_type == UE_ISOCHRONOUS) {
                if ((sc->sc_sof_val & 0xFF) != td->sof_val)
                        return (1);     /* busy */
                if (td->sof_val & 1)
-                       temp |= HCCHAR_ODDFRM;
+                       sc->sc_chan_state[td->channel].hcchar |= HCCHAR_ODDFRM;
+               else
+                       sc->sc_chan_state[td->channel].hcchar &= ~HCCHAR_ODDFRM;
                td->sof_val += td->sof_res;
-               /* DATA 0 */
-               td->toggle = 0;
        } else if (ep_type == UE_INTERRUPT) {
                if ((sc->sc_sof_val & 0xFF) != td->sof_val)
                        return (1);     /* busy */
                td->sof_val += td->sof_res;
-       } else {
-               if (td->did_nak) {
-                       if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
-                               return (1);     /* busy */
-                       td->sof_val += 1;
+       } else if (temp & HCINT_NAK) {
+               if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
+                       return (1);     /* busy */
+               td->sof_val += 1;
+       }
+
+       switch (sc->sc_chan_state[td->channel].state) {
+       case DWC_CHAN_ST_START:
+               if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+                       sc->sc_chan_state[td->channel].hcsplt &=
+                           ~HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_S_ANE;
+                       goto receive_spkt;
+               } else {
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_ANE;
+                       goto receive_pkt;
                }
-               if (td->set_toggle) {
-                       td->set_toggle = 0;
-                       td->toggle = 1;
+       case DWC_CHAN_ST_WAIT_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto receive_pkt;
+               if (!(sc->sc_chan_state[td->channel].hcint &
+                   HCINT_SOFTWARE_ONLY))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
+                       /* check if we are complete */
+                       if ((td->remainder == 0) || (td->got_short != 0)) {
+                               if (td->short_pkt) {
+                                       /* we are complete */
+                                       sc->sc_chan_state[
+                                           td->channel].hcint = 0;
+                                       sc->sc_chan_state[
+                                           td->channel].state = 0;
+                                       return (0);
+                               }
+                               /*
+                                * Else need to receive a zero length
+                                * packet.
+                                */
+                       }
+                       if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+                               sc->sc_chan_state[td->channel].hcsplt &=
+                                   ~HCSPLT_COMPSPLT;
+                               sc->sc_chan_state[td->channel].state =
+                                   DWC_CHAN_ST_WAIT_S_ANE;
+                               goto receive_spkt;
+                       } else {
+                               sc->sc_chan_state[td->channel].state =
+                                   DWC_CHAN_ST_WAIT_ANE;
+                               goto receive_pkt;
+                       }
                }
+               break;
+       case DWC_CHAN_ST_WAIT_S_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto receive_spkt;
+               if (sc->sc_chan_state[td->channel].hcint & (HCINT_ACK | 
HCINT_NYET)) {
+                       sc->sc_chan_state[td->channel].hcsplt |=
+                           HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_ANE;
+                       goto receive_pkt;
+               }
+               break;
+       default:
+               break;
+       }
+       return (1);             /* busy */
+
+receive_pkt:
+       if (ep_type == UE_ISOCHRONOUS) {
+               td->toggle = 0;
+       } else if (td->set_toggle) {
+               td->set_toggle = 0;
+               td->toggle = 1;
        }
 
        /* receive one packet */
@@ -936,16 +1057,46 @@ not_complete:
            (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
            (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+           sc->sc_chan_state[td->channel].hcsplt);
+
+       temp = sc->sc_chan_state[td->channel].hcchar;
        temp |= HCCHAR_EPDIR_IN;
 
        /* must enable channel before data can be received */
        DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
 
-       /* reset statemachine */
-       td->did_complete = 0;
-       td->did_nak = 0;
+       /* clear interrupts */
+       sc->sc_chan_state[td->channel].hcint = 0;
 
-       return (1);                     /* not complete */
+       return (1);     /* busy */
+
+receive_spkt:
+       if (ep_type == UE_ISOCHRONOUS) {
+               td->toggle = 0;
+       } else if (td->set_toggle) {
+               td->set_toggle = 0;
+               td->toggle = 1;
+       }
+
+       /* receive one packet */
+       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+           (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+           (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+           sc->sc_chan_state[td->channel].hcsplt);
+
+       temp = sc->sc_chan_state[td->channel].hcchar;
+       temp |= HCCHAR_EPDIR_IN;
+
+       /* must enable channel before data can be received */
+       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
+
+       /* clear interrupts */
+       sc->sc_chan_state[td->channel].hcint = 0;
+
+       return (1);     /* busy */
 }
 
 static uint8_t
@@ -1065,11 +1216,9 @@ static uint8_t
 dwc_otg_host_data_tx(struct dwc_otg_td *td)
 {
        struct dwc_otg_softc *sc;
-       uint32_t max_buffer;
        uint32_t count;
        uint32_t temp;
        uint8_t ep_type;
-       uint8_t max_frames;
 
        if (dwc_otg_host_channel_alloc(td))
                return (1);             /* busy */
@@ -1077,18 +1226,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
        /* get pointer to softc */
        sc = DWC_OTG_PC2SC(td->pc);
 
-       temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-       DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
+       ep_type = ((sc->sc_chan_state[td->channel].hcchar &
+           HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
 
-       DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-           td->channel,
-           temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-           DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+       temp = sc->sc_chan_state[td->channel].hcint;
 
-       if (temp & HCINT_NAK) {
-               td->did_nak = 1;
-               td->did_complete = 1;
-       }
+       DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+           td->channel, sc->sc_chan_state[td->channel].state, temp,
+           DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+           DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
        if (temp & HCINT_STALL) {
                td->error_stall = 1;
@@ -1096,70 +1242,132 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
                return (0);             /* complete */
        }
 
-       if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) {
+       if (temp & (HCINT_BBLERR | HCINT_XACTERR)) {
                td->error_any = 1;
                return (0);             /* complete */
        }
 
-       if (temp & HCINT_XFERCOMPL)
-               td->did_complete = 1;
+       /* channel must be disabled before we can complete the transfer */
+
+       if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) {
+               uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+               if (hcchar & HCCHAR_CHENA) {
+                       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
+                           HCCHAR_CHENA | HCCHAR_CHDIS);
+               } else {
+                       sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD;
+               }
+       }
 
-       if (td->did_complete) {
-               if (td->did_nak == 0) {
+       if (ep_type == UE_ISOCHRONOUS) {
+               if ((sc->sc_sof_val & 0xFF) != td->sof_val)
+                       return (1);     /* busy */
+               if (td->sof_val & 1)
+                       sc->sc_chan_state[td->channel].hcchar |= HCCHAR_ODDFRM;
+               else
+                       sc->sc_chan_state[td->channel].hcchar &= ~HCCHAR_ODDFRM;
+               td->sof_val += td->sof_res;
+       } else if (ep_type == UE_INTERRUPT) {
+               if ((sc->sc_sof_val & 0xFF) != td->sof_val)
+                       return (1);     /* busy */
+               td->sof_val += td->sof_res;
+       } else if (temp & HCINT_NAK) {
+               if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
+                       return (1);     /* busy */
+               td->sof_val += 1;
+       }
+
+       switch (sc->sc_chan_state[td->channel].state) {
+       case DWC_CHAN_ST_START:
+               if (sc->sc_chan_state[td->channel].hcsplt != 0) {
+                       sc->sc_chan_state[td->channel].hcsplt &= 
~HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_S_ANE;
+               } else {
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_ANE;
+               }
+               goto send_pkt;
+       case DWC_CHAN_ST_WAIT_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto send_pkt;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
                        td->offset += td->tx_bytes;
                        td->remainder -= td->tx_bytes;
                        td->toggle ^= 1;
-                       td->did_nak = 1;
 
                        /* check remainder */
                        if (td->remainder == 0) {
-                               if (td->short_pkt)
+                               if (td->short_pkt) {
+                                       sc->sc_chan_state[td->channel].hcint = 
0;
+                                       sc->sc_chan_state[td->channel].state = 
0;
                                        return (0);     /* complete */
+                               }
 
                                /* else we need to transmit a short packet */
                        }
-               } else {
-                       if ((sc->sc_sof_val & 1) != (td->sof_val & 1))
-                               return (1);     /* busy */
-                       td->sof_val += 1;
-               }
-       } else {
-               return (1);     /* busy */
-       }
-       temp = sc->sc_hcchar[td->channel];
+                       goto send_pkt;
+               }
+               break;
+       case DWC_CHAN_ST_WAIT_S_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto send_pkt;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
+                       sc->sc_chan_state[td->channel].hcsplt |= 
HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_C_ANE;
+                       goto send_cpkt;
+               }
+               break;
+       case DWC_CHAN_ST_WAIT_C_ANE:
+               if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD))
+                       break;
+               if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK)
+                       goto send_cpkt;
+               if (sc->sc_chan_state[td->channel].hcint &
+                   (HCINT_ACK | HCINT_NYET)) {
+                       td->offset += td->tx_bytes;
+                       td->remainder -= td->tx_bytes;
+                       td->toggle ^= 1;
+
+                       /* check remainder */
+                       if (td->remainder == 0) {
+                               if (td->short_pkt) {
+                                       sc->sc_chan_state[
+                                           td->channel].hcint = 0;
+                                       sc->sc_chan_state[
+                                           td->channel].state = 0;
+                                       return (0);     /* complete */
+                               }
 
-       ep_type = ((temp & HCCHAR_EPTYPE_MASK) >>
-           HCCHAR_EPTYPE_SHIFT);
+                               /* else we need to transmit a short packet */
+                       }
+                       sc->sc_chan_state[td->channel].hcsplt &=
+                           ~HCSPLT_COMPSPLT;
+                       sc->sc_chan_state[td->channel].state =
+                           DWC_CHAN_ST_WAIT_S_ANE;
+                       goto send_pkt;
+               }
+               break;
+       default:
+               break;
+       }
+       return (1);             /* busy */
 
+send_pkt:
        if (ep_type == UE_ISOCHRONOUS) {
-               if ((sc->sc_sof_val & 0xFF) != td->sof_val)
-                       return (1);     /* busy */
-               if (td->sof_val & 1)
-                       sc->sc_hcchar[td->channel] |= HCCHAR_ODDFRM;
-               else
-                       sc->sc_hcchar[td->channel] &= ~HCCHAR_ODDFRM;
-               td->sof_val += td->sof_res;
                td->toggle = 0;
-
-       } else if (ep_type == UE_INTERRUPT) {
-               if ((sc->sc_sof_val & 0xFF) != td->sof_val)
-                       return (1);     /* busy */
-               td->sof_val += td->sof_res;
        } else if (td->set_toggle) {
                td->set_toggle = 0;
                td->toggle = 1;
        }
 
-       temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS);
-
-       max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK);
-       max_frames = (temp & HPTXSTS_PTXQSPCAVAIL_MASK)
-           >> HPTXSTS_PTXQSPCAVAIL_SHIFT;
-
-       max_buffer = max_buffer - (max_buffer % td->max_packet_size);
-       if (max_buffer == 0 || max_frames == 0)
-               return (1);     /* busy */
-
        /* send one packet at a time */
        count = td->max_packet_size;
        if (td->remainder < count) {
@@ -1176,7 +1384,10 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
            (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
            (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
-       temp = sc->sc_hcchar[td->channel];
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+           sc->sc_chan_state[td->channel].hcsplt);
+
+       temp = sc->sc_chan_state[td->channel].hcchar;
        temp &= ~HCCHAR_EPDIR_IN;
 
        /* must enable before writing data to FIFO */
@@ -1197,11 +1408,31 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
                    sc->sc_tx_bounce_buffer, (count + 3) / 4);
        }
 
-       /* reset statemachine */
-       td->did_complete = 0;
-       td->did_nak = 0;
+       /* store number of bytes transmitted */
        td->tx_bytes = count;
 
+       /* clear interrupts */
+       sc->sc_chan_state[td->channel].hcint = 0;
+
+       return (1);     /* busy */
+
+send_cpkt:
+       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+           (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+           (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel),
+           sc->sc_chan_state[td->channel].hcsplt);
+
+       temp = sc->sc_chan_state[td->channel].hcchar;
+       temp &= ~HCCHAR_EPDIR_IN;
+
+       /* must enable channel before writing data to FIFO */
+       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
+
+       /* clear interrupts */
+       sc->sc_chan_state[td->channel].hcint = 0;
+
        return (1);     /* busy */
 }
 
@@ -1524,23 +1755,12 @@ repeat:
                        /* non-data messages we simply skip */
                        if (temp != GRXSTSRD_STP_DATA &&
                            temp != GRXSTSRD_OUT_DATA) {
-                               if (sc->sc_flags.status_device_mode) {
-                                       dwc_otg_common_rx_ack(sc);
-                                       goto repeat;
-                               } else if (temp != GRXSTSRD_OUT_COMPLETE &&
-                                   temp != GRXSTSRH_HALTED) {
-                                       dwc_otg_common_rx_ack(sc);
-                                       goto repeat;
-                               }
+                               dwc_otg_common_rx_ack(sc);
+                               goto repeat;
                        }
 
-                       if (temp == GRXSTSRH_HALTED || 
-                           temp == GRXSTSRD_OUT_COMPLETE) {
-                               temp = 0;
-                       } else {
-                               temp = GRXSTSRD_BCNT_GET(
-                                   sc->sc_last_rx_status);
-                       }
+                       temp = GRXSTSRD_BCNT_GET(
+                           sc->sc_last_rx_status);
                        ep_no = GRXSTSRD_CHNUM_GET(
                            sc->sc_last_rx_status);
 
@@ -1633,7 +1853,7 @@ void
 dwc_otg_interrupt(struct dwc_otg_softc *sc)
 {
        uint32_t status;
-       uint32_t haint;
+       uint8_t x;
 
        USB_BUS_LOCK(&sc->sc_bus);
 
@@ -1641,24 +1861,20 @@ dwc_otg_interrupt(struct dwc_otg_softc *
        status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
        DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status);
 
-       haint = DWC_OTG_READ_4(sc, DOTG_HAINT);
-
        DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n",
-           status, haint, DWC_OTG_READ_4(sc, DOTG_HFNUM));
+           status, DWC_OTG_READ_4(sc, DOTG_HAINT),
+           DWC_OTG_READ_4(sc, DOTG_HFNUM));
 
-       if (haint != 0) {
-               uint8_t x;
+       /* get all channel interrupts */
+       for (x = 0; x != sc->sc_host_ch_max; x++) {
+               uint32_t temp;
 
-               /* clear left-over interrupts */
-               for (x = 0; x != sc->sc_host_ch_max; x++) {
-                       if (!(haint & (1 << x)))
-                               continue;
-                       /* check if channel is disabled */
-                       if (sc->sc_hcchar[x] == 0) {
-                               uint32_t temp;
-                               temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
-                               DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
-                       }
+               temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+               if (temp != 0) {
+                       DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
+                       sc->sc_chan_state[x].hcint |=
+                           (temp & ~(HCINT_SOFTWARE_ONLY |
+                           HCINT_SUSPEND_ONLY | HCINT_CHHLTD));
                }
        }
 
@@ -1856,8 +2072,6 @@ dwc_otg_setup_standard_chain_sub(struct 
        td->error_any = 0;
        td->npkt = 0;
        td->did_stall = temp->did_stall;
-       td->did_nak = 1;
-       td->did_complete = 1;
        td->short_pkt = temp->short_pkt;
        td->alt_next = temp->setup_alt_next;
        td->set_toggle = 0;
@@ -2324,13 +2538,14 @@ dwc_otg_device_done(struct usb_xfer *xfe
                        DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
                            HCCHAR_CHENA | HCCHAR_CHDIS);
 
-                       sc->sc_hcchar[td->channel] = 0;
+                       sc->sc_chan_state[td->channel].hcchar = 0;
                        sc->sc_active_rx_ep &= ~(1 << td->channel);
 
-                       td->channel = DWC_OTG_MAX_CHANNELS;
-
                        /* release SOF's */
-                       dwc_otg_release_sof(sc);
+                       if (sc->sc_chan_state[td->channel].sof_requested != 0)
+                               dwc_otg_release_sof(sc);
+
+                       td->channel = DWC_OTG_MAX_CHANNELS;
                }
        }
        /* dequeue transfer and start next transfer */
@@ -2671,8 +2886,12 @@ dwc_otg_init(struct dwc_otg_softc *sc)
                uint8_t x;
 
                for (x = 0; x != sc->sc_host_ch_max; x++) {
-                       /* disable channel interrupts */
-                       DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0);
+                       /* enable interrupts */
+                       DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+                           HCINT_STALL | HCINT_BBLERR |
+                           HCINT_XACTERR | HCINT_XFERCOMPL |
+                           HCINT_NAK | HCINT_ACK | HCINT_NYET |
+                           HCINT_CHHLTD);
                        DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
                            HCCHAR_CHENA | HCCHAR_CHDIS);
                }
@@ -3613,18 +3832,21 @@ dwc_otg_device_resume(struct usb_device 
                        td = xfer->td_transfer_cache;
                        if (td != NULL &&
                            td->channel < DWC_OTG_MAX_CHANNELS) {
+                               sc->sc_chan_state[td->channel].hcint &= 
+                                   ~HCINT_SUSPEND_ONLY;
 
-                               sc->sc_hcchar[td->channel] =
-                                   (sc->sc_hcchar[td->channel] & 
~HCCHAR_CHDIS) |
-                                   HCCHAR_CHENA;
-
-                               DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-                                   sc->sc_hcchar[td->channel]);
+                               if 
(sc->sc_chan_state[td->channel].sof_requested == 0) {
+                                       
sc->sc_chan_state[td->channel].sof_requested = 1;
+                                       dwc_otg_request_sof(sc);
+                               }
                        }
                }
        }
 
        USB_BUS_UNLOCK(udev->bus);
+
+       /* poll all transfers again to restart resumed ones */
+       dwc_otg_do_poll(udev->bus);
 }
 
 static void
@@ -3648,12 +3870,15 @@ dwc_otg_device_suspend(struct usb_device
                        if (td != NULL &&
                            td->channel < DWC_OTG_MAX_CHANNELS) {
 
-                               sc->sc_hcchar[td->channel] =
-                                   (DWC_OTG_READ_4(sc, 
DOTG_HCCHAR(td->channel)) |
-                                   HCCHAR_CHDIS) & ~HCCHAR_CHENA;
-
+                               sc->sc_chan_state[td->channel].hcint |= 
+                                   HCINT_NAK | HCINT_CHHLTD | 
HCINT_SUSPEND_ONLY;
                                DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel),
-                                   sc->sc_hcchar[td->channel]);
+                                   HCCHAR_CHENA | HCCHAR_CHDIS);
+
+                               if 
(sc->sc_chan_state[td->channel].sof_requested != 0) {
+                                       
sc->sc_chan_state[td->channel].sof_requested = 0;
+                                       dwc_otg_release_sof(sc);
+                               }
                        }
                }
        }

Modified: head/sys/dev/usb/controller/dwc_otg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.h       Fri Sep 14 05:24:06 2012        
(r240481)
+++ head/sys/dev/usb/controller/dwc_otg.h       Fri Sep 14 07:52:57 2012        
(r240482)
@@ -68,8 +68,6 @@ struct dwc_otg_td {
        uint8_t did_stall:1;
        uint8_t toggle:1;
        uint8_t set_toggle:1;
-       uint8_t did_nak:1;
-       uint8_t did_complete:1;
        uint8_t got_short:1;
 };
 
@@ -128,6 +126,18 @@ struct dwc_otg_profile {
        uint16_t max_buffer;
 };
 
+struct dwc_otg_chan_state {
+       uint32_t hcchar;
+       uint32_t hcint;
+       uint32_t hcsplt;
+       uint8_t state;
+#define        DWC_CHAN_ST_START 0
+#define        DWC_CHAN_ST_WAIT_ANE 1
+#define        DWC_CHAN_ST_WAIT_S_ANE 2
+#define        DWC_CHAN_ST_WAIT_C_ANE 3
+       uint8_t sof_requested;
+};
+
 struct dwc_otg_softc {
        struct usb_bus sc_bus;
        union dwc_otg_hub_temp sc_hub_temp;
@@ -149,7 +159,7 @@ struct dwc_otg_softc {
        uint32_t sc_last_rx_status;
        uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS];
        uint32_t sc_in_ctl[DWC_OTG_MAX_ENDPOINTS];
-       uint32_t sc_hcchar[DWC_OTG_MAX_CHANNELS];
+       struct dwc_otg_chan_state sc_chan_state[DWC_OTG_MAX_CHANNELS];
        uint32_t sc_sof_refs;
        uint32_t sc_sof_val;
        uint32_t sc_hprt_val;

Modified: head/sys/dev/usb/controller/dwc_otgreg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otgreg.h    Fri Sep 14 05:24:06 2012        
(r240481)
+++ head/sys/dev/usb/controller/dwc_otgreg.h    Fri Sep 14 07:52:57 2012        
(r240482)
@@ -541,6 +541,8 @@
 #define        HCSPLT_PRTADDR_SHIFT            0
 #define        HCSPLT_PRTADDR_MASK             0x0000007f
 
+#define        HCINT_SUSPEND_ONLY              (1<<21) /* BSD only */
+#define        HCINT_SOFTWARE_ONLY             (1<<20) /* BSD only */
 #define        HCINT_DATATGLERR                (1<<10)
 #define        HCINT_FRMOVRUN                  (1<<9)
 #define        HCINT_BBLERR                    (1<<8)
_______________________________________________
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