diff options
author | hselasky <hselasky@FreeBSD.org> | 2014-05-05 11:50:52 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2014-05-05 11:50:52 +0000 |
commit | 70263b99a8b304f1846a3726265e261849146533 (patch) | |
tree | bf1fcb50f06e7472e05b28f78b9ba7dcec18f91d | |
parent | 54f013e376551734a05a51a5901c00a32da20767 (diff) | |
download | FreeBSD-src-70263b99a8b304f1846a3726265e261849146533.zip FreeBSD-src-70263b99a8b304f1846a3726265e261849146533.tar.gz |
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
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.c | 989 | ||||
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.h | 30 | ||||
-rw-r--r-- | sys/dev/usb/controller/dwc_otgreg.h | 10 |
3 files changed, 768 insertions, 261 deletions
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c index 8c70801..8a63d40 100644 --- a/sys/dev/usb/controller/dwc_otg.c +++ b/sys/dev/usb/controller/dwc_otg.c @@ -207,6 +207,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode) /* 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 *sc, uint8_t mode) 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 *sc, uint8_t mode) } 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 *sc) 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 *sc, uint8_t x) { 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, uint8_t x) 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_td *td) 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_otg_td *td) 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_otg_td *td) 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_td *td) 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 *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 *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) 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 *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 *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 *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_td *td) /* 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 *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 *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 *td) break; default: - DPRINTF("OTHER\n"); break; } /* release FIFO */ @@ -1153,6 +1254,8 @@ dwc_otg_host_data_rx(struct dwc_otg_td *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,99 +1336,90 @@ 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++; - td->curr_frame = count; + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } 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 *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 *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) 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 *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 *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 *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) 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 *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; + goto tt_wait; } + /* wait until next slot before trying again */ + td->tt_complete_slot++; - td->hcsplt |= HCSPLT_COMPSPLT; + td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), @@ -1676,7 +1946,14 @@ send_cpkt: /* must enable channel before writing data to FIFO */ 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 */ } @@ -2041,6 +2318,224 @@ dwc_otg_timer_stop(struct dwc_otg_softc *sc) } static void +dwc_otg_update_host_transfer_state(struct dwc_otg_softc *sc) +{ + TAILQ_HEAD(, usb_xfer) head; + struct usb_xfer *xfer; + struct usb_xfer *xfer_next; + struct dwc_otg_td *td; + uint8_t needsof; + uint16_t temp; + + /* FS/LS TT frames are one behind, so add one here */ + temp = (DWC_OTG_READ_4(sc, DOTG_HFNUM) + 1) & HFNUM_FRNUM_MASK; + + if (sc->sc_last_frame_num == temp) + return; + + sc->sc_last_frame_num = temp; + + needsof = 0; + + TAILQ_INIT(&head); + + if (sc->sc_irq_mask & GINTMSK_SOFMSK) { + uint8_t x; + + for (x = 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) + needsof = 1; + } + } + } + + if ((temp & 7) == 0) { + + /* reset TT info */ + memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); + + /* + * Plan ahead FULL speed transfers going through the + * transaction translator, according to the USB + * specified priority: + */ + TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { + struct dwc_otg_tt_info *pinfo; + + td = xfer->td_transfer_cache; + if (td == NULL || td->hcsplt == 0 || + (td->hcchar & HCCHAR_EPTYPE_MASK) != + (UE_CONTROL << HCCHAR_EPTYPE_SHIFT)) + continue; + + /* Reset state if stuck waiting for complete split */ + if (td->state == DWC_CHAN_ST_WAIT_C_PKT) + td->state = DWC_CHAN_ST_START; + + pinfo = sc->sc_tt_info + td->tt_index; + + td->tt_start_slot = pinfo->slot_index; + pinfo->bytes_used += td->max_packet_size; + while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) { + pinfo->bytes_used -= HCSPLT_XACTLEN_MAX; + pinfo->slot_index ++; + } + + td->tt_complete_slot = pinfo->slot_index + 2; + if (td->tt_complete_slot < 8) { + td->tt_scheduled = 1; + needsof = 1; + TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); + TAILQ_INSERT_TAIL(&head, xfer, wait_entry); + } else { + td->tt_scheduled = 0; + } + } + + TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { + struct dwc_otg_tt_info *pinfo; + + td = xfer->td_transfer_cache; + if (td == NULL || + (td->hcchar & HCCHAR_EPTYPE_MASK) != + (UE_ISOCHRONOUS << HCCHAR_EPTYPE_SHIFT)) + continue; + + /* execute more frames */ + td->tmr_val = 0; + + needsof = 1; + + if (td->hcsplt == 0) + continue; + + /* Reset state if stuck waiting for complete split */ + if (td->state == DWC_CHAN_ST_WAIT_C_PKT) + td->state = DWC_CHAN_ST_START; + + pinfo = sc->sc_tt_info + td->tt_index; + + td->tt_start_slot = pinfo->slot_index; + pinfo->bytes_used += td->remainder; + while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) { + pinfo->bytes_used -= HCSPLT_XACTLEN_MAX; + pinfo->slot_index ++; + } + + td->tt_complete_slot = pinfo->slot_index + 2; + if (td->tt_complete_slot < 8) { + td->tt_scheduled = 1; + TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); + TAILQ_INSERT_TAIL(&head, xfer, wait_entry); + } else { + td->tt_scheduled = 0; + } + } + + TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { + struct dwc_otg_tt_info *pinfo; + + td = xfer->td_transfer_cache; + if (td == NULL || td->hcsplt == 0 || + (td->hcchar & HCCHAR_EPTYPE_MASK) != + (UE_INTERRUPT << HCCHAR_EPTYPE_SHIFT)) { + continue; + } + + if (dwc_otg_host_rate_check_interrupt(sc, td)) + continue; + + /* Reset state if stuck waiting for complete split */ + if (td->state == DWC_CHAN_ST_WAIT_C_PKT) + td->state = DWC_CHAN_ST_START; + + pinfo = sc->sc_tt_info + td->tt_index; + + td->tt_start_slot = pinfo->slot_index; + pinfo->bytes_used += td->remainder; + while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) { + pinfo->bytes_used -= HCSPLT_XACTLEN_MAX; + pinfo->slot_index ++; + } + + td->tt_complete_slot = pinfo->slot_index + 2; + if (td->tt_complete_slot < 8) { + td->tt_scheduled = 1; + needsof = 1; + TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); + TAILQ_INSERT_TAIL(&head, xfer, wait_entry); + } else { + td->tt_scheduled = 0; + } + } + } + + if ((temp & 7) < 6) { + TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { + struct dwc_otg_tt_info *pinfo; + + td = xfer->td_transfer_cache; + if (td == NULL || td->hcsplt == 0 || + (td->hcchar & HCCHAR_EPTYPE_MASK) != + (UE_BULK << HCCHAR_EPTYPE_SHIFT)) { + continue; + } + + if ((temp & 7) == 0) { + /* Reset state if stuck waiting for complete split */ + if (td->state == DWC_CHAN_ST_WAIT_C_PKT) + td->state = DWC_CHAN_ST_START; + } else if (td->tt_scheduled != 0) + continue; /* already scheduled */ + + pinfo = sc->sc_tt_info + td->tt_index; + + td->tt_start_slot = pinfo->slot_index; + pinfo->bytes_used += td->remainder; + while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) { + pinfo->bytes_used -= HCSPLT_XACTLEN_MAX; + pinfo->slot_index ++; + } + + td->tt_complete_slot = pinfo->slot_index + 2; + if (td->tt_complete_slot < 8) { + td->tt_scheduled = 1; + needsof = 1; + TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); + TAILQ_INSERT_TAIL(&head, xfer, wait_entry); + } else { + td->tt_scheduled = 0; + } + } + } + + /* TT transfers need to be executed in a specific order */ + TAILQ_CONCAT(&head, &sc->sc_bus.intr_q.head, wait_entry); + + /* Put TT transfers first in the queue */ + TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry); + + if ((temp & 7) == 0) { + DPRINTFN(12, "SOF interrupt #%d, needsof=%d\n", + (int)temp, (int)needsof); + + /* update SOF IRQ mask */ + if (sc->sc_irq_mask & GINTMSK_SOFMSK) { + if (needsof == 0) { + sc->sc_irq_mask &= ~GINTMSK_SOFMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } + } else { + if (needsof != 0) { + sc->sc_irq_mask |= GINTMSK_SOFMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } + } + } +} + +static void dwc_otg_interrupt_poll(struct dwc_otg_softc *sc) { struct usb_xfer *xfer; @@ -2126,6 +2621,9 @@ repeat: got_rx_status = 1; } + /* update SOF related information */ + dwc_otg_update_host_transfer_state(sc); + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (!dwc_otg_xfer_do_fifo(xfer)) { /* queue has been modified */ @@ -2356,25 +2854,8 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc) } /* check for SOF interrupt */ - if (status & GINTSTS_SOF) { - 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) - y = 1; - } - } - if (y == 0) { - sc->sc_irq_mask &= ~GINTMSK_SOFMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - } - } - } + if (status & GINTSTS_SOF) + dwc_otg_update_host_transfer_state(sc); /* poll FIFO(s) */ dwc_otg_interrupt_poll(sc); @@ -2412,6 +2893,9 @@ dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp) td->channel = DWC_OTG_MAX_CHANNELS; td->state = 0; td->errcnt = 0; + td->tt_scheduled = 0; + td->tt_index = temp->tt_index; + td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; } static void @@ -2419,6 +2903,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) { struct dwc_otg_std_temp temp; struct dwc_otg_td *td; + struct usb_device *udev; uint32_t x; uint8_t need_sync; uint8_t is_host; @@ -2429,6 +2914,16 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) temp.max_frame_size = xfer->max_frame_size; + udev = xfer->xroot->udev; + if (udev->parent_hs_hub != NULL && udev->speed != USB_SPEED_HIGH) { + if (udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) + temp.tt_index = udev->device_index; + else + temp.tt_index = udev->parent_hs_hub->device_index; + } else { + temp.tt_index = udev->device_index; + } + td = xfer->td_start[0]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; @@ -2439,7 +2934,8 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.offset = 0; - temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.setup_alt_next = xfer->flags_int.short_frames_ok || + xfer->flags_int.isochronous_xfr; temp.did_stall = !xfer->flags_int.control_stall; is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST); @@ -2637,14 +3133,12 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) case USB_SPEED_FULL: case USB_SPEED_LOW: /* check if root HUB port is running High Speed */ - if (sc->sc_flags.status_high_speed != 0) { + if (xfer->xroot->udev->parent_hs_hub != NULL) { hcsplt = HCSPLT_SPLTENA | (xfer->xroot->udev->hs_port_no << HCSPLT_PRTADDR_SHIFT) | (xfer->xroot->udev->hs_hub_addr << HCSPLT_HUBADDR_SHIFT); - if (xfer_type == UE_ISOCHRONOUS) /* XXX */ - hcsplt |= (3 << HCSPLT_XACTPOS_SHIFT); } else { hcsplt = 0; } @@ -2657,6 +3151,12 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) ival = 127; td->tmr_val = sc->sc_tmr_val + ival; td->tmr_res = ival; + } else if (xfer_type == UE_ISOCHRONOUS) { + td->tmr_val = 0; + td->tmr_res = 1; + } else { + td->tmr_val = 0; + td->tmr_res = 0; } break; case USB_SPEED_HIGH: @@ -2675,19 +3175,19 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) ival = 127; td->tmr_val = sc->sc_tmr_val + ival; td->tmr_res = ival; + } else if (xfer_type == UE_ISOCHRONOUS) { + td->tmr_val = 0; + td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer); + } else { + td->tmr_val = 0; + td->tmr_res = 0; } break; default: hcsplt = 0; - break; - } - - if (xfer_type == UE_ISOCHRONOUS) { - td->tmr_val = xfer->endpoint->isoc_next & 0xFF; - td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer); - } else if (xfer_type != UE_INTERRUPT) { td->tmr_val = 0; td->tmr_res = 0; + break; } /* store configuration in all TD's */ @@ -2719,8 +3219,13 @@ dwc_otg_timeout(void *arg) static void dwc_otg_start_standard_chain(struct usb_xfer *xfer) { + struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); + DPRINTFN(9, "\n"); + /* update SOF related information */ + dwc_otg_update_host_transfer_state(sc); + /* poll one time - will turn on interrupts */ if (dwc_otg_xfer_do_fifo(xfer)) { @@ -2787,7 +3292,8 @@ dwc_otg_standard_done_sub(struct usb_xfer *xfer) } /* Check for short transfer */ if (len > 0) { - if (xfer->flags_int.short_frames_ok) { + if (xfer->flags_int.short_frames_ok || + xfer->flags_int.isochronous_xfr) { /* follow alt next */ if (td->alt_next) { td = td->obj_next; @@ -3357,9 +3863,15 @@ dwc_otg_device_isoc_close(struct usb_xfer *xfer) static void dwc_otg_device_isoc_enter(struct usb_xfer *xfer) { +} + +static void +dwc_otg_device_isoc_start(struct usb_xfer *xfer) +{ struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); uint32_t temp; - uint32_t nframes; + uint32_t msframes; + uint32_t framenum; uint8_t shift = usbd_xfer_get_fps_shift(xfer); DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", @@ -3369,34 +3881,42 @@ dwc_otg_device_isoc_enter(struct usb_xfer *xfer) temp = DWC_OTG_READ_4(sc, DOTG_HFNUM); /* get the current frame index */ - nframes = (temp & HFNUM_FRNUM_MASK); + framenum = (temp & HFNUM_FRNUM_MASK); } else { temp = DWC_OTG_READ_4(sc, DOTG_DSTS); /* get the current frame index */ - nframes = DSTS_SOFFN_GET(temp); + framenum = DSTS_SOFFN_GET(temp); } - if (sc->sc_flags.status_high_speed) - nframes /= 8; + if (xfer->xroot->udev->parent_hs_hub != NULL) + framenum /= 8; - nframes &= DWC_OTG_FRAME_MASK; + framenum &= DWC_OTG_FRAME_MASK; + + /* + * Compute number of milliseconds worth of data traffic for + * this USB transfer: + */ + if (xfer->xroot->udev->speed == USB_SPEED_HIGH) + msframes = ((xfer->nframes << shift) + 7) / 8; + else + msframes = xfer->nframes; /* * check if the frame index is within the window where the frames * will be inserted */ - temp = (nframes - xfer->endpoint->isoc_next) & DWC_OTG_FRAME_MASK; + temp = (framenum - xfer->endpoint->isoc_next) & DWC_OTG_FRAME_MASK; - if ((xfer->endpoint->is_synced == 0) || - (temp < (((xfer->nframes << shift) + 7) / 8))) { + if ((xfer->endpoint->is_synced == 0) || (temp < msframes)) { /* * If there is data underflow or the pipe queue is * empty we schedule the transfer a few frames ahead * of the current frame position. Else two isochronous * transfers might overlap. */ - xfer->endpoint->isoc_next = (nframes + 3) & DWC_OTG_FRAME_MASK; + xfer->endpoint->isoc_next = (framenum + 3) & DWC_OTG_FRAME_MASK; xfer->endpoint->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } @@ -3404,25 +3924,20 @@ dwc_otg_device_isoc_enter(struct usb_xfer *xfer) * compute how many milliseconds the insertion is ahead of the * current frame position: */ - temp = (xfer->endpoint->isoc_next - nframes) & DWC_OTG_FRAME_MASK; + temp = (xfer->endpoint->isoc_next - framenum) & DWC_OTG_FRAME_MASK; /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = - usb_isoc_time_expand(&sc->sc_bus, nframes) + temp + - (((xfer->nframes << shift) + 7) / 8); + usb_isoc_time_expand(&sc->sc_bus, framenum) + temp + msframes; /* setup TDs */ dwc_otg_setup_standard_chain(xfer); /* compute frame number for next insertion */ - xfer->endpoint->isoc_next += (xfer->nframes << shift); -} + xfer->endpoint->isoc_next += msframes; -static void -dwc_otg_device_isoc_start(struct usb_xfer *xfer) -{ /* start TD chain */ dwc_otg_start_standard_chain(xfer); } @@ -3987,8 +4502,8 @@ dwc_otg_xfer_setup(struct usb_setup_params *parm) * reasonable dummies: */ parm->hc_max_packet_size = 0x500; - parm->hc_max_packet_count = 1; - parm->hc_max_frame_size = 0x500; + parm->hc_max_packet_count = 3; + parm->hc_max_frame_size = 3 * 0x500; usbd_transfer_setup_sub(parm); @@ -4045,6 +4560,7 @@ dwc_otg_xfer_setup(struct usb_setup_params *parm) /* init TD */ td->max_packet_size = xfer->max_packet_size; + td->max_packet_count = xfer->max_packet_count; td->ep_no = ep_no; td->obj_next = last_obj; @@ -4081,40 +4597,7 @@ dwc_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, /* not supported */ return; } - } else { - uint16_t mps; - - mps = UGETW(edesc->wMaxPacketSize); - - /* Apply limitations of our USB host driver */ - - switch (udev->speed) { - case USB_SPEED_HIGH: - if (mps > 512) { - DPRINTF("wMaxPacketSize=0x%04x" - "is not supported\n", (int)mps); - /* not supported */ - return; - } - break; - - case USB_SPEED_FULL: - case USB_SPEED_LOW: - if (mps > 188) { - DPRINTF("wMaxPacketSize=0x%04x" - "is not supported\n", (int)mps); - /* not supported */ - return; - } - break; - - default: - DPRINTF("Invalid device speed\n"); - /* not supported */ - return; - } } - if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS) ep->methods = &dwc_otg_device_isoc_methods; else diff --git a/sys/dev/usb/controller/dwc_otg.h b/sys/dev/usb/controller/dwc_otg.h index a301698..2ae2eae 100644 --- a/sys/dev/usb/controller/dwc_otg.h +++ b/sys/dev/usb/controller/dwc_otg.h @@ -57,23 +57,24 @@ struct dwc_otg_td { uint32_t hcsplt; /* HOST CFG */ uint16_t max_packet_size; /* packet_size */ uint16_t npkt; + uint8_t max_packet_count; /* packet_count */ uint8_t errcnt; uint8_t tmr_res; uint8_t tmr_val; - uint8_t curr_frame; uint8_t ep_no; uint8_t channel; + uint8_t tt_index; /* TT data */ + uint8_t tt_start_slot; /* TT data */ + uint8_t tt_complete_slot; /* TT data */ + uint8_t tt_xactpos; /* TT data */ 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 -#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 +#define DWC_CHAN_ST_WAIT_C_PKT 4 +#define DWC_CHAN_ST_TX_PKT_ISOC 5 +#define DWC_CHAN_ST_TX_WAIT_ISOC 6 uint8_t error:1; uint8_t error_any:1; uint8_t error_stall:1; @@ -84,6 +85,7 @@ struct dwc_otg_td { uint8_t set_toggle:1; uint8_t got_short:1; uint8_t did_nak:1; + uint8_t tt_scheduled:1; }; struct dwc_otg_std_temp { @@ -103,6 +105,7 @@ struct dwc_otg_std_temp { uint8_t setup_alt_next; uint8_t did_stall; uint8_t bulk_or_control; + uint8_t tt_index; }; struct dwc_otg_config_desc { @@ -143,11 +146,18 @@ struct dwc_otg_profile { struct dwc_otg_chan_state { uint32_t hcint; + uint32_t tx_size; uint8_t wait_sof; uint8_t allocated; uint8_t suspended; }; +struct dwc_otg_tt_info { + uint16_t bytes_used; + uint8_t slot_index; + uint8_t dummy; +}; + struct dwc_otg_softc { struct usb_bus sc_bus; union dwc_otg_hub_temp sc_hub_temp; @@ -163,9 +173,11 @@ struct dwc_otg_softc { bus_space_handle_t sc_io_hdl; uint32_t sc_rx_bounce_buffer[1024 / 4]; - uint32_t sc_tx_bounce_buffer[(512 * DWC_OTG_MAX_TXP) / 4]; + uint32_t sc_tx_bounce_buffer[MAX(512 * DWC_OTG_MAX_TXP, 1024) / 4]; uint32_t sc_fifo_size; + uint32_t sc_tx_max_size; + uint32_t sc_tx_cur_size; uint32_t sc_irq_mask; uint32_t sc_last_rx_status; uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS]; @@ -174,7 +186,9 @@ struct dwc_otg_softc { uint32_t sc_tmr_val; uint32_t sc_hprt_val; + struct dwc_otg_tt_info sc_tt_info[DWC_OTG_MAX_DEVICES]; uint16_t sc_active_rx_ep; + uint16_t sc_last_frame_num; uint8_t sc_timer_active; uint8_t sc_dev_ep_max; diff --git a/sys/dev/usb/controller/dwc_otgreg.h b/sys/dev/usb/controller/dwc_otgreg.h index f59f48c..cd2f45d 100644 --- a/sys/dev/usb/controller/dwc_otgreg.h +++ b/sys/dev/usb/controller/dwc_otgreg.h @@ -536,6 +536,11 @@ #define HCSPLT_COMPSPLT (1<<16) #define HCSPLT_XACTPOS_SHIFT 14 #define HCSPLT_XACTPOS_MASK 0x0000c000 +#define HCSPLT_XACTPOS_MIDDLE 0 +#define HCSPLT_XACTPOS_LAST 1 +#define HCSPLT_XACTPOS_BEGIN 2 +#define HCSPLT_XACTPOS_ALL 3 +#define HCSPLT_XACTLEN_MAX 188 /* bytes */ #define HCSPLT_HUBADDR_SHIFT 7 #define HCSPLT_HUBADDR_MASK 0x00003f80 #define HCSPLT_PRTADDR_SHIFT 0 @@ -545,6 +550,11 @@ (HCINT_BBLERR | HCINT_XACTERR) #define HCINT_RETRY \ (HCINT_DATATGLERR | HCINT_FRMOVRUN | HCINT_NAK) +#define HCINT_DEFAULT_MASK \ + (HCINT_STALL | HCINT_BBLERR | \ + HCINT_XACTERR | HCINT_NAK | HCINT_ACK | HCINT_NYET | \ + HCINT_CHHLTD | HCINT_FRMOVRUN | \ + HCINT_DATATGLERR) #define HCINT_SOFTWARE_ONLY (1<<20) /* BSD only */ #define HCINT_DATATGLERR (1<<10) |