From owner-svn-src-all@FreeBSD.ORG Fri Sep 14 07:52:58 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 70273106564A; Fri, 14 Sep 2012 07:52:58 +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 597138FC16; Fri, 14 Sep 2012 07:52:58 +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 q8E7qwNR073508; Fri, 14 Sep 2012 07:52:58 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q8E7qwKx073504; Fri, 14 Sep 2012 07:52:58 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201209140752.q8E7qwKx073504@svn.freebsd.org> From: Hans Petter Selasky Date: Fri, 14 Sep 2012 07:52:58 +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: r240482 - 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: Fri, 14 Sep 2012 07:52:58 -0000 Author: hselasky Date: Fri Sep 14 07:52:57 2012 New Revision: 240482 URL: http://svn.freebsd.org/changeset/base/240482 Log: DWC OTG improvements. Implement full support for SPLIT transactions, in other words FULL/LOW speed devices through HIGH speed HUBs. Improve support for suspend and resume in host mode. Modified: head/sys/dev/usb/controller/dwc_otg.c head/sys/dev/usb/controller/dwc_otg.h head/sys/dev/usb/controller/dwc_otgreg.h Modified: head/sys/dev/usb/controller/dwc_otg.c ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.c Fri Sep 14 05:24:06 2012 (r240481) +++ head/sys/dev/usb/controller/dwc_otg.c Fri Sep 14 07:52:57 2012 (r240482) @@ -510,7 +510,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot } for (; x != max_channel; x++) { - if (sc->sc_hcchar[x] == 0) { + if (sc->sc_chan_state[x].hcchar == 0) { /* check if channel is enabled */ temp = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); @@ -519,7 +519,8 @@ dwc_otg_host_channel_alloc(struct dwc_ot continue; } - sc->sc_hcchar[x] = td->hcchar; + sc->sc_chan_state[x].hcchar = td->hcchar; + sc->sc_chan_state[x].hcsplt = td->hcsplt; DPRINTF("HCCHAR=0x%08x(0x%08x) HCSPLT=0x%08x\n", td->hcchar, temp, td->hcsplt); @@ -528,14 +529,14 @@ dwc_otg_host_channel_alloc(struct dwc_ot temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); - /* enable interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), - HCINT_STALL | HCINT_BBLERR | - HCINT_AHBERR | HCINT_XACTERR | - HCINT_XFERCOMPL | HCINT_NAK); + /* clear buffered interrupts */ + sc->sc_chan_state[x].hcint = 0; + + /* clear state */ + sc->sc_chan_state[x].state = 0; - DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), td->hcsplt); - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0); + /* we've requested SOF interrupt */ + sc->sc_chan_state[x].sof_requested = 1; /* set channel */ td->channel = x; @@ -558,8 +559,6 @@ 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 max_buffer; - uint8_t max_frames; if (dwc_otg_host_channel_alloc(td)) return (1); /* busy */ @@ -567,57 +566,103 @@ dwc_otg_host_setup_tx(struct dwc_otg_td /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel)); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp); + temp = sc->sc_chan_state[td->channel].hcint; - DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", - td->channel, - temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), + 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, + DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); - if (temp & HCINT_NAK) - td->did_nak = 1; - if (temp & HCINT_STALL) { td->error_stall = 1; td->error_any = 1; return (0); /* complete */ } - if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) { + if (temp & (HCINT_BBLERR | HCINT_XACTERR)) { td->error_any = 1; return (0); /* complete */ } - if (temp & HCINT_XFERCOMPL) - td->did_complete = 1; + if (temp & HCINT_NAK) { + if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) + return (1); /* busy */ + td->sof_val += 1; + } + + /* 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 (td->did_complete) { - if (td->did_nak == 0) { + switch (sc->sc_chan_state[td->channel].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) + goto send_pkt; + if (sc->sc_chan_state[td->channel].hcint & + (HCINT_ACK | HCINT_NYET)) { 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 */ - } else { - if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) - return (1); /* busy */ - td->sof_val += 1; } - } else { - return (1); /* busy */ + 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) + 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; + 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) + goto send_cpkt; + if (sc->sc_chan_state[td->channel].hcint & + (HCINT_ACK | HCINT_NYET)) { + 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; + default: + break; } - temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS); - - DPRINTF("HPTXSTS=0x%08x\n", temp); - - max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK); - max_frames = (temp & HPTXSTS_PTXQSPCAVAIL_MASK) - >> HPTXSTS_PTXQSPCAVAIL_SHIFT; - - max_buffer = max_buffer - (max_buffer % td->max_packet_size); - if (max_buffer == 0 || max_frames == 0) - return (1); /* busy */ + return (1); /* busy */ +send_pkt: if (sizeof(req) != td->remainder) { td->error_any = 1; return (0); /* complete */ @@ -630,7 +675,10 @@ dwc_otg_host_setup_tx(struct dwc_otg_td (1 << HCTSIZ_PKTCNT_SHIFT) | (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT)); - temp = sc->sc_hcchar[td->channel]; + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), + sc->sc_chan_state[td->channel].hcsplt); + + temp = sc->sc_chan_state[td->channel].hcchar; temp &= ~HCCHAR_EPDIR_IN; /* must enable channel before writing data to FIFO */ @@ -640,11 +688,30 @@ dwc_otg_host_setup_tx(struct dwc_otg_td bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4); - /* reset statemachine */ - td->did_complete = 0; - td->did_nak = 0; + /* 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: + 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); + + temp = sc->sc_chan_state[td->channel].hcchar; + temp &= ~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; + return (1); /* busy */ } @@ -793,21 +860,15 @@ dwc_otg_host_data_rx(struct dwc_otg_td * /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel)); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp); - - DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", - td->channel, - temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), - DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + ep_type = ((sc->sc_chan_state[td->channel].hcchar & + HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT); - if (temp & HCINT_NAK) { - td->did_nak = 1; + temp = sc->sc_chan_state[td->channel].hcint; - /* disable channel - will generate a halted event */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - HCCHAR_CHENA | HCCHAR_CHDIS); - } + 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, + DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); if (temp & HCINT_STALL) { td->error_stall = 1; @@ -815,11 +876,23 @@ dwc_otg_host_data_rx(struct dwc_otg_td * return (0); /* complete */ } - if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) { + if (temp & (HCINT_BBLERR | HCINT_XACTERR)) { 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; + } + } + /* check endpoint status */ if (sc->sc_last_rx_status == 0) goto not_complete; @@ -828,16 +901,6 @@ dwc_otg_host_data_rx(struct dwc_otg_td * goto not_complete; switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) { - case GRXSTSRH_HALTED: - DPRINTF("HALTED\n"); - td->did_complete = 1; - break; - case GRXSTSRH_IN_COMPLETE: - DPRINTF("COMPLETE\n"); - /* disable channel - will generate a halted event */ - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - HCCHAR_CHENA | HCCHAR_CHDIS); - break; case GRXSTSRH_IN_DATA: DPRINTF("DATA\n"); @@ -878,6 +941,7 @@ 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; break; default: @@ -888,45 +952,102 @@ dwc_otg_host_data_rx(struct dwc_otg_td * dwc_otg_common_rx_ack(sc); not_complete: - if (td->did_complete == 0) + if (temp & HCINT_SUSPEND_ONLY) return (1); /* busy */ - /* check if we are complete */ - if ((td->remainder == 0) || (td->got_short != 0)) { - if (td->short_pkt) { - /* we are complete */ - return (0); - } - /* else need to receive a zero length packet */ - } - - temp = sc->sc_hcchar[td->channel]; - - ep_type = ((temp & HCCHAR_EPTYPE_MASK) >> - HCCHAR_EPTYPE_SHIFT); - if (ep_type == UE_ISOCHRONOUS) { if ((sc->sc_sof_val & 0xFF) != td->sof_val) return (1); /* busy */ if (td->sof_val & 1) - temp |= HCCHAR_ODDFRM; + 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; - /* DATA 0 */ - td->toggle = 0; } 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 (td->did_nak) { - if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) - return (1); /* busy */ - td->sof_val += 1; + } 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) { + 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; + goto receive_spkt; + } else { + sc->sc_chan_state[td->channel].state = + DWC_CHAN_ST_WAIT_ANE; + goto receive_pkt; } - if (td->set_toggle) { - td->set_toggle = 0; - td->toggle = 1; + 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)) + break; + if (sc->sc_chan_state[td->channel].hcint & + (HCINT_ACK | HCINT_NYET)) { + /* 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); + } + /* + * 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; + goto receive_spkt; + } else { + sc->sc_chan_state[td->channel].state = + DWC_CHAN_ST_WAIT_ANE; + 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) + 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; + goto receive_pkt; + } + break; + default: + break; + } + return (1); /* busy */ + +receive_pkt: + if (ep_type == UE_ISOCHRONOUS) { + td->toggle = 0; + } else if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; } /* receive one packet */ @@ -936,16 +1057,46 @@ not_complete: (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); + + temp = sc->sc_chan_state[td->channel].hcchar; temp |= HCCHAR_EPDIR_IN; /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp); - /* reset statemachine */ - td->did_complete = 0; - td->did_nak = 0; + /* clear interrupts */ + sc->sc_chan_state[td->channel].hcint = 0; - return (1); /* not complete */ + return (1); /* busy */ + +receive_spkt: + if (ep_type == UE_ISOCHRONOUS) { + td->toggle = 0; + } else if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + + /* 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); + + temp = sc->sc_chan_state[td->channel].hcchar; + temp |= 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; + + return (1); /* busy */ } static uint8_t @@ -1065,11 +1216,9 @@ static uint8_t dwc_otg_host_data_tx(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; - uint32_t max_buffer; uint32_t count; uint32_t temp; uint8_t ep_type; - uint8_t max_frames; if (dwc_otg_host_channel_alloc(td)) return (1); /* busy */ @@ -1077,18 +1226,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td * /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel)); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp); + ep_type = ((sc->sc_chan_state[td->channel].hcchar & + HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT); - DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", - td->channel, - temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), - DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + temp = sc->sc_chan_state[td->channel].hcint; - if (temp & HCINT_NAK) { - td->did_nak = 1; - td->did_complete = 1; - } + 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, + DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); if (temp & HCINT_STALL) { td->error_stall = 1; @@ -1096,70 +1242,132 @@ dwc_otg_host_data_tx(struct dwc_otg_td * return (0); /* complete */ } - if (temp & (HCINT_BBLERR | HCINT_AHBERR | HCINT_XACTERR)) { + if (temp & (HCINT_BBLERR | HCINT_XACTERR)) { td->error_any = 1; return (0); /* complete */ } - if (temp & HCINT_XFERCOMPL) - td->did_complete = 1; + /* 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 (td->did_complete) { - if (td->did_nak == 0) { + 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) { + 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) + goto send_pkt; + if (sc->sc_chan_state[td->channel].hcint & + (HCINT_ACK | HCINT_NYET)) { td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle ^= 1; - td->did_nak = 1; /* check remainder */ if (td->remainder == 0) { - if (td->short_pkt) + if (td->short_pkt) { + sc->sc_chan_state[td->channel].hcint = 0; + sc->sc_chan_state[td->channel].state = 0; return (0); /* complete */ + } /* else we need to transmit a short packet */ } - } else { - if ((sc->sc_sof_val & 1) != (td->sof_val & 1)) - return (1); /* busy */ - td->sof_val += 1; - } - } else { - return (1); /* busy */ - } - temp = sc->sc_hcchar[td->channel]; + 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) + 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; + 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) + goto send_cpkt; + if (sc->sc_chan_state[td->channel].hcint & + (HCINT_ACK | HCINT_NYET)) { + 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; + return (0); /* complete */ + } - ep_type = ((temp & HCCHAR_EPTYPE_MASK) >> - HCCHAR_EPTYPE_SHIFT); + /* else we need to transmit a short packet */ + } + sc->sc_chan_state[td->channel].hcsplt &= + ~HCSPLT_COMPSPLT; + sc->sc_chan_state[td->channel].state = + DWC_CHAN_ST_WAIT_S_ANE; + goto send_pkt; + } + break; + default: + break; + } + return (1); /* busy */ +send_pkt: if (ep_type == UE_ISOCHRONOUS) { - if ((sc->sc_sof_val & 0xFF) != td->sof_val) - return (1); /* busy */ - if (td->sof_val & 1) - sc->sc_hcchar[td->channel] |= HCCHAR_ODDFRM; - else - sc->sc_hcchar[td->channel] &= ~HCCHAR_ODDFRM; - td->sof_val += td->sof_res; td->toggle = 0; - - } 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 (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } - temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS); - - max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK); - max_frames = (temp & HPTXSTS_PTXQSPCAVAIL_MASK) - >> HPTXSTS_PTXQSPCAVAIL_SHIFT; - - max_buffer = max_buffer - (max_buffer % td->max_packet_size); - if (max_buffer == 0 || max_frames == 0) - return (1); /* busy */ - /* send one packet at a time */ count = td->max_packet_size; if (td->remainder < count) { @@ -1176,7 +1384,10 @@ dwc_otg_host_data_tx(struct dwc_otg_td * (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); - temp = sc->sc_hcchar[td->channel]; + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), + sc->sc_chan_state[td->channel].hcsplt); + + temp = sc->sc_chan_state[td->channel].hcchar; temp &= ~HCCHAR_EPDIR_IN; /* must enable before writing data to FIFO */ @@ -1197,11 +1408,31 @@ dwc_otg_host_data_tx(struct dwc_otg_td * sc->sc_tx_bounce_buffer, (count + 3) / 4); } - /* reset statemachine */ - td->did_complete = 0; - td->did_nak = 0; + /* store number of bytes transmitted */ td->tx_bytes = count; + /* clear interrupts */ + sc->sc_chan_state[td->channel].hcint = 0; + + return (1); /* busy */ + +send_cpkt: + 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); + + temp = sc->sc_chan_state[td->channel].hcchar; + temp &= ~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; + return (1); /* busy */ } @@ -1524,23 +1755,12 @@ repeat: /* non-data messages we simply skip */ if (temp != GRXSTSRD_STP_DATA && temp != GRXSTSRD_OUT_DATA) { - if (sc->sc_flags.status_device_mode) { - dwc_otg_common_rx_ack(sc); - goto repeat; - } else if (temp != GRXSTSRD_OUT_COMPLETE && - temp != GRXSTSRH_HALTED) { - dwc_otg_common_rx_ack(sc); - goto repeat; - } + dwc_otg_common_rx_ack(sc); + goto repeat; } - if (temp == GRXSTSRH_HALTED || - temp == GRXSTSRD_OUT_COMPLETE) { - temp = 0; - } else { - temp = GRXSTSRD_BCNT_GET( - sc->sc_last_rx_status); - } + temp = GRXSTSRD_BCNT_GET( + sc->sc_last_rx_status); ep_no = GRXSTSRD_CHNUM_GET( sc->sc_last_rx_status); @@ -1633,7 +1853,7 @@ void dwc_otg_interrupt(struct dwc_otg_softc *sc) { uint32_t status; - uint32_t haint; + uint8_t x; USB_BUS_LOCK(&sc->sc_bus); @@ -1641,24 +1861,20 @@ dwc_otg_interrupt(struct dwc_otg_softc * status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status); - haint = DWC_OTG_READ_4(sc, DOTG_HAINT); - DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n", - status, haint, DWC_OTG_READ_4(sc, DOTG_HFNUM)); + status, DWC_OTG_READ_4(sc, DOTG_HAINT), + DWC_OTG_READ_4(sc, DOTG_HFNUM)); - if (haint != 0) { - uint8_t x; + /* get all channel interrupts */ + for (x = 0; x != sc->sc_host_ch_max; x++) { + uint32_t temp; - /* clear left-over interrupts */ - for (x = 0; x != sc->sc_host_ch_max; x++) { - if (!(haint & (1 << x))) - continue; - /* check if channel is disabled */ - if (sc->sc_hcchar[x] == 0) { - uint32_t temp; - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); - } + temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); + if (temp != 0) { + DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); + sc->sc_chan_state[x].hcint |= + (temp & ~(HCINT_SOFTWARE_ONLY | + HCINT_SUSPEND_ONLY | HCINT_CHHLTD)); } } @@ -1856,8 +2072,6 @@ dwc_otg_setup_standard_chain_sub(struct td->error_any = 0; td->npkt = 0; td->did_stall = temp->did_stall; - td->did_nak = 1; - td->did_complete = 1; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; td->set_toggle = 0; @@ -2324,13 +2538,14 @@ dwc_otg_device_done(struct usb_xfer *xfe DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), HCCHAR_CHENA | HCCHAR_CHDIS); - sc->sc_hcchar[td->channel] = 0; + sc->sc_chan_state[td->channel].hcchar = 0; sc->sc_active_rx_ep &= ~(1 << td->channel); - td->channel = DWC_OTG_MAX_CHANNELS; - /* release SOF's */ - dwc_otg_release_sof(sc); + if (sc->sc_chan_state[td->channel].sof_requested != 0) + dwc_otg_release_sof(sc); + + td->channel = DWC_OTG_MAX_CHANNELS; } } /* dequeue transfer and start next transfer */ @@ -2671,8 +2886,12 @@ dwc_otg_init(struct dwc_otg_softc *sc) uint8_t x; for (x = 0; x != sc->sc_host_ch_max; x++) { - /* disable channel interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0); + /* 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); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), HCCHAR_CHENA | HCCHAR_CHDIS); } @@ -3613,18 +3832,21 @@ dwc_otg_device_resume(struct usb_device td = xfer->td_transfer_cache; if (td != NULL && td->channel < DWC_OTG_MAX_CHANNELS) { + sc->sc_chan_state[td->channel].hcint &= + ~HCINT_SUSPEND_ONLY; - sc->sc_hcchar[td->channel] = - (sc->sc_hcchar[td->channel] & ~HCCHAR_CHDIS) | - HCCHAR_CHENA; - - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - sc->sc_hcchar[td->channel]); + if (sc->sc_chan_state[td->channel].sof_requested == 0) { + sc->sc_chan_state[td->channel].sof_requested = 1; + dwc_otg_request_sof(sc); + } } } } USB_BUS_UNLOCK(udev->bus); + + /* poll all transfers again to restart resumed ones */ + dwc_otg_do_poll(udev->bus); } static void @@ -3648,12 +3870,15 @@ dwc_otg_device_suspend(struct usb_device if (td != NULL && td->channel < DWC_OTG_MAX_CHANNELS) { - sc->sc_hcchar[td->channel] = - (DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)) | - HCCHAR_CHDIS) & ~HCCHAR_CHENA; - + sc->sc_chan_state[td->channel].hcint |= + HCINT_NAK | HCINT_CHHLTD | HCINT_SUSPEND_ONLY; DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), - sc->sc_hcchar[td->channel]); + HCCHAR_CHENA | HCCHAR_CHDIS); + + if (sc->sc_chan_state[td->channel].sof_requested != 0) { + sc->sc_chan_state[td->channel].sof_requested = 0; + dwc_otg_release_sof(sc); + } } } } Modified: head/sys/dev/usb/controller/dwc_otg.h ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.h Fri Sep 14 05:24:06 2012 (r240481) +++ head/sys/dev/usb/controller/dwc_otg.h Fri Sep 14 07:52:57 2012 (r240482) @@ -68,8 +68,6 @@ struct dwc_otg_td { uint8_t did_stall:1; uint8_t toggle:1; uint8_t set_toggle:1; - uint8_t did_nak:1; - uint8_t did_complete:1; uint8_t got_short:1; }; @@ -128,6 +126,18 @@ struct dwc_otg_profile { uint16_t max_buffer; }; +struct dwc_otg_chan_state { + uint32_t hcchar; + uint32_t hcint; + uint32_t hcsplt; + 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 + uint8_t sof_requested; +}; + struct dwc_otg_softc { struct usb_bus sc_bus; union dwc_otg_hub_temp sc_hub_temp; @@ -149,7 +159,7 @@ struct dwc_otg_softc { uint32_t sc_last_rx_status; uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS]; uint32_t sc_in_ctl[DWC_OTG_MAX_ENDPOINTS]; - uint32_t sc_hcchar[DWC_OTG_MAX_CHANNELS]; + struct dwc_otg_chan_state sc_chan_state[DWC_OTG_MAX_CHANNELS]; uint32_t sc_sof_refs; uint32_t sc_sof_val; uint32_t sc_hprt_val; Modified: head/sys/dev/usb/controller/dwc_otgreg.h ============================================================================== --- head/sys/dev/usb/controller/dwc_otgreg.h Fri Sep 14 05:24:06 2012 (r240481) +++ head/sys/dev/usb/controller/dwc_otgreg.h Fri Sep 14 07:52:57 2012 (r240482) @@ -541,6 +541,8 @@ #define HCSPLT_PRTADDR_SHIFT 0 #define HCSPLT_PRTADDR_MASK 0x0000007f +#define HCINT_SUSPEND_ONLY (1<<21) /* BSD only */ +#define HCINT_SOFTWARE_ONLY (1<<20) /* BSD only */ #define HCINT_DATATGLERR (1<<10) #define HCINT_FRMOVRUN (1<<9) #define HCINT_BBLERR (1<<8)