From owner-svn-src-all@FreeBSD.ORG Sun Sep 23 12:19:20 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 659C0106564A; Sun, 23 Sep 2012 12:19:20 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 4FA998FC08; Sun, 23 Sep 2012 12:19:20 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q8NCJK5J095996; Sun, 23 Sep 2012 12:19:20 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q8NCJKHF095993; Sun, 23 Sep 2012 12:19:20 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201209231219.q8NCJKHF095993@svn.freebsd.org> From: Hans Petter Selasky Date: Sun, 23 Sep 2012 12:19:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r240857 - head/sys/dev/usb/controller X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 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, 23 Sep 2012 12:19:20 -0000 Author: hselasky Date: Sun Sep 23 12:19:19 2012 New Revision: 240857 URL: http://svn.freebsd.org/changeset/base/240857 Log: DWC OTG host mode improvements. Add support for the 3-strikes and you are gone rule. Optimise use of channels so that when a channel is not ready another channel is used. Instead of using the SOF interrupt use the system timer to drive the host statemachine. This might give lower throughput and higher latency, but reduces the CPU usage significantly. The DWC OTG host mode support should not be considered for serious USB host controller applications. Some problems are still seen with LOW speed USB devices. Modified: head/sys/dev/usb/controller/dwc_otg.c head/sys/dev/usb/controller/dwc_otg.h head/sys/dev/usb/controller/dwc_otgreg.h Modified: head/sys/dev/usb/controller/dwc_otg.c ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.c Sun Sep 23 09:39:04 2012 (r240856) +++ head/sys/dev/usb/controller/dwc_otg.c Sun Sep 23 12:19:19 2012 (r240857) @@ -104,7 +104,7 @@ __FBSDID("$FreeBSD$"); #define DWC_OTG_USE_HSIC 0 #ifdef USB_DEBUG -static int dwc_otg_debug = 0; +static int dwc_otg_debug; static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW, 0, "USB DWC OTG"); SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RW, @@ -132,6 +132,7 @@ static void dwc_otg_device_done(struct u 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); /* * Here is a configuration that the chip supports. @@ -160,23 +161,6 @@ dwc_otg_get_hw_ep_profile(struct usb_dev *ppf = NULL; } -static void -dwc_otg_request_sof(struct dwc_otg_softc *sc) -{ - sc->sc_sof_refs++; - sc->sc_irq_mask |= GINTMSK_SOFMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); -} - -static void -dwc_otg_release_sof(struct dwc_otg_softc *sc) -{ - if (--(sc->sc_sof_refs) == 0) { - sc->sc_irq_mask &= ~GINTMSK_SOFMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - } -} - static int dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode) { @@ -227,12 +211,26 @@ dwc_otg_init_fifo(struct dwc_otg_softc * 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_XFERCOMPL | + HCINT_NAK | HCINT_ACK | HCINT_NYET | + HCINT_CHHLTD | HCINT_FRMOVRUN | + HCINT_DATATGLERR); + } + + /* enable host channel interrupts */ + DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, + (1U << sc->sc_host_ch_max) - 1U); } if (mode == DWC_MODE_DEVICE) { DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, - (0x10 << 16) | (tx_start / 4)); + (0x10 << 16) | (tx_start / 4)); fifo_size -= 0x40; tx_start += 0x40; @@ -487,11 +485,92 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof sc->sc_last_rx_status = 0; } +static void +dwc_otg_clear_hcint(struct dwc_otg_softc *sc, uint8_t x) +{ + uint32_t hcint; + + hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); + DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint); + + /* clear buffered interrupts */ + sc->sc_chan_state[x].hcint = 0; +} + +static uint8_t +dwc_otg_host_channel_wait(struct dwc_otg_td *td) +{ + struct dwc_otg_softc *sc; + 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].hcint & HCINT_HALTED_ONLY) { + dwc_otg_clear_hcint(sc, x); + return (1); + } + + if (x == 0) + return (0); /* wait */ + + /* find new disabled channel */ + for (x = 1; x != sc->sc_host_ch_max; x++) { + + uint32_t hcchar; + + if (sc->sc_chan_state[x].allocated) + continue; + + /* check if channel is enabled */ + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); + if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) { + DPRINTF("CH=%d is BUSY\n", x); + continue; + } + + sc->sc_chan_state[td->channel].allocated = 0; + sc->sc_chan_state[x].allocated = 1; + + 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(0x%08x) " + "HCSPLT=0x%08x\n", x, td->hcchar, + 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 temp; uint8_t x; uint8_t max_channel; @@ -510,47 +589,79 @@ dwc_otg_host_channel_alloc(struct dwc_ot } for (; x != max_channel; x++) { - if (sc->sc_chan_state[x].hcchar == 0) { - /* check if channel is enabled */ - temp = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); - if (temp & HCCHAR_CHENA) { - DPRINTF("CH=%d is BUSY\n", x); - continue; - } + uint32_t hcchar; - sc->sc_chan_state[x].hcchar = td->hcchar; - sc->sc_chan_state[x].hcsplt = td->hcsplt; + if (sc->sc_chan_state[x].allocated) + continue; + + /* check if channel is enabled */ + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); + if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) { + DPRINTF("CH=%d is BUSY\n", x); + continue; + } - DPRINTF("HCCHAR=0x%08x(0x%08x) HCSPLT=0x%08x\n", - td->hcchar, temp, td->hcsplt); + sc->sc_chan_state[x].allocated = 1; - /* clear leftover interrupts */ - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); + /* clear interrupts */ + dwc_otg_clear_hcint(sc, x); + + DPRINTF("CH=%d HCCHAR=0x%08x(0x%08x) " + "HCSPLT=0x%08x\n", x, td->hcchar, + hcchar, td->hcsplt); + + /* set active channel */ + sc->sc_active_rx_ep |= (1 << x); - /* clear buffered interrupts */ - sc->sc_chan_state[x].hcint = 0; + /* set channel */ + td->channel = x; - /* clear state */ - sc->sc_chan_state[x].state = 0; + return (0); /* allocated */ + } + return (1); /* busy */ +} + +static void +dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x) +{ + uint32_t hcchar; + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); + if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), HCCHAR_CHENA | HCCHAR_CHDIS); +} + +static void +dwc_otg_host_channel_free(struct dwc_otg_td *td) +{ + struct dwc_otg_softc *sc; + uint8_t x; + + if (td->channel >= DWC_OTG_MAX_CHANNELS) + return; /* already freed */ - /* we've requested SOF interrupt */ - sc->sc_chan_state[x].sof_requested = 1; + /* free channel */ + x = td->channel; + td->channel = DWC_OTG_MAX_CHANNELS; - /* set channel */ - td->channel = x; + DPRINTF("CH=%d\n", x); - /* set active EP */ - sc->sc_active_rx_ep |= (1 << x); + /* get pointer to softc */ + sc = DWC_OTG_PC2SC(td->pc); - /* request SOF's */ - dwc_otg_request_sof(sc); + dwc_otg_host_channel_disable(sc, x); - return (0); /* allocated */ - } + sc->sc_chan_state[x].allocated = 0; + sc->sc_chan_state[x].suspended = 0; + + /* ack any pending messages */ + if (sc->sc_last_rx_status != 0 && + GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) { + dwc_otg_common_rx_ack(sc); } - return (1); /* busy */ + + /* clear active channel */ + sc->sc_active_rx_ep &= ~(1 << x); } static uint8_t @@ -558,7 +669,8 @@ dwc_otg_host_setup_tx(struct dwc_otg_td { struct usb_device_request req __aligned(4); struct dwc_otg_softc *sc; - uint32_t temp; + uint32_t hcint; + uint32_t hcchar; if (dwc_otg_host_channel_alloc(td)) return (1); /* busy */ @@ -566,94 +678,93 @@ dwc_otg_host_setup_tx(struct dwc_otg_td /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - temp = sc->sc_chan_state[td->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, sc->sc_chan_state[td->channel].state, temp, + td->channel, td->state, hcint, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); - if (temp & HCINT_STALL) { + if (hcint & HCINT_STALL) { + DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; return (0); /* complete */ } - if (temp & (HCINT_BBLERR | HCINT_XACTERR)) { - td->error_any = 1; - return (0); /* complete */ - } - - if (temp & HCINT_NAK) { - if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) - return (1); /* busy */ - td->sof_val += 1; + if (hcint & HCINT_ERRORS) { + DPRINTF("CH=%d ERROR\n", td->channel); + td->errcnt++; + if (td->hcsplt != 0 || td->errcnt >= 3) { + td->error_any = 1; + return (0); /* complete */ + } } /* channel must be disabled before we can complete the transfer */ - if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) { - uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)); - if (hcchar & HCCHAR_CHENA) { - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - HCCHAR_CHENA | HCCHAR_CHDIS); - } else { - sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD; + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { + uint32_t hcchar; + + dwc_otg_host_channel_disable(sc, td->channel); + + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)); + + if (!(hcchar & HCCHAR_CHENA)) { + hcint |= HCINT_HALTED_ONLY; + sc->sc_chan_state[td->channel].hcint = hcint; } + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; } - switch (sc->sc_chan_state[td->channel].state) { + switch (td->state) { case DWC_CHAN_ST_START: - if (sc->sc_chan_state[td->channel].hcsplt != 0) { - sc->sc_chan_state[td->channel].hcsplt &= - ~HCSPLT_COMPSPLT; - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_S_ANE; - } else { - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_ANE; - } goto send_pkt; + case DWC_CHAN_ST_WAIT_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (!dwc_otg_host_channel_wait(td)) + break; + td->did_nak = 1; goto send_pkt; - if (sc->sc_chan_state[td->channel].hcint & - (HCINT_ACK | HCINT_NYET)) { + } + if (hcint & (HCINT_ACK | HCINT_NYET)) { + if (!dwc_otg_host_channel_wait(td)) + break; td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; - sc->sc_chan_state[td->channel].hcint = 0; - sc->sc_chan_state[td->channel].state = 0; return (0); /* complete */ } break; case DWC_CHAN_ST_WAIT_S_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (!dwc_otg_host_channel_wait(td)) + break; + td->did_nak = 1; goto send_pkt; - if (sc->sc_chan_state[td->channel].hcint & - (HCINT_ACK | HCINT_NYET)) { - sc->sc_chan_state[td->channel].hcsplt |= HCSPLT_COMPSPLT; - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_C_ANE; + } + if (hcint & (HCINT_ACK | HCINT_NYET)) { + if (!dwc_otg_host_channel_wait(td)) + break; goto send_cpkt; } break; case DWC_CHAN_ST_WAIT_C_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (!dwc_otg_host_channel_wait(td)) + break; + td->did_nak = 1; goto send_cpkt; - if (sc->sc_chan_state[td->channel].hcint & - (HCINT_ACK | HCINT_NYET)) { + } + if (hcint & (HCINT_ACK | HCINT_NYET)) { + if (!dwc_otg_host_channel_wait(td)) + break; td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; - sc->sc_chan_state[td->channel].hcint = 0; - sc->sc_chan_state[td->channel].state = 0; return (0); /* complete */ } break; @@ -668,6 +779,13 @@ send_pkt: return (0); /* complete */ } + if (td->hcsplt != 0) { + td->hcsplt &= ~HCSPLT_COMPSPLT; + td->state = DWC_CHAN_ST_WAIT_S_ANE; + } else { + td->state = DWC_CHAN_ST_WAIT_ANE; + } + usbd_copy_out(td->pc, 0, &req, sizeof(req)); DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), @@ -675,14 +793,13 @@ send_pkt: (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), - sc->sc_chan_state[td->channel].hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); - temp = sc->sc_chan_state[td->channel].hcchar; - temp &= ~HCCHAR_EPDIR_IN; + hcchar = td->hcchar; + hcchar &= ~HCCHAR_EPDIR_IN; /* must enable channel before writing data to FIFO */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp); + 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, @@ -691,26 +808,22 @@ send_pkt: /* store number of bytes transmitted */ td->tx_bytes = sizeof(req); - /* clear interrupts */ - sc->sc_chan_state[td->channel].hcint = 0; - return (1); /* busy */ send_cpkt: + td->hcsplt |= HCSPLT_COMPSPLT; + td->state = DWC_CHAN_ST_WAIT_C_ANE; + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), - sc->sc_chan_state[td->channel].hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); - temp = sc->sc_chan_state[td->channel].hcchar; - temp &= ~HCCHAR_EPDIR_IN; + hcchar = td->hcchar; + hcchar &= ~HCCHAR_EPDIR_IN; /* must enable channel before writing data to FIFO */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp); - - /* clear interrupts */ - sc->sc_chan_state[td->channel].hcint = 0; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); return (1); /* busy */ } @@ -847,58 +960,110 @@ not_complete: } static uint8_t -dwc_otg_host_data_rx(struct dwc_otg_td *td) +dwc_otg_host_rate_check(struct dwc_otg_td *td, + uint8_t do_inc) { struct dwc_otg_softc *sc; - uint32_t temp; - uint16_t count; uint8_t ep_type; + /* get pointer to softc */ + sc = DWC_OTG_PC2SC(td->pc); + + 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; + if (do_inc) + td->tmr_val += td->tmr_res; + } else if (ep_type == UE_INTERRUPT) { + if ((sc->sc_tmr_val & 0xFF) != td->tmr_val) + goto busy; + if (do_inc) + td->tmr_val += td->tmr_res; + } 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; + } + return (0); +busy: + return (1); +} + +static uint8_t +dwc_otg_host_data_rx(struct dwc_otg_td *td) +{ + struct dwc_otg_softc *sc; + uint32_t hcint; + uint32_t hcchar; + uint32_t count; + if (dwc_otg_host_channel_alloc(td)) return (1); /* busy */ /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - ep_type = ((sc->sc_chan_state[td->channel].hcchar & - HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT); - - temp = sc->sc_chan_state[td->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, sc->sc_chan_state[td->channel].state, temp, + td->channel, td->state, hcint, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); - if (temp & HCINT_STALL) { + /* check interrupt bits */ + + if (hcint & HCINT_STALL) { + DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; return (0); /* complete */ } - if (temp & (HCINT_BBLERR | HCINT_XACTERR)) { - td->error_any = 1; - return (0); /* complete */ + if (hcint & HCINT_ERRORS) { + DPRINTF("CH=%d ERROR\n", td->channel); + td->errcnt++; + if (td->hcsplt != 0 || td->errcnt >= 3) { + td->error_any = 1; + return (0); /* complete */ + } } /* channel must be disabled before we can complete the transfer */ - if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) { - uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)); - if (hcchar & HCCHAR_CHENA) { - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - HCCHAR_CHENA | HCCHAR_CHDIS); - } else { - sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD; + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { + + dwc_otg_host_channel_disable(sc, td->channel); + + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)); + + if (!(hcchar & HCCHAR_CHENA)) { + hcint |= HCINT_HALTED_ONLY; + sc->sc_chan_state[td->channel].hcint = hcint; } + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; } /* check endpoint status */ if (sc->sc_last_rx_status == 0) - goto not_complete; + goto check_state; if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->channel) - goto not_complete; + goto check_state; switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) { case GRXSTSRH_IN_DATA: @@ -941,7 +1106,8 @@ dwc_otg_host_data_rx(struct dwc_otg_td * td->remainder -= count; td->offset += count; - sc->sc_chan_state[td->channel].hcint |= HCINT_SOFTWARE_ONLY; + hcint |= HCINT_SOFTWARE_ONLY | HCINT_ACK; + sc->sc_chan_state[td->channel].hcint = hcint; break; default: @@ -951,105 +1117,82 @@ dwc_otg_host_data_rx(struct dwc_otg_td * /* release FIFO */ dwc_otg_common_rx_ack(sc); -not_complete: - if (temp & HCINT_SUSPEND_ONLY) - return (1); /* busy */ - - if (ep_type == UE_ISOCHRONOUS) { - if ((sc->sc_sof_val & 0xFF) != td->sof_val) - return (1); /* busy */ - if (td->sof_val & 1) - sc->sc_chan_state[td->channel].hcchar |= HCCHAR_ODDFRM; - else - sc->sc_chan_state[td->channel].hcchar &= ~HCCHAR_ODDFRM; - td->sof_val += td->sof_res; - } else if (ep_type == UE_INTERRUPT) { - if ((sc->sc_sof_val & 0xFF) != td->sof_val) - return (1); /* busy */ - td->sof_val += td->sof_res; - } else if (temp & HCINT_NAK) { - if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) - return (1); /* busy */ - td->sof_val += 1; - } - - switch (sc->sc_chan_state[td->channel].state) { +check_state: + switch (td->state) { case DWC_CHAN_ST_START: - if (sc->sc_chan_state[td->channel].hcsplt != 0) { - sc->sc_chan_state[td->channel].hcsplt &= - ~HCSPLT_COMPSPLT; - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_S_ANE; + if (td->hcsplt != 0) goto receive_spkt; - } else { - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_ANE; + else goto receive_pkt; - } + case DWC_CHAN_ST_WAIT_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) - goto receive_pkt; - if (!(sc->sc_chan_state[td->channel].hcint & - HCINT_SOFTWARE_ONLY)) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (!dwc_otg_host_channel_wait(td)) + break; + + if (td->hcsplt != 0) + goto receive_spkt; + else + goto receive_pkt; + } + if (!(hcint & HCINT_SOFTWARE_ONLY)) break; - if (sc->sc_chan_state[td->channel].hcint & - (HCINT_ACK | HCINT_NYET)) { + if (hcint & (HCINT_ACK | HCINT_NYET)) { + 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) { - /* we are complete */ - sc->sc_chan_state[ - td->channel].hcint = 0; - sc->sc_chan_state[ - td->channel].state = 0; - return (0); - } + if (td->short_pkt) + return (0); /* complete */ + /* * Else need to receive a zero length * packet. */ } - if (sc->sc_chan_state[td->channel].hcsplt != 0) { - sc->sc_chan_state[td->channel].hcsplt &= - ~HCSPLT_COMPSPLT; - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_S_ANE; + if (td->hcsplt != 0) goto receive_spkt; - } else { - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_ANE; + else goto receive_pkt; - } } break; + case DWC_CHAN_ST_WAIT_S_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (!dwc_otg_host_channel_wait(td)) + break; goto receive_spkt; - if (sc->sc_chan_state[td->channel].hcint & (HCINT_ACK | HCINT_NYET)) { - sc->sc_chan_state[td->channel].hcsplt |= - HCSPLT_COMPSPLT; - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_ANE; + } + if (hcint & (HCINT_ACK | HCINT_NYET)) { + if (!dwc_otg_host_channel_wait(td)) + break; goto receive_pkt; } break; + + case DWC_CHAN_ST_RX_PKT: + goto receive_pkt; + + case DWC_CHAN_ST_RX_SPKT: + goto receive_spkt; + default: break; } - return (1); /* busy */ + goto busy; receive_pkt: - if (ep_type == UE_ISOCHRONOUS) { - td->toggle = 0; - } else if (td->set_toggle) { - td->set_toggle = 0; - td->toggle = 1; + if (dwc_otg_host_rate_check(td, 1)) { + td->state = DWC_CHAN_ST_RX_PKT; + dwc_otg_host_channel_free(td); + goto busy; } + if (td->hcsplt != 0) + td->hcsplt |= HCSPLT_COMPSPLT; + td->state = DWC_CHAN_ST_WAIT_ANE; + /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) | @@ -1057,45 +1200,40 @@ receive_pkt: (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), - sc->sc_chan_state[td->channel].hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); - temp = sc->sc_chan_state[td->channel].hcchar; - temp |= HCCHAR_EPDIR_IN; + hcchar = td->hcchar; + hcchar |= HCCHAR_EPDIR_IN; /* must enable channel before data can be received */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp); + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); - /* clear interrupts */ - sc->sc_chan_state[td->channel].hcint = 0; - - return (1); /* busy */ + goto busy; receive_spkt: - if (ep_type == UE_ISOCHRONOUS) { - td->toggle = 0; - } else if (td->set_toggle) { - td->set_toggle = 0; - td->toggle = 1; + if (dwc_otg_host_rate_check(td, 0)) { + td->state = DWC_CHAN_ST_RX_SPKT; + dwc_otg_host_channel_free(td); + goto busy; } + td->hcsplt &= ~HCSPLT_COMPSPLT; + td->state = DWC_CHAN_ST_WAIT_S_ANE; + /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), - sc->sc_chan_state[td->channel].hcsplt); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt); - temp = sc->sc_chan_state[td->channel].hcchar; - temp |= HCCHAR_EPDIR_IN; + hcchar = td->hcchar; + hcchar |= HCCHAR_EPDIR_IN; /* must enable channel before data can be received */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp); - - /* clear interrupts */ - sc->sc_chan_state[td->channel].hcint = 0; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); +busy: return (1); /* busy */ } @@ -1217,7 +1355,8 @@ dwc_otg_host_data_tx(struct dwc_otg_td * { struct dwc_otg_softc *sc; uint32_t count; - uint32_t temp; + uint32_t hcint; + uint32_t hcchar; uint8_t ep_type; if (dwc_otg_host_channel_alloc(td)) @@ -1226,146 +1365,142 @@ dwc_otg_host_data_tx(struct dwc_otg_td * /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - ep_type = ((sc->sc_chan_state[td->channel].hcchar & + ep_type = ((td->hcchar & HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT); - temp = sc->sc_chan_state[td->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, sc->sc_chan_state[td->channel].state, temp, + td->channel, td->state, hcint, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); - if (temp & HCINT_STALL) { + if (hcint & HCINT_STALL) { + DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; return (0); /* complete */ } - if (temp & (HCINT_BBLERR | HCINT_XACTERR)) { - td->error_any = 1; - return (0); /* complete */ + if (hcint & HCINT_ERRORS) { + DPRINTF("CH=%d ERROR\n", td->channel); + td->errcnt++; + if (td->hcsplt != 0 || td->errcnt >= 3) { + td->error_any = 1; + return (0); /* complete */ + } } /* channel must be disabled before we can complete the transfer */ - if (temp & (HCINT_NAK | HCINT_ACK | HCINT_NYET)) { - uint32_t hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)); - if (hcchar & HCCHAR_CHENA) { - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - HCCHAR_CHENA | HCCHAR_CHDIS); - } else { - sc->sc_chan_state[td->channel].hcint |= HCINT_CHHLTD; - } - } + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { - if (ep_type == UE_ISOCHRONOUS) { - if ((sc->sc_sof_val & 0xFF) != td->sof_val) - return (1); /* busy */ - if (td->sof_val & 1) - sc->sc_chan_state[td->channel].hcchar |= HCCHAR_ODDFRM; - else - sc->sc_chan_state[td->channel].hcchar &= ~HCCHAR_ODDFRM; - td->sof_val += td->sof_res; - } else if (ep_type == UE_INTERRUPT) { - if ((sc->sc_sof_val & 0xFF) != td->sof_val) - return (1); /* busy */ - td->sof_val += td->sof_res; - } else if (temp & HCINT_NAK) { - if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) - return (1); /* busy */ - td->sof_val += 1; + dwc_otg_host_channel_disable(sc, td->channel); + + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)); + + if (!(hcchar & HCCHAR_CHENA)) { + hcint |= HCINT_HALTED_ONLY; + sc->sc_chan_state[td->channel].hcint = hcint; + } + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; } - switch (sc->sc_chan_state[td->channel].state) { + switch (td->state) { case DWC_CHAN_ST_START: - if (sc->sc_chan_state[td->channel].hcsplt != 0) { - sc->sc_chan_state[td->channel].hcsplt &= ~HCSPLT_COMPSPLT; - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_S_ANE; - } else { - sc->sc_chan_state[td->channel].state = - DWC_CHAN_ST_WAIT_ANE; - } goto send_pkt; + case DWC_CHAN_ST_WAIT_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + if (!dwc_otg_host_channel_wait(td)) + break; + td->did_nak = 1; goto send_pkt; - if (sc->sc_chan_state[td->channel].hcint & - (HCINT_ACK | HCINT_NYET)) { + } + if (hcint & (HCINT_ACK | HCINT_NYET)) { + if (!dwc_otg_host_channel_wait(td)) + break; + td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle ^= 1; /* check remainder */ if (td->remainder == 0) { - if (td->short_pkt) { - sc->sc_chan_state[td->channel].hcint = 0; - sc->sc_chan_state[td->channel].state = 0; + if (td->short_pkt) return (0); /* complete */ - } - /* else we need to transmit a short packet */ + /* + * Else we need to transmit a short + * packet: + */ } goto send_pkt; } break; case DWC_CHAN_ST_WAIT_S_ANE: - if (!(sc->sc_chan_state[td->channel].hcint & HCINT_CHHLTD)) - break; - if (sc->sc_chan_state[td->channel].hcint & HCINT_NAK) + if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***