From owner-svn-src-all@FreeBSD.ORG Sun Jun 8 20:39:39 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 9D4CA26B; Sun, 8 Jun 2014 20:39:39 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 882CA26DC; Sun, 8 Jun 2014 20:39:39 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s58KddKW021708; Sun, 8 Jun 2014 20:39:39 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s58Kdd3d021706; Sun, 8 Jun 2014 20:39:39 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201406082039.s58Kdd3d021706@svn.freebsd.org> From: Hans Petter Selasky Date: Sun, 8 Jun 2014 20:39:39 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r267242 - stable/10/sys/dev/usb/controller X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 08 Jun 2014 20:39:39 -0000 Author: hselasky Date: Sun Jun 8 20:39:39 2014 New Revision: 267242 URL: http://svnweb.freebsd.org/changeset/base/267242 Log: MFC r267044, r267120, r267210 and r267211: Multiple fixes for FULL and LOW speed USB transfers going through the DWC OTG as split transactions. INTERRUPT transfers should have a higher chance of success after this series of patches and the chance of data loss should be reduced. Modified: stable/10/sys/dev/usb/controller/dwc_otg.c stable/10/sys/dev/usb/controller/dwc_otg.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/dev/usb/controller/dwc_otg.c ============================================================================== --- stable/10/sys/dev/usb/controller/dwc_otg.c Sun Jun 8 20:20:08 2014 (r267241) +++ stable/10/sys/dev/usb/controller/dwc_otg.c Sun Jun 8 20:39:39 2014 (r267242) @@ -98,6 +98,8 @@ GINTMSK_USBSUSPMSK | \ GINTMSK_IEPINTMSK | \ GINTMSK_SESSREQINTMSK | \ + GINTMSK_RXFLVLMSK | \ + GINTMSK_HCHINTMSK | \ GINTMSK_OTGINTMSK | \ GINTMSK_PRTINTMSK) @@ -239,7 +241,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc * for (x = 0; x != sc->sc_host_ch_max; x++) { /* disable all host interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), + HCINT_DEFAULT_MASK); } DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ, @@ -256,8 +259,9 @@ dwc_otg_init_fifo(struct dwc_otg_softc * /* store maximum periodic and non-periodic FIFO TX size */ sc->sc_tx_max_size = fifo_size; - /* disable all host channel interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0); + /* enable all host channel interrupts */ + DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, + (1U << sc->sc_host_ch_max) - 1U); } if (mode == DWC_MODE_DEVICE) { @@ -568,11 +572,9 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof { DPRINTFN(5, "RX status clear\n"); - if (sc->sc_flags.status_device_mode != 0) { - /* enable RX FIFO level interrupt */ - sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - } + /* enable RX FIFO level interrupt */ + sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* clear cached status */ sc->sc_last_rx_status = 0; @@ -592,13 +594,13 @@ dwc_otg_clear_hcint(struct dwc_otg_softc } static uint8_t -dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which, uint8_t is_out) +dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t is_out) { uint32_t tx_p_size; uint32_t tx_np_size; uint8_t x; - if (td->channel[which] < DWC_OTG_MAX_CHANNELS) + if (td->channel < DWC_OTG_MAX_CHANNELS) return (0); /* already allocated */ /* check if device is suspended */ @@ -607,8 +609,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot /* compute needed TX FIFO size */ if (is_out != 0) { - if (td->ep_type == UE_INTERRUPT || - td->ep_type == UE_ISOCHRONOUS) { + if (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) @@ -658,7 +659,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot sc->sc_active_rx_ep |= (1 << x); /* set channel */ - td->channel[which] = x; + td->channel = x; return (0); /* allocated */ } @@ -668,16 +669,16 @@ dwc_otg_host_channel_alloc(struct dwc_ot } static void -dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which) +dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { uint8_t x; - if (td->channel[which] >= DWC_OTG_MAX_CHANNELS) + if (td->channel >= DWC_OTG_MAX_CHANNELS) return; /* already freed */ /* free channel */ - x = td->channel[which]; - td->channel[which] = DWC_OTG_MAX_CHANNELS; + x = td->channel; + td->channel = DWC_OTG_MAX_CHANNELS; DPRINTF("CH=%d\n", x); @@ -687,7 +688,7 @@ dwc_otg_host_channel_free(struct dwc_otg * after a fixed given amount of time the host channel is no * longer doing any USB traffic: */ - if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) { + if (td->ep_type == UE_ISOCHRONOUS) { /* double buffered */ sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX; } else { @@ -707,6 +708,18 @@ dwc_otg_host_channel_free(struct dwc_otg sc->sc_active_rx_ep &= ~(1 << x); } +static void +dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) +{ + /* dump any pending messages */ + if (sc->sc_last_rx_status != 0) { + if (td->channel < DWC_OTG_MAX_CHANNELS && + td->channel == GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) { + dwc_otg_common_rx_ack(sc); + } + } +} + static uint8_t dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { @@ -715,13 +728,15 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof uint32_t hcchar; uint8_t delta; - if (td->channel[0] < DWC_OTG_MAX_CHANNELS) { - hcint = sc->sc_chan_state[td->channel[0]].hcint; + dwc_otg_host_dump_rx(sc, td); + + if (td->channel < DWC_OTG_MAX_CHANNELS) { + 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[0], td->state, hcint, - DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])), - DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0]))); + td->channel, td->state, hcint, + DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); } else { hcint = 0; goto check_state; @@ -731,12 +746,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof HCINT_ACK | HCINT_NYET)) { /* give success bits priority over failure bits */ } else if (hcint & HCINT_STALL) { - DPRINTF("CH=%d STALL\n", td->channel[0]); + DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; goto complete; } else if (hcint & HCINT_ERRORS) { - DPRINTF("CH=%d ERROR\n", td->channel[0]); + DPRINTF("CH=%d ERROR\n", td->channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { td->error_any = 1; @@ -804,7 +819,7 @@ check_state: send_pkt: /* free existing channel, if any */ - dwc_otg_host_channel_free(sc, td, 0); + dwc_otg_host_channel_free(sc, td); if (sizeof(req) != td->remainder) { td->error_any = 1; @@ -827,7 +842,7 @@ send_pkt: } /* allocate a new channel */ - if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) { + if (dwc_otg_host_channel_alloc(sc, td, 1)) { td->state = DWC_CHAN_ST_START; goto busy; } @@ -841,23 +856,26 @@ send_pkt: usbd_copy_out(td->pc, 0, &req, sizeof(req)); - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); hcchar = td->hcchar; 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[0]), hcchar); + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); /* transfer data into FIFO */ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, - DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4); + DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4); + + /* wait until next slot before trying complete split */ + td->tt_complete_slot = sc->sc_last_frame_num + 1; /* store number of bytes transmitted */ td->tx_bytes = sizeof(req); @@ -865,7 +883,7 @@ send_pkt: send_cpkt: /* free existing channel, if any */ - dwc_otg_host_channel_free(sc, td, 0); + dwc_otg_host_channel_free(sc, td); delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { @@ -880,34 +898,34 @@ send_cpkt: goto complete; } /* allocate a new channel */ - if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) { + if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } - /* wait until next slot before trying again */ - td->tt_complete_slot++; + /* wait until next slot before trying complete split */ + td->tt_complete_slot = sc->sc_last_frame_num + 1; td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]), + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); hcchar = td->hcchar; 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[0]), hcchar); + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); busy: return (1); /* busy */ complete: - dwc_otg_host_channel_free(sc, td, 0); + dwc_otg_host_channel_free(sc, td); return (0); /* complete */ } @@ -1085,55 +1103,21 @@ busy: } static uint8_t -dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) +dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { - uint32_t hcint; - uint32_t hcchar; uint32_t count; - uint8_t delta; uint8_t channel; - channel = td->channel[td->tt_channel_tog]; - - if (channel < DWC_OTG_MAX_CHANNELS) { - hcint = sc->sc_chan_state[channel].hcint; - - 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 */ - - 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", channel); - td->error_stall = 1; - td->error_any = 1; - goto complete; - } else if (hcint & HCINT_ERRORS) { - DPRINTF("CH=%d ERROR\n", channel); - td->errcnt++; - if (td->hcsplt != 0 || td->errcnt >= 3) { - if (td->ep_type != UE_ISOCHRONOUS) { - td->error_any = 1; - goto complete; - } - } - } - /* check endpoint status */ if (sc->sc_last_rx_status == 0) - goto check_state; + goto busy; + + channel = td->channel; + if (channel >= DWC_OTG_MAX_CHANNELS) + goto busy; if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel) - goto check_state; + goto busy; switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) { case GRXSTSRH_IN_DATA: @@ -1141,7 +1125,7 @@ dwc_otg_host_data_rx(struct dwc_otg_soft DPRINTF("DATA ST=%d STATUS=0x%08x\n", (int)td->state, (int)sc->sc_last_rx_status); - if (hcint & HCINT_SOFTWARE_ONLY) { + if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) { /* * When using SPLIT transactions on interrupt * endpoints, sometimes data occurs twice. @@ -1203,21 +1187,71 @@ dwc_otg_host_data_rx(struct dwc_otg_soft td->remainder -= count; td->offset += count; - hcint |= HCINT_SOFTWARE_ONLY; - sc->sc_chan_state[channel].hcint = hcint; + sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY; break; - default: break; } /* release FIFO */ dwc_otg_common_rx_ack(sc); +busy: + return (0); +complete: + return (1); +} -check_state: - if (hcint & (HCINT_ERRORS | HCINT_RETRY | - HCINT_ACK | HCINT_NYET)) { - if (!(hcint & HCINT_ERRORS)) - td->errcnt = 0; +static uint8_t +dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td) +{ + uint32_t hcint; + uint32_t hcchar; + uint8_t delta; + uint8_t channel; + + channel = td->channel; + + if (channel < DWC_OTG_MAX_CHANNELS) { + hcint = sc->sc_chan_state[channel].hcint; + + 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))); + + /* check interrupt bits */ + 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", channel); + td->error_stall = 1; + td->error_any = 1; + goto complete; + } else if (hcint & HCINT_ERRORS) { + DPRINTF("CH=%d ERROR\n", channel); + td->errcnt++; + if (td->hcsplt != 0 || td->errcnt >= 3) { + if (td->ep_type != UE_ISOCHRONOUS) { + td->error_any = 1; + goto complete; + } + } + } + + /* check channels for data, if any */ + if (dwc_otg_host_data_rx_sub(sc, td)) + goto complete; + + /* refresh interrupt status */ + hcint = sc->sc_chan_state[channel].hcint; + + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; + } + } else { + hcint = 0; } switch (td->state) { @@ -1229,6 +1263,22 @@ check_state: case DWC_CHAN_ST_WAIT_ANE: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (td->ep_type == UE_INTERRUPT) { + /* + * The USB specification does not + * mandate a particular data toggle + * value for USB INTERRUPT + * transfers. Switch the data toggle + * value to receive the packet + * correctly: + */ + if (hcint & HCINT_DATATGLERR) { + DPRINTF("Retrying packet due to " + "data toggle error\n"); + td->toggle ^= 1; + goto receive_pkt; + } + } td->did_nak++; td->tt_scheduled = 0; if (td->hcsplt != 0) @@ -1306,7 +1356,7 @@ check_state: receive_pkt: /* free existing channel, if any */ - dwc_otg_host_channel_free(sc, td, td->tt_channel_tog); + dwc_otg_host_channel_free(sc, td); if (td->hcsplt != 0) { delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; @@ -1316,9 +1366,10 @@ receive_pkt: } 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) + if (td->ep_type != UE_ISOCHRONOUS) { + /* we missed the service interval */ td->error_any = 1; + } goto complete; } /* complete split */ @@ -1330,12 +1381,12 @@ receive_pkt: } /* allocate a new channel */ - if (dwc_otg_host_channel_alloc(sc, td, td->tt_channel_tog, 0)) { + if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } - channel = td->channel[td->tt_channel_tog]; + channel = td->channel; /* set toggle, if any */ if (td->set_toggle) { @@ -1357,46 +1408,23 @@ receive_pkt: hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; - /* 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++; + /* receive complete split ASAP */ + 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); - } 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); - /* must enable channel before data can be received */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); + /* wait until next slot before trying complete split */ + td->tt_complete_slot = sc->sc_last_frame_num + 1; - 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: /* free existing channel(s), if any */ - dwc_otg_host_channel_free(sc, td, 0); - dwc_otg_host_channel_free(sc, td, 1); + dwc_otg_host_channel_free(sc, td); delta = td->tt_start_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { @@ -1412,19 +1440,16 @@ receive_spkt: } /* allocate a new channel */ - if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) { + if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_START; goto busy; } - channel = td->channel[0]; + channel = td->channel; td->hcsplt &= ~HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_S_ANE; - /* reset channel toggle */ - td->tt_channel_tog = 0; - /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)); @@ -1440,14 +1465,16 @@ receive_spkt: hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; + /* wait until next slot before trying complete split */ + td->tt_complete_slot = sc->sc_last_frame_num + 1; + /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); busy: return (1); /* busy */ complete: - dwc_otg_host_channel_free(sc, td, 0); - dwc_otg_host_channel_free(sc, td, 1); + dwc_otg_host_channel_free(sc, td); return (0); /* complete */ } @@ -1569,7 +1596,9 @@ dwc_otg_host_data_tx(struct dwc_otg_soft uint8_t delta; uint8_t channel; - channel = td->channel[td->tt_channel_tog]; + dwc_otg_host_dump_rx(sc, td); + + channel = td->channel; if (channel < DWC_OTG_MAX_CHANNELS) { hcint = sc->sc_chan_state[channel].hcint; @@ -1578,36 +1607,34 @@ dwc_otg_host_data_tx(struct dwc_otg_soft 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; - } - 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", channel); - td->error_stall = 1; - td->error_any = 1; - goto complete; - } else if (hcint & HCINT_ERRORS) { - DPRINTF("CH=%d ERROR\n", channel); - td->errcnt++; - if (td->hcsplt != 0 || td->errcnt >= 3) { + 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", channel); + td->error_stall = 1; td->error_any = 1; goto complete; + } else if (hcint & HCINT_ERRORS) { + DPRINTF("CH=%d ERROR\n", channel); + td->errcnt++; + if (td->hcsplt != 0 || td->errcnt >= 3) { + td->error_any = 1; + goto complete; + } } - } - if (hcint & (HCINT_ERRORS | HCINT_RETRY | - HCINT_ACK | HCINT_NYET)) { + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { - if (!(hcint & HCINT_ERRORS)) - td->errcnt = 0; + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; + } + } else { + hcint = 0; } -check_state: switch (td->state) { case DWC_CHAN_ST_START: goto send_pkt; @@ -1694,16 +1721,16 @@ check_state: td->tt_xactpos++; /* free existing channel, if any */ - dwc_otg_host_channel_free(sc, td, td->tt_channel_tog); + dwc_otg_host_channel_free(sc, td); td->state = DWC_CHAN_ST_TX_PKT_ISOC; /* FALLTHROUGH */ case DWC_CHAN_ST_TX_PKT_ISOC: - if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) + if (dwc_otg_host_channel_alloc(sc, td, 1)) break; - channel = td->channel[0]; + channel = td->channel; goto send_isoc_pkt; default: break; @@ -1712,8 +1739,7 @@ check_state: send_pkt: /* free existing channel(s), if any */ - dwc_otg_host_channel_free(sc, td, 0); - dwc_otg_host_channel_free(sc, td, 1); + dwc_otg_host_channel_free(sc, td); if (td->hcsplt != 0) { delta = td->tt_start_slot - sc->sc_last_frame_num - 1; @@ -1734,12 +1760,12 @@ send_pkt: } /* allocate a new channel */ - if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) { + if (dwc_otg_host_channel_alloc(sc, td, 1)) { td->state = DWC_CHAN_ST_START; goto busy; } - channel = td->channel[0]; + channel = td->channel; /* set toggle, if any */ if (td->set_toggle) { @@ -1757,7 +1783,7 @@ send_isoc_pkt: count = td->remainder; if (count > HCSPLT_XACTLEN_BURST) { DPRINTF("TT overflow\n"); - td->error = 1; + td->error_any = 1; goto complete; } /* Update transaction position */ @@ -1883,7 +1909,7 @@ send_isoc_pkt: send_cpkt: /* free existing channel, if any */ - dwc_otg_host_channel_free(sc, td, td->tt_channel_tog); + dwc_otg_host_channel_free(sc, td); delta = td->tt_complete_slot - sc->sc_last_frame_num - 1; if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) { @@ -1899,12 +1925,12 @@ send_cpkt: } /* allocate a new channel */ - if (dwc_otg_host_channel_alloc(sc, td, td->tt_channel_tog, 0)) { + if (dwc_otg_host_channel_alloc(sc, td, 0)) { td->state = DWC_CHAN_ST_WAIT_C_PKT; goto busy; } - channel = td->channel[td->tt_channel_tog]; + channel = td->channel; td->hcsplt |= HCSPLT_COMPSPLT; td->state = DWC_CHAN_ST_WAIT_C_ANE; @@ -1917,48 +1943,22 @@ send_cpkt: hcchar = td->hcchar; hcchar &= ~HCCHAR_EPDIR_IN; - /* 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; - /* wait until next slot before trying again */ - 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; + /* receive complete split ASAP */ + 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); + /* 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 send_cpkt; - } else { - /* wait until next slot before trying again */ - td->tt_complete_slot++; - } - } - } + /* wait until next slot before trying complete split */ + td->tt_complete_slot = sc->sc_last_frame_num + 1; busy: return (1); /* busy */ complete: - dwc_otg_host_channel_free(sc, td, 0); - dwc_otg_host_channel_free(sc, td, 1); + dwc_otg_host_channel_free(sc, td); return (0); /* complete */ } @@ -2348,6 +2348,14 @@ dwc_otg_host_channel_disable(struct dwc_ sc->sc_chan_state[x].tx_np_size = 0; } +static uint16_t +dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo) +{ + if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX) + pinfo->slot_index++; + return (pinfo->slot_index); +} + static uint8_t dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc) { @@ -2356,6 +2364,7 @@ dwc_otg_update_host_transfer_schedule_lo struct usb_xfer *xfer_next; struct dwc_otg_td *td; uint16_t temp; + uint16_t slot; uint8_t x; temp = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK; @@ -2377,11 +2386,53 @@ dwc_otg_update_host_transfer_schedule_lo } if ((temp & 7) == 0) { + + /* reset the schedule */ + memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info)); + TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { td = xfer->td_transfer_cache; if (td == NULL || td->ep_type != UE_ISOCHRONOUS) continue; + /* check for IN direction */ + if ((td->hcchar & HCCHAR_EPDIR_IN) != 0) + continue; + + /* execute more frames */ + td->tmr_val = 0; + + sc->sc_needsof = 1; + + if (td->hcsplt == 0 || td->tt_scheduled != 0) + continue; + + /* compute slot */ + slot = dwc_otg_compute_isoc_rx_tt_slot( + sc->sc_tt_info + td->tt_index); + if (slot > 3) { + /* + * Not enough time to get complete + * split executed. + */ + continue; + } + /* Delayed start */ + td->tt_start_slot = temp + slot; + td->tt_scheduled = 1; + TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); + TAILQ_INSERT_TAIL(&head, xfer, wait_entry); + } + + TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) { + td = xfer->td_transfer_cache; + if (td == NULL || td->ep_type != UE_ISOCHRONOUS) + continue; + + /* check for OUT direction */ + if ((td->hcchar & HCCHAR_EPDIR_IN) == 0) + continue; + /* execute more frames */ td->tmr_val = 0; @@ -2391,8 +2442,7 @@ dwc_otg_update_host_transfer_schedule_lo continue; /* Start ASAP */ - td->tt_start_slot = temp + 0; - td->tt_complete_slot = temp + 2; + td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2418,8 +2468,7 @@ dwc_otg_update_host_transfer_schedule_lo } /* start ASAP */ - td->tt_start_slot = temp + 0; - td->tt_complete_slot = temp + 2; + td->tt_start_slot = temp; sc->sc_needsof = 1; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); @@ -2440,8 +2489,7 @@ dwc_otg_update_host_transfer_schedule_lo continue; /* start ASAP */ - td->tt_start_slot = temp + 0; - td->tt_complete_slot = temp + 1; + td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2462,8 +2510,7 @@ dwc_otg_update_host_transfer_schedule_lo continue; /* start ASAP */ - td->tt_start_slot = temp + 0; - td->tt_complete_slot = temp + 1; + td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2739,8 +2786,6 @@ dwc_otg_interrupt(void *arg) /* Disable SOF interrupt */ sc->sc_irq_mask &= ~GINTMSK_SOFMSK; - /* Enable RX frame interrupt */ - sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ @@ -2781,7 +2826,7 @@ dwc_otg_interrupt(void *arg) * suspend and RX frame interrupt: */ sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK); - sc->sc_irq_mask |= (GINTMSK_USBSUSPMSK | GINTMSK_RXFLVLMSK); + sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ @@ -2852,10 +2897,6 @@ dwc_otg_interrupt(void *arg) /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); - /* disable RX FIFO level interrupt */ - sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - /* update host frame interval */ dwc_otg_update_host_frame_interval(sc); } @@ -2935,12 +2976,10 @@ dwc_otg_setup_standard_chain_sub(struct td->set_toggle = 0; td->got_short = 0; td->did_nak = 0; - td->channel[0] = DWC_OTG_MAX_CHANNELS; - td->channel[1] = DWC_OTG_MAX_CHANNELS; + td->channel = DWC_OTG_MAX_CHANNELS; td->state = 0; td->errcnt = 0; td->tt_scheduled = 0; - td->tt_channel_tog = 0; td->tt_xactpos = HCSPLT_XACTPOS_BEGIN; } @@ -3156,9 +3195,21 @@ dwc_otg_setup_standard_chain(struct usb_ (xfer->max_packet_size << HCCHAR_MPS_SHIFT) | HCCHAR_CHENA; - /* XXX stability hack - possible HW issue */ - if (td->ep_type == UE_CONTROL) - hcchar |= (UE_BULK << HCCHAR_EPTYPE_SHIFT); + /* + * We are not always able to meet the timing + * requirements of the USB interrupt endpoint's + * complete split token, when doing transfers going + * via a transaction translator. Use the CONTROL + * transfer type instead of the INTERRUPT transfer + * type in general, as a means to workaround + * that. This trick should work for both FULL and LOW + * speed USB traffic going through a TT. For non-TT + * traffic it works aswell. The reason for using + * CONTROL type instead of BULK is that some TTs might + * reject LOW speed BULK traffic. + */ + if (td->ep_type == UE_INTERRUPT) + hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT); else hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT); @@ -3199,13 +3250,12 @@ dwc_otg_setup_standard_chain(struct usb_ break; case USB_SPEED_HIGH: hcsplt = 0; - if (td->ep_type == UE_ISOCHRONOUS || - td->ep_type == UE_INTERRUPT) { - hcchar |= ((xfer->max_packet_count & 3) - << HCCHAR_MC_SHIFT); - } if (td->ep_type == UE_INTERRUPT) { uint32_t ival; +#if 0 + hcchar |= ((xfer->max_packet_count & 3) + << HCCHAR_MC_SHIFT); +#endif ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE; if (ival == 0) ival = 1; @@ -3214,6 +3264,8 @@ dwc_otg_setup_standard_chain(struct usb_ td->tmr_val = sc->sc_tmr_val + ival; td->tmr_res = ival; } else if (td->ep_type == UE_ISOCHRONOUS) { + hcchar |= ((xfer->max_packet_count & 3) + << HCCHAR_MC_SHIFT); td->tmr_val = 0; td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer); } else { @@ -3458,12 +3510,9 @@ dwc_otg_device_done(struct usb_xfer *xfe } else { struct dwc_otg_td *td; - td = xfer->td_transfer_first; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***