Date: Fri, 9 May 2014 14:23:07 +0000 (UTC) From: Hans Petter Selasky <hselasky@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r265777 - head/sys/dev/usb/controller Message-ID: <201405091423.s49EN7tj086880@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Fri May 9 14:23:06 2014 New Revision: 265777 URL: http://svnweb.freebsd.org/changeset/base/265777 Log: Multiple DWC OTG host mode related fixes and improvements: - Rework how we allocate and free USB host channels, so that we only allocate a channel if there is a real packet going out on the USB cable. - Use BULK type for control data and status, due to instabilities in the HW it appears. - Split FIFO TX levels into one for the periodic FIFO and one for the non-periodic FIFO. - Use correct HFNUM mask when scheduling host transactions. The HFNUM register does not count the full 16-bit range. - Correct START/COMPLETION slot for TT transactions. For INTERRUPT and ISOCHRONOUS type transactions the hardware always respects the ODDFRM bit, which means we need to allocate multiple host channels when processing such endpoints, to not miss any so-called complete split opportunities. - When doing ISOCHRONOUS OUT transfers through a TT send all data payload in a single ALL-burst. This deacreases the likelyhood for isochronous data underruns. - Fixed unbalanced unlock in case of "dwc_otg_init_fifo()" failure. - Increase interrupt priority. MFC after: 2 weeks Modified: head/sys/dev/usb/controller/dwc_otg.c head/sys/dev/usb/controller/dwc_otg.h head/sys/dev/usb/controller/dwc_otg_fdt.c 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 May 9 14:15:48 2014 (r265776) +++ head/sys/dev/usb/controller/dwc_otg.c Fri May 9 14:23:06 2014 (r265777) @@ -92,6 +92,9 @@ #define DWC_OTG_PC2SC(pc) \ DWC_OTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus) +#define DWC_OTG_PC2UDEV(pc) \ + (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev) + #define DWC_OTG_MSK_GINT_ENABLED \ (GINTMSK_ENUMDONEMSK | \ GINTMSK_USBRSTMSK | \ @@ -136,8 +139,8 @@ static dwc_otg_cmd_t dwc_otg_host_data_r static void dwc_otg_device_done(struct usb_xfer *, usb_error_t); static void dwc_otg_do_poll(struct usb_bus *); static void dwc_otg_standard_done(struct usb_xfer *); -static void dwc_otg_root_intr(struct dwc_otg_softc *sc); -static void dwc_otg_interrupt_poll(struct dwc_otg_softc *sc); +static void dwc_otg_root_intr(struct dwc_otg_softc *); +static void dwc_otg_interrupt_poll(struct dwc_otg_softc *); /* * Here is a configuration that the chip supports. @@ -177,26 +180,33 @@ dwc_otg_init_fifo(struct dwc_otg_softc * fifo_size = sc->sc_fifo_size; - fifo_regs = 4 * (sc->sc_dev_ep_max + sc->sc_dev_in_ep_max); + /* + * NOTE: Reserved fixed size area at end of RAM, which must + * not be allocated to the FIFOs: + */ + fifo_regs = 4 * 16; - if (fifo_size >= fifo_regs) - fifo_size -= fifo_regs; - else - fifo_size = 0; + if (fifo_size < fifo_regs) { + DPRINTF("Too little FIFO\n"); + return (EINVAL); + } + + /* subtract FIFO regs from total once */ + fifo_size -= fifo_regs; /* split equally for IN and OUT */ fifo_size /= 2; - DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4); - - /* align to 4-bytes */ + /* align to 4 bytes boundary */ fifo_size &= ~3; + /* set global receive FIFO size */ + DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4); + tx_start = fifo_size; - if (fifo_size < 0x40) { + if (fifo_size < 64) { DPRINTFN(-1, "Not enough data space for EP0 FIFO.\n"); - USB_BUS_UNLOCK(&sc->sc_bus); return (EINVAL); } @@ -205,14 +215,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc * /* reset active endpoints */ sc->sc_active_rx_ep = 0; - /* reset TX size */ - sc->sc_tx_cur_size = 0; - - /* reset TT info */ - memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); - + /* split equally for periodic and non-periodic */ fifo_size /= 2; + /* align to 4 bytes boundary */ + fifo_size &= ~3; + DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, ((fifo_size / 4) << 16) | (tx_start / 4)); @@ -228,7 +236,11 @@ dwc_otg_init_fifo(struct dwc_otg_softc * ((fifo_size / 4) << 16) | (tx_start / 4)); - /* store maximum TX FIFO size */ + /* reset FIFO TX levels */ + sc->sc_tx_cur_p_level = 0; + sc->sc_tx_cur_np_level = 0; + + /* store maximum periodic and non-periodic FIFO TX size */ sc->sc_tx_max_size = fifo_size; /* disable all host channel interrupts */ @@ -311,11 +323,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc * /* reset active endpoints */ sc->sc_active_rx_ep = 0; - /* reset TX size */ - sc->sc_tx_cur_size = 0; + /* reset periodic and non-periodic FIFO TX size */ + sc->sc_tx_max_size = fifo_size; - /* reset TT info */ - memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); + /* reset FIFO TX levels */ + sc->sc_tx_cur_p_level = 0; + sc->sc_tx_cur_np_level = 0; } return (0); } @@ -555,125 +568,70 @@ dwc_otg_clear_hcint(struct dwc_otg_softc sc->sc_chan_state[x].hcint = 0; } -/* - * This function waits until a DWC OTG host channel is ready to be - * used again: - */ static uint8_t -dwc_otg_host_channel_wait(struct dwc_otg_td *td) +dwc_otg_host_channel_alloc(struct dwc_otg_td *td, uint8_t which) { struct dwc_otg_softc *sc; + uint32_t tx_p_size; + uint32_t tx_np_size; uint8_t x; - x = td->channel; - - DPRINTF("CH=%d\n", x); - - /* get pointer to softc */ - sc = DWC_OTG_PC2SC(td->pc); - - if (sc->sc_chan_state[x].wait_sof == 0) { - dwc_otg_clear_hcint(sc, x); - return (1); /* done */ - } - - if (x == 0) - return (0); /* wait */ - - /* find new disabled channel */ - for (x = 1; x != sc->sc_host_ch_max; x++) { - - if (sc->sc_chan_state[x].allocated) - continue; - if (sc->sc_chan_state[x].wait_sof != 0) - continue; - - 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; - } - - /* clear interrupts */ - dwc_otg_clear_hcint(sc, x); - - DPRINTF("CH=%d HCCHAR=0x%08x " - "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt); - - /* ack any pending messages */ - if (sc->sc_last_rx_status != 0 && - GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == td->channel) { - /* get rid of message */ - dwc_otg_common_rx_ack(sc); - } - - /* move active channel */ - sc->sc_active_rx_ep &= ~(1 << td->channel); - sc->sc_active_rx_ep |= (1 << x); - - /* set channel */ - td->channel = x; - - return (1); /* new channel allocated */ - } - return (0); /* wait */ -} - -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; - - if (td->channel < DWC_OTG_MAX_CHANNELS) + if (td->channel[which] < DWC_OTG_MAX_CHANNELS) return (0); /* already allocated */ + /* check if device is suspended */ + if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0) + return (1); /* busy - cannot transfer data */ + /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - 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) { + /* compute needed TX FIFO size */ + if (td->ep_type == UE_CONTROL) { + /* RX and TX transactions */ + tx_p_size = 0; + tx_np_size = td->max_packet_size; + } else if ((td->hcchar & HCCHAR_EPDIR) == HCCHAR_EPDIR_OUT) { + if (td->ep_type == UE_INTERRUPT || + td->ep_type == UE_ISOCHRONOUS) { + tx_p_size = td->max_packet_size; + tx_np_size = 0; + if (td->hcsplt != 0 && tx_p_size > HCSPLT_XACTLEN_BURST) + tx_p_size = HCSPLT_XACTLEN_BURST; + if ((sc->sc_tx_cur_p_level + tx_p_size) > sc->sc_tx_max_size) { DPRINTF("Too little FIFO space\n"); return (1); /* too little FIFO */ } } else { - tx_size = 0; + tx_p_size = 0; + tx_np_size = td->max_packet_size; + if (td->hcsplt != 0 && tx_np_size > HCSPLT_XACTLEN_BURST) + tx_np_size = HCSPLT_XACTLEN_BURST; + if ((sc->sc_tx_cur_np_level + tx_np_size) > sc->sc_tx_max_size) { + DPRINTF("Too little FIFO space\n"); + return (1); /* too little FIFO */ + } } + } else { + /* not a TX transaction */ + tx_p_size = 0; + tx_np_size = 0; } - for (; x != max_channel; x++) { - - if (sc->sc_chan_state[x].allocated) + for (x = 0; x != sc->sc_host_ch_max; x++) { + if (sc->sc_chan_state[x].allocated != 0) continue; + /* check if channel is still enabled */ if (sc->sc_chan_state[x].wait_sof != 0) continue; sc->sc_chan_state[x].allocated = 1; - sc->sc_chan_state[x].tx_size = tx_size; + sc->sc_chan_state[x].tx_p_size = tx_p_size; + sc->sc_chan_state[x].tx_np_size = tx_np_size; - /* keep track of used FIFO */ - sc->sc_tx_cur_size += tx_size; + /* keep track of used TX FIFO, if any */ + sc->sc_tx_cur_p_level += tx_p_size; + sc->sc_tx_cur_np_level += tx_np_size; /* clear interrupts */ dwc_otg_clear_hcint(sc, x); @@ -685,54 +643,38 @@ dwc_otg_host_channel_alloc(struct dwc_ot sc->sc_active_rx_ep |= (1 << x); /* set channel */ - td->channel = x; + td->channel[which] = x; return (0); /* allocated */ } + /* wait a bit */ + dwc_otg_enable_sof_irq(sc); return (1); /* busy */ } static void -dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x) -{ - uint32_t hcchar; - if (sc->sc_chan_state[x].wait_sof != 0) - return; - hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); - if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) { - /* disable channel */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), - HCCHAR_CHENA | HCCHAR_CHDIS); - /* don't re-use channel until next SOF is transmitted */ - sc->sc_chan_state[x].wait_sof = 2; - } -} - -static void -dwc_otg_host_channel_free(struct dwc_otg_td *td) +dwc_otg_host_channel_free(struct dwc_otg_td *td, uint8_t which) { struct dwc_otg_softc *sc; uint8_t x; - if (td->channel >= DWC_OTG_MAX_CHANNELS) + if (td->channel[which] >= DWC_OTG_MAX_CHANNELS) return; /* already freed */ /* free channel */ - x = td->channel; - td->channel = DWC_OTG_MAX_CHANNELS; + x = td->channel[which]; + td->channel[which] = DWC_OTG_MAX_CHANNELS; DPRINTF("CH=%d\n", x); /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - - dwc_otg_host_channel_disable(sc, x); - + sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX; 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; + /* keep track of used TX FIFO, if any */ + sc->sc_tx_cur_p_level -= sc->sc_chan_state[x].tx_p_size; + sc->sc_tx_cur_np_level -= sc->sc_chan_state[x].tx_np_size; /* ack any pending messages */ if (sc->sc_last_rx_status != 0 && @@ -751,30 +693,33 @@ dwc_otg_host_setup_tx(struct dwc_otg_td struct dwc_otg_softc *sc; uint32_t hcint; uint32_t hcchar; - - if (dwc_otg_host_channel_alloc(td)) - goto busy; + uint8_t delta; /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - hcint = sc->sc_chan_state[td->channel].hcint; + if (td->channel[0] < DWC_OTG_MAX_CHANNELS) { + hcint = sc->sc_chan_state[td->channel[0]].hcint; - DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", - td->channel, td->state, hcint, - DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), - DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", + td->channel[0], td->state, hcint, + DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0]))); + } else { + hcint = 0; + goto check_state; + } 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); + DPRINTF("CH=%d STALL\n", td->channel[0]); td->error_stall = 1; td->error_any = 1; goto complete; } else if (hcint & HCINT_ERRORS) { - DPRINTF("CH=%d ERROR\n", td->channel); + DPRINTF("CH=%d ERROR\n", td->channel[0]); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { td->error_any = 1; @@ -782,34 +727,23 @@ dwc_otg_host_setup_tx(struct dwc_otg_td } } - /* channel must be disabled before we can complete the transfer */ - if (hcint & (HCINT_ERRORS | HCINT_RETRY | HCINT_ACK | HCINT_NYET)) { - - dwc_otg_host_channel_disable(sc, td->channel); - if (!(hcint & HCINT_ERRORS)) td->errcnt = 0; } +check_state: 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: 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)) { - if (!dwc_otg_host_channel_wait(td)) - break; + } else if (hcint & (HCINT_ACK | HCINT_NYET)) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; @@ -820,35 +754,22 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 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)) { - if (!dwc_otg_host_channel_wait(td)) - break; + } else if (hcint & (HCINT_ACK | HCINT_NYET)) { goto send_cpkt; } break; case DWC_CHAN_ST_WAIT_C_ANE: if (hcint & HCINT_NYET) { - if (!dwc_otg_host_channel_wait(td)) - break; goto send_cpkt; - } - if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { - if (!dwc_otg_host_channel_wait(td)) - break; + } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { td->did_nak = 1; td->tt_scheduled = 0; goto send_pkt; - } - if (hcint & HCINT_ACK) { - if (!dwc_otg_host_channel_wait(td)) - break; + } else if (hcint & HCINT_ACK) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; @@ -857,8 +778,6 @@ dwc_otg_host_setup_tx(struct dwc_otg_td break; case DWC_CHAN_ST_WAIT_C_PKT: - if (!dwc_otg_host_channel_wait(td)) - break; goto send_cpkt; default: @@ -867,20 +786,36 @@ dwc_otg_host_setup_tx(struct dwc_otg_td goto busy; send_pkt: + /* free existing channel, if any */ + dwc_otg_host_channel_free(td, 0); + if (sizeof(req) != td->remainder) { td->error_any = 1; goto complete; } if (td->hcsplt != 0) { - /* 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 */ + delta = td->tt_start_slot - sc->sc_last_frame_num - 1; + if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { + td->state = DWC_CHAN_ST_START; + goto busy; + } + delta = sc->sc_last_frame_num - td->tt_start_slot; + if (delta > 5) { + /* missed it */ + td->tt_scheduled = 0; td->state = DWC_CHAN_ST_START; - goto tt_wait; + goto busy; } + } + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(td, 0)) { + td->state = DWC_CHAN_ST_START; + goto busy; + } + + if (td->hcsplt != 0) { td->hcsplt &= ~HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_S_ANE; } else { @@ -889,60 +824,73 @@ send_pkt: usbd_copy_out(td->pc, 0, &req, sizeof(req)); - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); hcchar = td->hcchar; - hcchar &= ~HCCHAR_EPDIR_IN; + hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK); + hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; /* must enable channel before writing data to FIFO */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar); /* transfer data into FIFO */ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, - DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4); + DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4); /* store number of bytes transmitted */ td->tx_bytes = sizeof(req); - goto 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 */ + /* free existing channel, if any */ + dwc_otg_host_channel_free(td, 0); + + delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; + if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_WAIT_C_PKT; - goto tt_wait; + goto busy; + } + delta = sc->sc_last_frame_num - td->tt_start_slot; + if (delta > DWC_OTG_TT_SLOT_MAX) { + /* we missed the service interval */ + if (td->ep_type != UE_ISOCHRONOUS) + td->error_any = 1; + goto complete; } + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(td, 0)) { + td->state = DWC_CHAN_ST_WAIT_C_PKT; + goto busy; + } + /* wait until next slot before trying again */ td->tt_complete_slot++; td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); hcchar = td->hcchar; - hcchar &= ~HCCHAR_EPDIR_IN; + hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK); + hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; /* must enable channel before writing data to FIFO */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); - goto busy; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar); -tt_wait: - /* free allocated channel */ - dwc_otg_host_channel_free(td); busy: return (1); /* busy */ + complete: + dwc_otg_host_channel_free(td, 0); return (0); /* complete */ } @@ -1100,20 +1048,11 @@ static uint8_t dwc_otg_host_rate_check(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; - uint8_t ep_type; /* 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 (ep_type == UE_ISOCHRONOUS) { - + if (td->ep_type == UE_ISOCHRONOUS) { /* non TT isochronous traffic */ if ((td->tmr_val != 0) || (sc->sc_last_frame_num & (td->tmr_res - 1))) { @@ -1122,7 +1061,7 @@ dwc_otg_host_rate_check(struct dwc_otg_t td->tmr_val = 1; /* executed */ td->toggle = 0; - } else if (ep_type == UE_INTERRUPT) { + } else if (td->ep_type == UE_INTERRUPT) { if (!td->tt_scheduled) goto busy; td->tt_scheduled = 0; @@ -1144,23 +1083,24 @@ 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)) - goto busy; + uint8_t delta; + uint8_t channel; /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); + channel = td->channel[td->tt_channel_tog]; - ep_type = ((td->hcchar & - HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT); + if (channel < DWC_OTG_MAX_CHANNELS) { + hcint = sc->sc_chan_state[channel].hcint; - hcint = sc->sc_chan_state[td->channel].hcint; - - DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", - td->channel, td->state, hcint, - DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), - DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", + channel, td->state, hcint, + DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel))); + } else { + hcint = 0; + goto check_state; + } /* check interrupt bits */ @@ -1168,37 +1108,26 @@ dwc_otg_host_data_rx(struct dwc_otg_td * HCINT_ACK | HCINT_NYET)) { /* give success bits priority over failure bits */ } else if (hcint & HCINT_STALL) { - DPRINTF("CH=%d STALL\n", td->channel); + DPRINTF("CH=%d STALL\n", channel); td->error_stall = 1; td->error_any = 1; goto complete; } else if (hcint & HCINT_ERRORS) { - DPRINTF("CH=%d ERROR\n", td->channel); + DPRINTF("CH=%d ERROR\n", channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { - if (ep_type != UE_ISOCHRONOUS) { + if (td->ep_type != UE_ISOCHRONOUS) { td->error_any = 1; goto complete; } } } - /* channel must be disabled before we can complete the transfer */ - - if (hcint & (HCINT_ERRORS | HCINT_RETRY | - HCINT_ACK | HCINT_NYET)) { - - dwc_otg_host_channel_disable(sc, td->channel); - - if (!(hcint & HCINT_ERRORS)) - td->errcnt = 0; - } - /* check endpoint status */ if (sc->sc_last_rx_status == 0) goto check_state; - if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->channel) + if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel) goto check_state; switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) { @@ -1220,7 +1149,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td * count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); /* check for isochronous transfer or high-speed bandwidth endpoint */ - if (ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) { + if (td->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 { @@ -1269,8 +1198,8 @@ dwc_otg_host_data_rx(struct dwc_otg_td * td->remainder -= count; td->offset += count; - hcint |= HCINT_SOFTWARE_ONLY | HCINT_ACK; - sc->sc_chan_state[td->channel].hcint = hcint; + hcint |= HCINT_SOFTWARE_ONLY; + sc->sc_chan_state[channel].hcint = hcint; break; default: @@ -1280,10 +1209,14 @@ dwc_otg_host_data_rx(struct dwc_otg_td * dwc_otg_common_rx_ack(sc); check_state: + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; + } + switch (td->state) { case DWC_CHAN_ST_START: - if (!dwc_otg_host_channel_wait(td)) - break; if (td->hcsplt != 0) goto receive_spkt; else @@ -1291,38 +1224,29 @@ check_state: case DWC_CHAN_ST_WAIT_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { - if (!dwc_otg_host_channel_wait(td)) - break; - td->did_nak = 1; td->tt_scheduled = 0; if (td->hcsplt != 0) goto receive_spkt; else goto receive_pkt; - } - if (!(hcint & HCINT_SOFTWARE_ONLY)) { - if (hcint & HCINT_NYET) { - if (ep_type == UE_ISOCHRONOUS) { - /* we missed the service interval */ - goto complete; - } - if (!dwc_otg_host_channel_wait(td)) - break; + } else if (hcint & HCINT_NYET) { + if (td->hcsplt != 0) { + /* try again */ goto receive_pkt; + } else { + /* not a valid token for IN endpoints */ + td->error_any = 1; + goto complete; } - break; - } - if (hcint & (HCINT_ACK | HCINT_NYET)) { - if (!dwc_otg_host_channel_wait(td)) - break; - - if (ep_type == UE_ISOCHRONOUS) { + } else if (hcint & HCINT_ACK) { + if (td->ep_type == UE_ISOCHRONOUS) { /* check if we are complete */ if ((td->remainder == 0) || - (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) + (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) { goto complete; - + } + /* get another packet */ goto receive_pkt; } else { /* check if we are complete */ @@ -1350,23 +1274,17 @@ check_state: * 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)) { - if (!dwc_otg_host_channel_wait(td)) - break; + } else if (hcint & HCINT_NYET) { + td->tt_scheduled = 0; + goto receive_spkt; + } else if (hcint & HCINT_ACK) goto receive_pkt; - } break; case DWC_CHAN_ST_WAIT_C_PKT: - if (!dwc_otg_host_channel_wait(td)) - break; goto receive_pkt; default: @@ -1375,103 +1293,149 @@ check_state: goto busy; receive_pkt: + /* free existing channel, if any */ + dwc_otg_host_channel_free(td, td->tt_channel_tog); + 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 */ + delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; + if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_WAIT_C_PKT; - goto tt_wait; + goto busy; } - /* wait until next slot before trying again */ - td->tt_complete_slot++; - - /* set toggle, if any */ - if (td->set_toggle) { - td->set_toggle = 0; - td->toggle = 1; + delta = sc->sc_last_frame_num - td->tt_start_slot; + if (delta > DWC_OTG_TT_SLOT_MAX) { + /* we missed the service interval */ + if (td->ep_type != UE_ISOCHRONOUS) + td->error_any = 1; + goto complete; } + /* complete split */ td->hcsplt |= HCSPLT_COMPSPLT; - 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); + td->state = DWC_CHAN_ST_WAIT_C_PKT; + goto busy; + } + + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(td, td->tt_channel_tog)) { + td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; - } else { - count = td->max_packet_size; } + + channel = td->channel[td->tt_channel_tog]; + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + td->state = DWC_CHAN_ST_WAIT_ANE; /* receive one packet */ - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), - (count << HCTSIZ_XFERSIZE_SHIFT) | + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), + (td->max_packet_size << 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; + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); 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); + /* check if other channel is allocated */ + if (td->channel[td->tt_channel_tog ^ 1] < DWC_OTG_MAX_CHANNELS) { + /* second - receive after next SOF event */ + if ((sc->sc_last_frame_num & 1) != 0) + hcchar |= HCCHAR_ODDFRM; + else + hcchar &= ~HCCHAR_ODDFRM; + + /* other channel is next */ + td->tt_channel_tog ^= 1; + td->tt_complete_slot++; + + /* must enable channel before data can be received */ + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); + } else { + /* first - receive after second next SOF event */ + if ((sc->sc_last_frame_num & 1) == 0) + hcchar |= HCCHAR_ODDFRM; + else + hcchar &= ~HCCHAR_ODDFRM; + + /* must enable channel before data can be received */ + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); + + if (td->hcsplt != 0) { + if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) { + /* allocate a second channel */ + td->tt_channel_tog ^= 1; + goto receive_pkt; + } else { + td->tt_complete_slot++; + } + } + } goto busy; receive_spkt: - /* 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 */ + /* free existing channel(s), if any */ + dwc_otg_host_channel_free(td, 0); + dwc_otg_host_channel_free(td, 1); + + delta = td->tt_start_slot - sc->sc_last_frame_num - 1; + if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { td->state = DWC_CHAN_ST_START; - goto tt_wait; + goto busy; } - if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) { - /* set return state */ + delta = sc->sc_last_frame_num - td->tt_start_slot; + if (delta > 5) { + /* missed it */ + td->tt_scheduled = 0; td->state = DWC_CHAN_ST_START; - goto tt_wait; + goto busy; } - /* send ASAP */ - if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1)) - td->hcchar |= HCCHAR_ODDFRM; - else - td->hcchar &= ~HCCHAR_ODDFRM; + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(td, 0)) { + td->state = DWC_CHAN_ST_START; + goto busy; + } + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405091423.s49EN7tj086880>