Author: hselasky
Date: Fri Nov  9 16:28:58 2012
New Revision: 242829
URL: http://svnweb.freebsd.org/changeset/base/242829

Log:
  Fix LOW and FULL speed USB INTERRUPT endpoint support for the
  DWC OTG driver. Fix a hang issue when using LOW and FULL speed
  BULK traffic. Make sure we don't ask for data in the last
  microframe. This allows using devices like USB mice and USB
  keyboards connected to the RPI-B.
  
  Suggested by: gonzo @

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 Nov  9 16:00:30 2012        
(r242828)
+++ head/sys/dev/usb/controller/dwc_otg.c       Fri Nov  9 16:28:58 2012        
(r242829)
@@ -372,6 +372,15 @@ 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)
+               return;
+       sc->sc_irq_mask |= GINTSTS_SOF;
+       DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+}
+
+static void
 dwc_otg_resume_irq(struct dwc_otg_softc *sc)
 {
        if (sc->sc_flags.status_suspend) {
@@ -523,10 +532,6 @@ dwc_otg_host_channel_wait(struct dwc_otg
        if (x == 0)
                return (0);     /* wait */
 
-       /* assume NAK-ing is next */
-       if (sc->sc_chan_state[x].hcint & HCINT_NYET)
-               return (0);     /* wait */
-
        /* find new disabled channel */
        for (x = 1; x != sc->sc_host_ch_max; x++) {
 
@@ -629,8 +634,7 @@ dwc_otg_host_channel_disable(struct dwc_
                /* don't re-use channel until next SOF is transmitted */
                sc->sc_chan_state[x].wait_sof = 2;
                /* enable SOF interrupt */
-               sc->sc_irq_mask |= GINTMSK_SOFMSK; 
-               DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+               dwc_otg_enable_sof_irq(sc);
        }
 }
 
@@ -688,14 +692,15 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
            DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
-       if (hcint & HCINT_STALL) {
+       if (hcint & (HCINT_RETRY |
+           HCINT_ACK | HCINT_NYET)) {
+               /* give success bits priority over failure bits */
+       } else if (hcint & HCINT_STALL) {
                DPRINTF("CH=%d STALL\n", td->channel);
                td->error_stall = 1;
                td->error_any = 1;
                return (0);             /* complete */
-       }
-
-       if (hcint & HCINT_ERRORS) {
+       } else if (hcint & HCINT_ERRORS) {
                DPRINTF("CH=%d ERROR\n", td->channel);
                td->errcnt++;
                if (td->hcsplt != 0 || td->errcnt >= 3) {
@@ -769,6 +774,8 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
                        return (0);     /* complete */
                }
                break;
+       case DWC_CHAN_ST_TX_PKT_SYNC:
+               goto send_pkt_sync;
        default:
                break;
        }
@@ -780,7 +787,21 @@ 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 */
+               }
+
                td->hcsplt &= ~HCSPLT_COMPSPLT;
                td->state = DWC_CHAN_ST_WAIT_S_ANE;
        } else {
@@ -961,8 +982,7 @@ not_complete:
 }
 
 static uint8_t
-dwc_otg_host_rate_check(struct dwc_otg_td *td,
-    uint8_t do_inc)
+dwc_otg_host_rate_check(struct dwc_otg_td *td)
 {
        struct dwc_otg_softc *sc;
        uint8_t ep_type;
@@ -981,13 +1001,14 @@ dwc_otg_host_rate_check(struct dwc_otg_t
                        td->hcchar |= HCCHAR_ODDFRM;
                else
                        td->hcchar &= ~HCCHAR_ODDFRM;
-               if (do_inc)
-                       td->tmr_val += td->tmr_res;
+               td->tmr_val += td->tmr_res;
        } else if (ep_type == UE_INTERRUPT) {
-               if ((sc->sc_tmr_val & 0xFF) != td->tmr_val)
+               uint8_t delta;
+
+               delta = sc->sc_tmr_val - td->tmr_val;
+               if (delta >= 128)
                        goto busy;
-               if (do_inc)
-                       td->tmr_val += td->tmr_res;
+               td->tmr_val = sc->sc_tmr_val + td->tmr_res;
        } else if (td->did_nak != 0) {
                goto busy;
        } 
@@ -1010,6 +1031,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
        uint32_t hcint;
        uint32_t hcchar;
        uint32_t count;
+       uint8_t ep_type;
 
        if (dwc_otg_host_channel_alloc(td))
                return (1);             /* busy */
@@ -1017,6 +1039,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
        /* get pointer to softc */
        sc = DWC_OTG_PC2SC(td->pc);
 
+       ep_type = ((td->hcchar &
+           HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
+
        hcint = sc->sc_chan_state[td->channel].hcint;
 
        DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
@@ -1026,14 +1051,15 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 
        /* check interrupt bits */
 
-       if (hcint & HCINT_STALL) {
+       if (hcint & (HCINT_RETRY |
+           HCINT_ACK | HCINT_NYET)) {
+               /* give success bits priority over failure bits */
+       } else if (hcint & HCINT_STALL) {
                DPRINTF("CH=%d STALL\n", td->channel);
                td->error_stall = 1;
                td->error_any = 1;
                return (0);             /* complete */
-       }
-
-       if (hcint & HCINT_ERRORS) {
+       } else if (hcint & HCINT_ERRORS) {
                DPRINTF("CH=%d ERROR\n", td->channel);
                td->errcnt++;
                if (td->hcsplt != 0 || td->errcnt >= 3) {
@@ -1063,7 +1089,17 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
        switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
        case GRXSTSRH_IN_DATA:
 
-               DPRINTF("DATA\n");
+               DPRINTF("DATA ST=%d STATUS=0x%08x\n",
+                   (int)td->state, (int)sc->sc_last_rx_status);
+
+               if (hcint & HCINT_SOFTWARE_ONLY) {
+                       /*
+                        * When using SPLIT transactions on interrupt
+                        * endpoints, sometimes data occurs twice.
+                        */
+                       DPRINTF("Data already received\n");
+                       break;
+               }
 
                td->toggle ^= 1;
 
@@ -1131,12 +1167,16 @@ check_state:
                        else
                                goto receive_pkt;
                }
-               if (hcint & HCINT_NYET) {
-                       if (td->hcsplt != 0)
-                               goto receive_pkt;
-               }
-               if (!(hcint & HCINT_SOFTWARE_ONLY))
+               if (!(hcint & HCINT_SOFTWARE_ONLY)) {
+                       if (hcint & HCINT_NYET) {
+                               if (td->hcsplt != 0) {
+                                       if (!dwc_otg_host_channel_wait(td))
+                                               break;
+                                       goto receive_pkt;
+                               }
+                       }
                        break;
+               }
                if (hcint & (HCINT_ACK | HCINT_NYET)) {
                        if (!dwc_otg_host_channel_wait(td))
                                break;
@@ -1179,20 +1219,38 @@ check_state:
        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 (dwc_otg_host_rate_check(td, 1)) {
+       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;
+               }
+
+               td->curr_frame = count;
+               td->hcsplt |= HCSPLT_COMPSPLT;
+       } else if (dwc_otg_host_rate_check(td)) {
                td->state = DWC_CHAN_ST_RX_PKT;
                dwc_otg_host_channel_free(td);
                goto busy;
        }
 
-       if (td->hcsplt != 0)
-               td->hcsplt |= HCSPLT_COMPSPLT;
        td->state = DWC_CHAN_ST_WAIT_ANE;
 
        /* receive one packet */
@@ -1213,12 +1271,42 @@ receive_pkt:
        goto busy;
 
 receive_spkt:
-       if (dwc_otg_host_rate_check(td, 0)) {
+       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;
+                       dwc_otg_host_channel_free(td);
+                       goto busy;
+               }
+       }
+
        td->hcsplt &= ~HCSPLT_COMPSPLT;
        td->state = DWC_CHAN_ST_WAIT_S_ANE;
 
@@ -1377,14 +1465,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
            DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
-       if (hcint & HCINT_STALL) {
+       if (hcint & (HCINT_RETRY |
+           HCINT_ACK | HCINT_NYET)) {
+               /* give success bits priority over failure bits */
+       } else if (hcint & HCINT_STALL) {
                DPRINTF("CH=%d STALL\n", td->channel);
                td->error_stall = 1;
                td->error_any = 1;
                return (0);             /* complete */
-       }
-
-       if (hcint & HCINT_ERRORS) {
+       } else if (hcint & HCINT_ERRORS) {
                DPRINTF("CH=%d ERROR\n", td->channel);
                td->errcnt++;
                if (td->hcsplt != 0 || td->errcnt >= 3) {
@@ -1482,6 +1571,9 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
        case DWC_CHAN_ST_TX_PKT:
                goto send_pkt;
 
+       case DWC_CHAN_ST_TX_PKT_SYNC:
+               goto send_pkt_sync;
+
        case DWC_CHAN_ST_TX_CPKT:
                goto send_cpkt;
 
@@ -1491,13 +1583,25 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
        goto busy;
 
 send_pkt:
-       if (dwc_otg_host_rate_check(td, 1)) {
+       if (dwc_otg_host_rate_check(td)) {
                td->state = DWC_CHAN_ST_TX_PKT;
                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;
+               }
+
                td->hcsplt &= ~HCSPLT_COMPSPLT;
                td->state = DWC_CHAN_ST_WAIT_S_ANE;
        } else {
@@ -1549,6 +1653,13 @@ send_pkt:
        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;
+       }
+
        td->hcsplt |= HCSPLT_COMPSPLT;
        td->state = DWC_CHAN_ST_WAIT_C_ANE;
 
@@ -2242,6 +2353,9 @@ dwc_otg_interrupt(struct dwc_otg_softc *
                if (sc->sc_irq_mask & GINTMSK_SOFMSK) {
                        uint8_t x;
                        uint8_t y;
+
+                       DPRINTFN(12, "SOF interrupt\n");
+
                        for (x = y = 0; x != sc->sc_host_ch_max; x++) {
                                if (sc->sc_chan_state[x].wait_sof != 0) {
                                        if (--(sc->sc_chan_state[x].wait_sof) 
!= 0)
@@ -2280,6 +2394,7 @@ dwc_otg_setup_standard_chain_sub(struct 
        td->remainder = temp->len;
        td->tx_bytes = 0;
        td->error_any = 0;
+       td->error_stall = 0;
        td->npkt = 0;
        td->did_stall = temp->did_stall;
        td->short_pkt = temp->short_pkt;
@@ -2531,8 +2646,8 @@ dwc_otg_setup_standard_chain(struct usb_
                                ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
                                if (ival == 0)
                                        ival = 1;
-                               else if (ival > 255)
-                                       ival = 255;
+                               else if (ival > 127)
+                                       ival = 127;
                                td->tmr_val = sc->sc_tmr_val + ival;
                                td->tmr_res = ival;
                        }
@@ -2549,8 +2664,8 @@ dwc_otg_setup_standard_chain(struct usb_
                                ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
                                if (ival == 0)
                                        ival = 1;
-                               else if (ival > 255)
-                                       ival = 255;
+                               else if (ival > 127)
+                                       ival = 127;
                                td->tmr_val = sc->sc_tmr_val + ival;
                                td->tmr_res = ival;
                        }

Modified: head/sys/dev/usb/controller/dwc_otg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.h       Fri Nov  9 16:00:30 2012        
(r242828)
+++ head/sys/dev/usb/controller/dwc_otg.h       Fri Nov  9 16:28:58 2012        
(r242829)
@@ -60,6 +60,7 @@ struct dwc_otg_td {
        uint8_t errcnt;
        uint8_t tmr_res;
        uint8_t tmr_val;
+       uint8_t curr_frame;
        uint8_t ep_no;
        uint8_t channel;
        uint8_t state;
@@ -69,8 +70,10 @@ struct dwc_otg_td {
 #define        DWC_CHAN_ST_WAIT_C_ANE 3
 #define        DWC_CHAN_ST_RX_PKT 4
 #define        DWC_CHAN_ST_RX_SPKT 5
+#define        DWC_CHAN_ST_RX_SPKT_SYNC 6
 #define        DWC_CHAN_ST_TX_PKT 4
 #define        DWC_CHAN_ST_TX_CPKT 5
+#define        DWC_CHAN_ST_TX_PKT_SYNC 6
        uint8_t error:1;
        uint8_t error_any:1;
        uint8_t error_stall:1;

Modified: head/sys/dev/usb/controller/dwc_otgreg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otgreg.h    Fri Nov  9 16:00:30 2012        
(r242828)
+++ head/sys/dev/usb/controller/dwc_otgreg.h    Fri Nov  9 16:28:58 2012        
(r242829)
@@ -299,7 +299,7 @@
 #define        GRXSTSRD_DPID_DATA0             (0<<15)
 #define        GRXSTSRD_DPID_DATA1             (2<<15)
 #define        GRXSTSRD_DPID_DATA2             (1<<15)
-#define        GRXSTSRD_PID_MDATA              (3<<15)
+#define        GRXSTSRD_DPID_MDATA             (3<<15)
 #define        GRXSTSRD_BCNT_MASK              0x00007ff0
 #define        GRXSTSRD_BCNT_GET(x)            (((x) >> 4) & 0x7FF)
 #define        GRXSTSRD_BCNT_SHIFT             4
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to