Index: dwc_otg.c =================================================================== --- dwc_otg.c (revision 279589) +++ dwc_otg.c (working copy) @@ -1,5 +1,6 @@ /* $FreeBSD$ */ /*- + * Copyright (c) 2015 Daisuke Aoyama. All rights reserved. * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved. * @@ -85,6 +86,36 @@ #include #include +#ifdef __arm__ +#ifdef SMP +#define DWC_OTG_RMB() __asm __volatile("dmb ish" : : : "memory") +#define DWC_OTG_WMB() __asm __volatile("dmb ishst" : : : "memory") +#else /* !SMP */ +#define DWC_OTG_RMB() __asm __volatile("" : : : "memory") +#define DWC_OTG_WMB() __asm __volatile("" : : : "memory") +#endif /* SMP */ +#else /* !__arm__ */ +#define DWC_OTG_RMB() rmb() +#define DWC_OTG_WMB() wmb() +#endif /* __arm__ */ + +// XXX NOT USE +//#define NO_HCINT_CHHLTD +//#define NO_INT_TXFEMP + +static inline uint32_t +DWC_OTG_READ_4(struct dwc_otg_softc *sc, uint32_t reg) +{ + DWC_OTG_RMB(); + return bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg); +} +static inline void +DWC_OTG_WRITE_4(struct dwc_otg_softc *sc, uint32_t reg, uint32_t data) +{ + bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data); + DWC_OTG_WMB(); +} + #define DWC_OTG_BUS2SC(bus) \ ((struct dwc_otg_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct dwc_otg_softc *)0)->sc_bus)))) @@ -152,6 +183,7 @@ static void dwc_otg_root_intr(struct dwc_otg_softc *); static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *); static void dwc_otg_host_channel_disable(struct dwc_otg_softc *, uint8_t); +static uint32_t dwc_otg_intr_rxflvl(struct dwc_otg_softc *sc); /* * Here is a configuration that the chip supports. @@ -186,7 +218,7 @@ struct dwc_otg_profile *pf; uint32_t fifo_size; uint32_t fifo_regs; - uint32_t tx_start; + uint32_t tx_start, tx_size; uint8_t x; fifo_size = sc->sc_fifo_size; @@ -205,8 +237,16 @@ /* subtract FIFO regs from total once */ fifo_size -= fifo_regs; - /* split equally for IN and OUT */ - fifo_size /= 2; + if (mode == DWC_MODE_HOST) { + /* ptx/nptx is multiple of 512 */ + tx_size = (((fifo_size / 2) + 1024 - 1) / 1024) * 1024; + fifo_size -= tx_size; + } else { + /* split equally for IN and OUT */ + fifo_size /= 2; + tx_size = fifo_size; + } + DPRINTF("RX FIFO=%u\n", fifo_size); /* align to 4 bytes boundary */ fifo_size &= ~3; @@ -223,7 +263,7 @@ /* disable any leftover host channels */ for (x = 0; x != sc->sc_host_ch_max; x++) { - if (sc->sc_chan_state[x].wait_sof == 0) + if (sc->sc_chan_state[x].wait_halted == 0) continue; dwc_otg_host_channel_disable(sc, x); } @@ -234,7 +274,8 @@ sc->sc_active_rx_ep = 0; /* split equally for periodic and non-periodic */ - fifo_size /= 2; + fifo_size = tx_size / 2; + DPRINTF("PTX/NPTX FIFO=%u\n", fifo_size); /* align to 4 bytes boundary */ fifo_size &= ~3; @@ -246,7 +287,7 @@ tx_start += fifo_size; for (x = 0; x != sc->sc_host_ch_max; x++) { - /* disable all host interrupts */ + /* enable all host interrupts */ DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_DEFAULT_MASK); } @@ -258,10 +299,6 @@ /* reset host channel state */ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state)); - /* 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; @@ -351,10 +388,6 @@ /* reset host channel state */ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state)); - - /* reset FIFO TX levels */ - sc->sc_tx_cur_p_level = 0; - sc->sc_tx_cur_np_level = 0; } return (0); } @@ -468,6 +501,15 @@ } static void +dwc_otg_enable_txfemp_irq(struct dwc_otg_softc *sc) +{ + + DPRINTFN(5, "enable Periodic/non-Periodic TxFIFO Empty Mask\n"); + sc->sc_irq_mask |= GINTMSK_PTXFEMPMSK | GINTMSK_NPTXFEMPMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); +} + +static void dwc_otg_resume_irq(struct dwc_otg_softc *sc) { if (sc->sc_flags.status_suspend) { @@ -591,6 +633,9 @@ { uint32_t hcint; + if (x >= DWC_OTG_MAX_CHANNELS) + return; + /* clear all pending interrupts */ hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint); @@ -600,10 +645,9 @@ } static uint8_t -dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, 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 < DWC_OTG_MAX_CHANNELS) @@ -613,53 +657,59 @@ if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0) return (1); /* busy - cannot transfer data */ - /* compute needed TX FIFO size */ if (is_out != 0) { - 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) - 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 */ + /* + * check GINTSTS_PTXFEMP and GINTSTS_NPTXFEMP + * but should not block here since it is invoked by intr. + */ + uint32_t temp, timeout = 10; /* 10us (assume 1 transaction) */ + temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + if (td->ep_type == UE_INTERRUPT || + td->ep_type == UE_ISOCHRONOUS) { + while (!(temp & GINTSTS_PTXFEMP) && --timeout != 0) { + DELAY(1); + temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); } + if (!(temp & GINTSTS_PTXFEMP)) { + DPRINTF("Periodic TX FIFO is not empty\n"); + return (1); + } } else { - 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 */ + while (!(temp & GINTSTS_NPTXFEMP) && --timeout != 0) { + DELAY(1); + temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); } + if (!(temp & GINTSTS_NPTXFEMP)) { + DPRINTF("Non-Periodic TX FIFO is not empty\n"); + return (1); + } } - } else { - /* not a TX transaction */ - tx_p_size = 0; - tx_np_size = 0; } + DWC_OTG_RMB(); 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) + if (sc->sc_chan_state[x].wait_halted != 0) continue; + sc->sc_chan_state[x].td = td; sc->sc_chan_state[x].allocated = 1; - 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 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); + /* reset registers */ + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), ~0); + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_DEFAULT_MASK); + DPRINTF("CH=%d HCCHAR=0x%08x " - "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt); + "HCSPLT=0x%08x td=%p\n", x, td->hcchar, td->hcsplt, td); /* set active channel */ sc->sc_active_rx_ep |= (1 << x); @@ -667,9 +717,12 @@ /* set channel */ td->channel = x; + DWC_OTG_WMB(); return (0); /* allocated */ } - /* wait a bit */ + /* restart on next SOF */ + sc->sc_req_q_start = 1; + DWC_OTG_WMB(); dwc_otg_enable_sof_irq(sc); return (1); /* busy */ } @@ -677,6 +730,7 @@ static void dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td) { + uint32_t hcchar; uint8_t x; if (td->channel >= DWC_OTG_MAX_CHANNELS) @@ -686,23 +740,12 @@ x = td->channel; td->channel = DWC_OTG_MAX_CHANNELS; - DPRINTF("CH=%d\n", x); + DPRINTF("CH=%d td=%p\n", x, td); - /* - * We need to let programmed host channels run till complete - * else the host channel will stop functioning. Assume that - * after a fixed given amount of time the host channel is no - * longer doing any USB traffic: - */ - if (td->ep_type == UE_ISOCHRONOUS) { - /* double buffered */ - sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX; - } else { - /* single buffered */ - sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MIN; - } - + DWC_OTG_RMB(); + sc->sc_chan_state[x].td = NULL; sc->sc_chan_state[x].allocated = 0; + sc->sc_chan_state[x].wait_halted = 1; /* ack any pending messages */ if (sc->sc_last_rx_status != 0 && @@ -712,6 +755,30 @@ /* clear active channel */ sc->sc_active_rx_ep &= ~(1 << x); + DWC_OTG_WMB(); + + /* cleanup registers */ +#ifndef NO_HCINT_CHHLTD + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINTMSK_CHHLTDMSK); +#else + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0); +#endif + DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), ~0); + + DPRINTFN(5, "disable ch=%u, w=%d\n", x, sc->sc_chan_state[x].wait_halted); + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); + if ((hcchar & HCCHAR_CHENA) == 0) { + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), 0); + } + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), + hcchar | HCCHAR_CHENA | HCCHAR_CHDIS); + /* don't write state after this (chhltd will be asserted) */ + +#ifndef NO_INT_TXFEMP + /* restart queue */ + dwc_otg_enable_txfemp_irq(sc); +#endif } static void @@ -739,7 +806,8 @@ 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", + 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))); @@ -874,6 +942,7 @@ hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; /* must enable channel before writing data to FIFO */ + DPRINTFN(9, "stx:hcchar=0x%08x\n", hcchar); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); /* transfer data into FIFO */ @@ -925,6 +994,7 @@ hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT; /* must enable channel before writing data to FIFO */ + DPRINTFN(9, "stx:hcchar=0x%08x\n", hcchar); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); busy: @@ -1098,6 +1168,15 @@ goto busy; td->tt_scheduled = 0; } else if (td->did_nak >= DWC_OTG_NAK_MAX) { + /* + * don't block control packet here. + * no longer call frequently same as polling. + * otherwise, you get an error like: + * usbd_req_re_enumerate: addr=2, set address failed! + */ + if (td->ep_type == UE_CONTROL && + td->did_nak < (DWC_OTG_NAK_MAX * 3)) + return (0); goto busy; } else if (td->set_toggle) { td->set_toggle = 0; @@ -1216,10 +1295,250 @@ channel = td->channel; + /* handle normal bulk transfer (e.g. CSW, SCSI READ(16)) */ + if (td->ep_type == UE_BULK && td->hcsplt == 0) { + uint32_t count, pktcnt, hctsiz; +#ifdef DEBUG + uint32_t gintsts; +#endif + //DPRINTFN(9, "UE_BULK\n"); + if (channel >= DWC_OTG_MAX_CHANNELS) { + /* limitation for RPi */ + if (td->did_nak > (DWC_OTG_NAK_MAX * 3)) + goto busy; + + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(sc, td, 0)) { + DPRINTF("xfer: ch alloc error: nak=%u\n", + td->did_nak); + td->state = DWC_CHAN_ST_START; + goto busy; + } + /* + * don't release CH until xfer is completed or + * aborted + */ + channel = td->channel; + //DPRINTFN(5, "new ch=%d\n", channel); + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + + /* setup transfer count */ + count = td->remainder; + pktcnt = (td->remainder + td->max_packet_size - 1) / + td->max_packet_size; + /* need at least one packet */ + if (pktcnt == 0) + pktcnt = 1; + /* adjust to multiple of max_packet_size */ + count = pktcnt * td->max_packet_size; + if (count > 0x7ffff || pktcnt > 0x3ff) { + device_printf(sc->sc_bus.parent, + "can't handle packet\n"); + goto busy; + } + DPRINTFN(4, "pktcnt=%u, count=%u\n", pktcnt, count); + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (pktcnt << HCTSIZ_PKTCNT_SHIFT) | + (td->toggle ? (HCTSIZ_PID_DATA1 << + HCTSIZ_PID_SHIFT) : + (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); + + /* enable host channel */ + hcchar = td->hcchar; + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar |= HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); + + /* enable RX FIFO level interrupt */ + sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + + /* ready for loop */ + hcint = 0; + } else { + hcint = sc->sc_chan_state[channel].hcint; + +#ifdef DEBUG + gintsts = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + DPRINTFN(9, "rx:gintsts=0x%x, mask=0x%x, hcint=0x%x\n", + gintsts, sc->sc_irq_mask, hcint); +#endif + + DPRINTFN(10, "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))); + + 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->errcnt >= 3) { + td->error_any = 1; + goto complete; + } + } + + /* copy recieved data */ + if (dwc_otg_host_data_rx_sub(sc, td)) + goto complete; + + /* refresh interrupt status */ + hcint = sc->sc_chan_state[channel].hcint; + + if (td->got_short) { + /* no more data */ + DPRINTFN(9, "short packet\n"); + goto xfercompl; + } + + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; + } + + if (hcint & HCINT_NAK) { + sc->sc_chan_state[channel].hcint = 0; + + DPRINTF("NAK:%u, off=%u, hcint=0x%x, nak=%u\n", + td->remainder, td->offset, hcint, + td->did_nak); + td->did_nak++; + td->tt_scheduled = 0; + + if (td->did_nak > (DWC_OTG_NAK_MAX * 2)) { + /* restart transfer */ + dwc_otg_host_channel_free(sc, td); + goto busy; + } + + /* request next IN transfer */ + hcchar = DWC_OTG_READ_4(sc, + DOTG_HCCHAR(channel)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar |= HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), + hcchar); + goto busy; + } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + DPRINTF("ERR:%u, off=%u, hcint=0x%x\n", + td->remainder, td->offset, hcint); + td->did_nak++; + td->tt_scheduled = 0; + + /* restart transfer */ + dwc_otg_host_channel_free(sc, td); + goto busy; + } else if (hcint & HCINT_NYET) { + /* wrong response */ + td->error_any = 1; + goto complete; + } else if (hcint & HCINT_ACK) { + /* wait for data - ACK arrived first */ + if (!(hcint & HCINT_SOFTWARE_ONLY)) + goto busy; + + /* ACK is used for trigger of transfer */ + sc->sc_chan_state[channel].hcint = 0; + + /* get transfer counts */ + hctsiz = DWC_OTG_READ_4(sc, + DOTG_HCTSIZ(channel)); + pktcnt = (hctsiz & HCTSIZ_PKTCNT_MASK) >> + HCTSIZ_PKTCNT_SHIFT; + count= (hctsiz & HCTSIZ_XFERSIZE_MASK) >> + HCTSIZ_XFERSIZE_SHIFT; + DPRINTFN(4, "READ: pktcnt=%u, size=%u\n", + pktcnt, count); + + if (pktcnt != 0 && !td->got_short) { + /* request next IN transfer */ + hcchar = DWC_OTG_READ_4(sc, + DOTG_HCCHAR(channel)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar |= HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, + DOTG_HCCHAR(channel), hcchar); + } + td->tt_scheduled = 0; + td->did_nak = 0; + goto busy; + } else if (hcint & HCINT_XFERCOMPL) { + xfercompl: + sc->sc_chan_state[channel].hcint = 0; + + /* get transfer counts */ + hctsiz = DWC_OTG_READ_4(sc, + DOTG_HCTSIZ(channel)); + pktcnt = (hctsiz & HCTSIZ_PKTCNT_MASK) >> + HCTSIZ_PKTCNT_SHIFT; + count= (hctsiz & HCTSIZ_XFERSIZE_MASK) >> + HCTSIZ_XFERSIZE_SHIFT; + DPRINTFN(4, "COMP: pktcnt=%u, size=%u\n", + pktcnt, count); + + if (pktcnt != 0) { + /* expect short packet */ + if (!td->got_short) { + device_printf( + sc->sc_bus.parent, + "rx transfer error: " + "pktcnt=%u, rem=%u, " + "short=%u\n", + pktcnt, td->remainder, + td->got_short); + td->error_any = 1; + goto complete; + } + } + if (td->remainder != 0 && !td->got_short) { + /* upper layer should retry */ + DPRINTF("rem=%d\n", td->remainder); + td->error_any = 1; + goto complete; + } + goto complete; + } else { +#ifdef DEBUG + gintsts = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + DPRINTF("xx:gintsts=0x%x, mask=0x%x\n", + gintsts, sc->sc_irq_mask); +#endif + goto busy; + } + } + + td->state = DWC_CHAN_ST_WAIT_ANE; + goto busy; + } + if (channel < DWC_OTG_MAX_CHANNELS) { hcint = sc->sc_chan_state[channel].hcint; +#ifdef DEBUG + uint32_t gintsts; + gintsts = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + DPRINTFN(9, "rx:gintsts=0x%x, mask=0x%x, hcint=0x%x\n", + gintsts, sc->sc_irq_mask, hcint); +#endif - DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", + DPRINTFN(10, "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))); @@ -1260,6 +1579,7 @@ hcint = 0; } + DPRINTFN(10, "rx:st=%u ept=%u\n", td->state, td->ep_type); switch (td->state) { case DWC_CHAN_ST_START: if (td->hcsplt != 0) @@ -1414,13 +1734,16 @@ hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; - /* receive complete split ASAP */ - if ((sc->sc_last_frame_num & 1) != 0) - hcchar |= HCCHAR_ODDFRM; - else - hcchar &= ~HCCHAR_ODDFRM; + if (td->ep_type != UE_CONTROL && td->ep_type != UE_BULK) { + /* 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 */ + DPRINTFN(9, "rx:hcchar=0x%08x\n", hcchar); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); /* wait until next slot before trying complete split */ @@ -1462,11 +1785,13 @@ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); - /* send after next SOF event */ - if ((sc->sc_last_frame_num & 1) == 0) - td->hcchar |= HCCHAR_ODDFRM; - else - td->hcchar &= ~HCCHAR_ODDFRM; + if (td->ep_type != UE_CONTROL && td->ep_type != UE_BULK) { + /* send after next SOF event */ + if ((sc->sc_last_frame_num & 1) == 0) + td->hcchar |= HCCHAR_ODDFRM; + else + td->hcchar &= ~HCCHAR_ODDFRM; + } hcchar = td->hcchar; hcchar |= HCCHAR_EPDIR_IN; @@ -1475,6 +1800,7 @@ td->tt_complete_slot = sc->sc_last_frame_num + 1; /* must enable channel before data can be received */ + DPRINTFN(9, "rx:hcchar=0x%08x\n", hcchar); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); busy: return (1); /* busy */ @@ -1606,10 +1932,454 @@ channel = td->channel; + /* handle normal bulk transfer (e.g. CBW, SCSI WRITE(16)) */ + if (td->ep_type == UE_BULK && td->hcsplt == 0) { + uint32_t pktcnt, pktsz, hctsiz, pid; +#ifdef DEBUG + uint32_t gintsts; +#endif + //DPRINTFN(9, "UE_BULK\n"); + if (td->req_ping && channel >= DWC_OTG_MAX_CHANNELS) { + do_ping: + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(sc, td, 1)) { + DPRINTF("ping: ch alloc error: nak=%u\n", + td->did_nak); + td->state = DWC_CHAN_ST_START; + goto busy; + } + channel = td->channel; + //DPRINTFN(5, "new ch=%d\n", channel); + + /* enable ping */ + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), + HCTSIZ_DOPNG | + (1 << HCTSIZ_PKTCNT_SHIFT)); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), 0); + + /* enable host channel */ + hcchar = td->hcchar; + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); + goto busy; + } else if (td->req_ping) { + hcint = sc->sc_chan_state[channel].hcint; + DPRINTFN(9, "ping: ch=%u, hcint=0x%08x\n", + td->channel, hcint); + if (hcint & HCINT_ACK) { + sc->sc_chan_state[channel].hcint = 0; + DPRINTFN(4, "stop ping nak=%u\n", td->did_nak); + td->req_ping = 0; + td->did_nak = 0; + td->tt_scheduled = 0; + + /* close ping channel */ + dwc_otg_host_channel_free(sc, td); + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(sc, td, 1)) { + DPRINTF("ping: ch alloc error: " + "nak=%u\n", td->did_nak); + td->state = DWC_CHAN_ST_START; + goto busy; + } + channel = td->channel; + //DPRINTFN(5, "new ch=%d\n", channel); + + /* setup transfer count */ + count = td->remainder; + pktcnt = (td->remainder + + td->max_packet_size - 1) / + td->max_packet_size; + if (count > 0x7ffff || pktcnt > 0x3ff) { + device_printf(sc->sc_bus.parent, + "can't handle packet\n"); + goto busy; + } + DPRINTFN(4, "pktcnt=%u, count=%u\n", + pktcnt, count); + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (pktcnt << HCTSIZ_PKTCNT_SHIFT) | + (td->toggle ? (HCTSIZ_PID_DATA1 << + HCTSIZ_PID_SHIFT) : + (HCTSIZ_PID_DATA0 << + HCTSIZ_PID_SHIFT))); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), + td->hcsplt); + + /* enable host channel */ + hcchar = td->hcchar; + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), + hcchar); + + /* continue transfer */ + sc->sc_np_txq_cnt++; + DPRINTFN(4, "txq=%u\n", sc->sc_np_txq_cnt); + goto bulk_transfer; + } else if (hcint & HCINT_NAK) { + sc->sc_chan_state[channel].hcint = 0; + /* retry ping */ + DPRINTFN(4, "retry ping nak=%u\n", + td->did_nak); + td->did_nak++; + td->tt_scheduled = 0; + + /* enable host channel */ + hcchar = DWC_OTG_READ_4(sc, + DOTG_HCCHAR(channel)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), + hcchar); + goto busy; + } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + DPRINTF("retry|err hcint=0x%08x\n", hcint); + /* restart ping */ + dwc_otg_host_channel_free(sc, td); + goto busy; + } +#ifdef DEBUG + gintsts = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + DPRINTF("gintsts=0x%x, mask=0x%x\n", + gintsts, sc->sc_irq_mask); +#endif + goto busy; + } else if (channel >= DWC_OTG_MAX_CHANNELS) { + /* allocate a new channel */ + if (dwc_otg_host_channel_alloc(sc, td, 1)) { + DPRINTF("xfer: ch alloc error: nak=%u\n", + td->did_nak); + td->state = DWC_CHAN_ST_START; + goto busy; + } + /* + * don't release CH until xfer is completed or + * aborted + */ + channel = td->channel; + //DPRINTFN(5, "new ch=%d\n", channel); + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + + /* setup transfer count */ + count = td->remainder; + pktcnt = (td->remainder + td->max_packet_size - 1) / + td->max_packet_size; + if (count > 0x7ffff || pktcnt > 0x3ff) { + device_printf(sc->sc_bus.parent, + "can't handle packet\n"); + goto busy; + } + DPRINTFN(4, "pktcnt=%u, count=%u\n", pktcnt, count); + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (pktcnt << HCTSIZ_PKTCNT_SHIFT) | + (td->toggle ? (HCTSIZ_PID_DATA1 << + HCTSIZ_PID_SHIFT) : + (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt); + + /* enable host channel */ + hcchar = td->hcchar; + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); + + /* ready for loop */ + td->req_ping = 0; + td->tx_bytes = 0; + sc->sc_np_txq_cnt++; + DPRINTFN(4, "txq=%u\n", sc->sc_np_txq_cnt); + hcint = 0; + } else { + hcint = sc->sc_chan_state[channel].hcint; +#ifdef DEBUG + gintsts = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + DPRINTFN(9, "tx:gintsts=0x%x, mask=0x%x, hcint=0x%x\n", + gintsts, sc->sc_irq_mask, hcint); +#endif + + DPRINTFN(10, "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))); + + if (hcint & HCINT_STALL) { + DPRINTF("CH=%d STALL\n", channel); + td->error_stall = 1; + td->error_any = 1; + sc->sc_np_txq_cnt--; + DPRINTFN(4, "txq=%u\n", sc->sc_np_txq_cnt); + goto complete; + } else if (hcint & HCINT_ERRORS) { + DPRINTF("CH=%d ERROR\n", channel); + td->errcnt++; + if (td->errcnt >= 3) { + td->error_any = 1; + sc->sc_np_txq_cnt--; + DPRINTFN(4, "txq=%u\n", + sc->sc_np_txq_cnt); + goto complete; + } + } + + if (hcint & (HCINT_ERRORS | HCINT_RETRY | + HCINT_ACK | HCINT_NYET)) { + if (!(hcint & HCINT_ERRORS)) + td->errcnt = 0; + } + + if (hcint & HCINT_NAK) { + sc->sc_chan_state[channel].hcint = 0; + + DPRINTF("NAK:%u, off=%u, hcint=0x%x, ch=%u\n", + td->remainder, td->offset, hcint, channel); + td->did_nak++; + td->tt_scheduled = 0; + + if (td->did_nak > (DWC_OTG_NAK_MAX * 2)) { + /* restart transfer */ + dwc_otg_host_channel_free(sc, td); + goto busy; + } + + /* disable host channel */ + hcchar = DWC_OTG_READ_4(sc, + DOTG_HCCHAR(channel)); + hcchar &= ~HCCHAR_CHENA; + DWC_OTG_WRITE_4(sc, + DOTG_HCCHAR(channel), hcchar); + + /* setup transfer count */ + count = td->remainder; + pktcnt = (td->remainder + + td->max_packet_size - 1) / + td->max_packet_size; + if (count > 0x7ffff || pktcnt > 0x3ff) { + device_printf(sc->sc_bus.parent, + "can't handle packet\n"); + goto busy; + } + DPRINTFN(4, "pktcnt=%u, count=%u\n", + pktcnt, count); + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel), + (count << HCTSIZ_XFERSIZE_SHIFT) | + (pktcnt << HCTSIZ_PKTCNT_SHIFT) | + (td->toggle ? (HCTSIZ_PID_DATA1 << + HCTSIZ_PID_SHIFT) : + (HCTSIZ_PID_DATA0 << + HCTSIZ_PID_SHIFT))); + DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), + td->hcsplt); + + /* enable host channel */ + hcchar = DWC_OTG_READ_4(sc, + DOTG_HCCHAR(channel)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), + hcchar); + goto bulk_transfer; + } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { + DPRINTF("ERR:%u, off=%u, hcint=0x%x\n", + td->remainder, td->offset, hcint); + td->did_nak++; + td->tt_scheduled = 0; + + sc->sc_np_txq_cnt--; + DPRINTFN(4, "txq=%u\n", sc->sc_np_txq_cnt); + + /* restart transfer */ + dwc_otg_host_channel_free(sc, td); + goto busy; + } else if (hcint & (HCINT_ACK | HCINT_NYET)) { + sc->sc_chan_state[channel].hcint = 0; + + /* get transfer counts */ + hctsiz = DWC_OTG_READ_4(sc, + DOTG_HCTSIZ(channel)); + pktcnt = (hctsiz & HCTSIZ_PKTCNT_MASK) >> + HCTSIZ_PKTCNT_SHIFT; + count= (hctsiz & HCTSIZ_XFERSIZE_MASK) >> + HCTSIZ_XFERSIZE_SHIFT; + DPRINTFN(4, "WRITE: pktcnt=%u, size=%u\n", + pktcnt, count); + + td->did_nak = 0; + td->tt_scheduled = 0; + + /* update info */ + pktsz = min(td->remainder, + td->max_packet_size); + td->offset += pktsz; + td->remainder -= pktsz; + td->toggle ^= 1; + + /* skip last NYET (handled in next requet) */ + if (td->remainder != 0 && + (hcint & HCINT_NYET)) { + DPRINTF("nyet:%u, off=%u, " + "hcint=0x%x, td=%p, ch=%u\n", + td->remainder, td->offset, + hcint, td, channel); + + sc->sc_np_txq_cnt--; + DPRINTFN(4, "txq=%u\n", + sc->sc_np_txq_cnt); + + /* save current PID */ + hctsiz = DWC_OTG_READ_4(sc, + DOTG_HCTSIZ(channel)); + pid = (hctsiz & HCTSIZ_PID_MASK) >> + HCTSIZ_PID_SHIFT; + td->toggle = + (pid == HCTSIZ_PID_DATA0) ? 0 : 1; + + /* verify ACK by using PING */ + td->req_ping = 1; + + /* close channel and restart ping */ + dwc_otg_host_channel_free(sc, td); + goto do_ping; + } + + /* usually complete with ACK */ + if (hcint & HCINT_XFERCOMPL) { + DPRINTFN(9, "HCINT_XFERCOMPL\n"); + goto xfercompl; + } + + /* continue transfer */ + if (pktcnt != 0) { + /* enable host channel */ + hcchar = DWC_OTG_READ_4(sc, + DOTG_HCCHAR(channel)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR_IN; + DWC_OTG_WRITE_4(sc, + DOTG_HCCHAR(channel), hcchar); + goto bulk_transfer; + } + goto busy; + } else if (hcint & HCINT_XFERCOMPL) { + xfercompl: + sc->sc_chan_state[channel].hcint = 0; + + /* get transfer counts */ + hctsiz = DWC_OTG_READ_4(sc, + DOTG_HCTSIZ(channel)); + pktcnt = (hctsiz & HCTSIZ_PKTCNT_MASK) >> + HCTSIZ_PKTCNT_SHIFT; + count= (hctsiz & HCTSIZ_XFERSIZE_MASK) >> + HCTSIZ_XFERSIZE_SHIFT; + DPRINTFN(4, "COMP: pktcnt=%u, size=%u\n", + pktcnt, count); + + sc->sc_np_txq_cnt--; + DPRINTFN(4, "txq=%u\n", sc->sc_np_txq_cnt); + + if (pktcnt != 0) { + device_printf(sc->sc_bus.parent, + "tx transfer error: pktcnt=%u\n", + pktcnt); + td->error_any = 1; + goto complete; + } + goto complete; + } else { +#ifdef DEBUG + gintsts = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + DPRINTF("xx:gintsts=0x%x, mask=0x%x\n", + gintsts, sc->sc_irq_mask); +#endif + goto busy; + } + } + + bulk_transfer: + if (td->req_ping) { + device_printf(sc->sc_bus.parent, + "wrong request!!\n"); + goto busy; + } +#ifdef DEBUG + DPRINTFN(5, "rem=%u\n", td->remainder); +#endif + /* fill TX FIFO */ + if (td->remainder != 0) { + uint32_t gnptxsts, nptxqspcavail, nptxfspcavail; + + /* copy one packet */ + pktsz = min(td->remainder, td->max_packet_size); + + /* check FIFO space */ + gnptxsts = DWC_OTG_READ_4(sc, DOTG_GNPTXSTS); + nptxqspcavail = (gnptxsts & + GNPTXSTS_NPTXQSPCAVAIL_MASK) >> + GNPTXSTS_NPTXQSPCAVAIL_SHIFT; + nptxfspcavail = (gnptxsts & + GNPTXSTS_NPTXFSPCAVAIL_MASK) >> + GNPTXSTS_NPTXFSPCAVAIL_SHIFT; + DPRINTFN(4, "nptxqspcavail=%u, nptxfspcavail=%u\n", + nptxqspcavail, nptxfspcavail); + if (nptxqspcavail < 1 || + ((nptxfspcavail+3)/4) < ((pktsz+3)/4)) { + DPRINTF("NPTX FIFO is not empty\n"); + /* restart transfer */ + dwc_otg_host_channel_free(sc, td); + sc->sc_np_txq_cnt--; + goto busy; + } + + /* clear topmost word before copy */ + sc->sc_tx_bounce_buffer[(pktsz - 1) / 4] = 0; +#ifdef DEBUG + if (td->offset > 512) + DPRINTFN(5, "off=%u, sz=%u\n", + td->offset, pktsz); +#endif + /* copy out data */ + usbd_copy_out(td->pc, td->offset, + sc->sc_tx_bounce_buffer, pktsz); + + /* transfer data into FIFO */ + bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, + DOTG_DFIFO(channel), + sc->sc_tx_bounce_buffer, (pktsz + 3) / 4); + + /* update info */ + td->tx_bytes += pktsz; + } +#ifdef DEBUG + if (hcint != 0) { + DPRINTFN(5, "off=%u, rem=%u, hcint=0x%x\n", + td->offset, td->remainder, hcint); + } +#endif + + td->state = DWC_CHAN_ST_WAIT_ANE; + goto busy; + } + 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", + DPRINTFN(10, "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))); @@ -1641,6 +2411,7 @@ hcint = 0; } + DPRINTFN(10, "tx:st=%u ept=%u\n", td->state, td->ep_type); switch (td->state) { case DWC_CHAN_ST_START: goto send_pkt; @@ -1885,13 +2656,16 @@ hcchar = td->hcchar; hcchar &= ~HCCHAR_EPDIR_IN; - /* send after next SOF event */ - if ((sc->sc_last_frame_num & 1) == 0) - hcchar |= HCCHAR_ODDFRM; - else - hcchar &= ~HCCHAR_ODDFRM; + if (td->ep_type != UE_CONTROL && td->ep_type != UE_BULK) { + /* send after next SOF event */ + if ((sc->sc_last_frame_num & 1) == 0) + hcchar |= HCCHAR_ODDFRM; + else + hcchar &= ~HCCHAR_ODDFRM; + } /* must enable before writing data to FIFO */ + DPRINTFN(9, "tx:hcchar=0x%08x\n", hcchar); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); if (count != 0) { @@ -1949,13 +2723,16 @@ hcchar = td->hcchar; hcchar &= ~HCCHAR_EPDIR_IN; - /* receive complete split ASAP */ - if ((sc->sc_last_frame_num & 1) != 0) - hcchar |= HCCHAR_ODDFRM; - else - hcchar &= ~HCCHAR_ODDFRM; + if (td->ep_type != UE_CONTROL && td->ep_type != UE_BULK) { + /* 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 */ + DPRINTFN(9, "tx:hcchar=0x%08x\n", hcchar); DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); /* wait until next slot before trying complete split */ @@ -2205,7 +2982,7 @@ uint8_t tmr_val; uint8_t tmr_res; - DPRINTFN(9, "\n"); + //DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; if (td == NULL) @@ -2255,9 +3032,10 @@ { struct dwc_otg_td *td; - DPRINTFN(9, "\n"); + //DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; + //DPRINTFN(9, "xfer=%p, td=%p\n", xfer, td); if (td == NULL) { /* compute all actual lengths */ dwc_otg_standard_done(xfer); @@ -2275,7 +3053,7 @@ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); - DPRINTF("\n"); + //DPRINTF("\n"); USB_BUS_SPIN_LOCK(&sc->sc_bus); @@ -2291,6 +3069,7 @@ } /* enable SOF interrupt, which will poll jobs */ + sc->sc_req_q_start = 1; dwc_otg_enable_sof_irq(sc); USB_BUS_SPIN_UNLOCK(&sc->sc_bus); @@ -2334,24 +3113,22 @@ { uint32_t hcchar; + if (x >= DWC_OTG_MAX_CHANNELS) + return; + hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); /* disable host channel, if any */ if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) { + DPRINTFN(5, "wait for halted ch=%u\n", x); + sc->sc_chan_state[x].wait_halted = 1; /* disable channel */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), - HCCHAR_CHENA | HCCHAR_CHDIS); - /* wait for chip to get its brains in order */ - sc->sc_chan_state[x].wait_sof = 2; + hcchar | HCCHAR_CHENA | HCCHAR_CHDIS); + } else { + DPRINTFN(5, "disable ch=%u\n", x); + sc->sc_chan_state[x].wait_halted = 0; } - - /* release TX FIFO usage, 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; - - /* don't release TX FIFO usage twice */ - sc->sc_chan_state[x].tx_p_size = 0; - sc->sc_chan_state[x].tx_np_size = 0; } static uint16_t @@ -2371,7 +3148,6 @@ 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; @@ -2382,15 +3158,6 @@ TAILQ_INIT(&head); - for (x = 0; x != sc->sc_host_ch_max; x++) { - if (sc->sc_chan_state[x].wait_sof == 0) - continue; - - sc->sc_needsof = 1; - if (--(sc->sc_chan_state[x].wait_sof) == 0) - dwc_otg_host_channel_disable(sc, x); - } - if ((temp & 7) == 0) { /* reset the schedule */ @@ -2571,110 +3338,116 @@ return (1); } -static void -dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc) +#define INVALID_CHNUM 0xffffffffU +static uint32_t +dwc_otg_intr_rxflvl(struct dwc_otg_softc *sc) { - struct usb_xfer *xfer; - uint32_t temp; - uint8_t got_rx_status; - uint8_t x; + uint32_t chnum, bcnt, pktsts; -repeat: - /* get all channel interrupts */ - for (x = 0; x != sc->sc_host_ch_max; x++) { - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); - if (temp != 0) { - DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); - temp &= ~HCINT_SOFTWARE_ONLY; - sc->sc_chan_state[x].hcint |= temp; + DWC_OTG_RMB(); + if (sc->sc_last_rx_status != 0) { + chnum = GRXSTSRH_CHNUM_GET(sc->sc_last_rx_status); + if (!sc->sc_chan_state[chnum].allocated) { + DPRINTFN(5, "Closed host channel %u\n", chnum); + /* closed host channel */ + dwc_otg_common_rx_ack(sc); + } else { + device_printf(sc->sc_bus.parent, + "%s: duplicate RX drop old data ch=%u\n", + __func__, chnum); } + sc->sc_last_rx_status = 0; } + sc->sc_last_rx_status = DWC_OTG_READ_4(sc, DOTG_GRXSTSPH); + DWC_OTG_WMB(); if (sc->sc_last_rx_status == 0) { + device_printf(sc->sc_bus.parent, + "%s: wrong request\n", __func__); + return (INVALID_CHNUM); + } - temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS); - if (temp & GINTSTS_RXFLVL) { - /* pop current status */ - sc->sc_last_rx_status = - DWC_OTG_READ_4(sc, DOTG_GRXSTSPD); + chnum = GRXSTSRH_CHNUM_GET(sc->sc_last_rx_status); + bcnt = GRXSTSRH_BCNT_GET(sc->sc_last_rx_status); + pktsts = sc->sc_last_rx_status & GRXSTSRH_PKTSTS_MASK; + if (pktsts == GRXSTSRH_HALTED) { + DPRINTFN(5, "pktsts=halted, ch=%u\n", chnum); +#ifdef NO_HCINT_CHHLTD + if (!sc->sc_chan_state[chnum].allocated && + sc->sc_chan_state[chnum].wait_halted) { + /* closed host channel */ + DPRINTFN(5, "halted ch=%u,w=%u->0\n", + chnum, sc->sc_chan_state[chnum].wait_halted); + sc->sc_chan_state[chnum].wait_halted = 0; + DWC_OTG_WMB(); } +#endif + dwc_otg_common_rx_ack(sc); + return (INVALID_CHNUM); + } + if (pktsts != GRXSTSRH_IN_DATA && + pktsts != GRXSTSRD_OUT_DATA && + pktsts != GRXSTSRD_STP_DATA) { + DPRINTFN(4, "pktsts=%u, ch=%u, bcnt=%u\n", + (pktsts & GRXSTSRH_PKTSTS_MASK) >> + GRXSTSRH_PKTSTS_SHIFT, chnum, bcnt); + dwc_otg_common_rx_ack(sc); + return (INVALID_CHNUM); + } + if (bcnt != 0) { + DPRINTF("Reading %u bytes from ch %u\n", bcnt, chnum); + bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, + DOTG_DFIFO(chnum), + sc->sc_rx_bounce_buffer, (bcnt + 3) / 4); + DWC_OTG_WMB(); + } + if (!sc->sc_chan_state[chnum].allocated) { + /* closed host channel */ + DPRINTFN(5, "Closed host channel %u\n", chnum); + dwc_otg_common_rx_ack(sc); + return (INVALID_CHNUM); + } + DPRINTFN(5, "RX status = 0x%08x: ch=%u pid=%u bytes=%u sts=%u\n", + sc->sc_last_rx_status, chnum, + (sc->sc_last_rx_status >> GRXSTSRH_DPID_SHIFT) & 3, + bcnt, (pktsts >> GRXSTSRH_PKTSTS_SHIFT) & 15); + return (chnum); +} - if (sc->sc_last_rx_status != 0) { +static void +dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc) +{ + struct usb_xfer *xfer; +#if 0 + uint32_t temp; + uint8_t x; - uint8_t ep_no; - - temp = sc->sc_last_rx_status & - GRXSTSRD_PKTSTS_MASK; - - /* non-data messages we simply skip */ - if (temp != GRXSTSRD_STP_DATA && - temp != GRXSTSRD_OUT_DATA) { - dwc_otg_common_rx_ack(sc); - goto repeat; - } - - temp = GRXSTSRD_BCNT_GET( - sc->sc_last_rx_status); - ep_no = GRXSTSRD_CHNUM_GET( - sc->sc_last_rx_status); - - /* receive data, if any */ + /* get allocated channel interrupts */ + for (x = 0; x != sc->sc_host_ch_max; x++) { + /* don't remove HCINT_CHHLTD here */ + if (sc->sc_chan_state[x].allocated) { + temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x)); if (temp != 0) { - DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no); - bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl, - DOTG_DFIFO(ep_no), - sc->sc_rx_bounce_buffer, (temp + 3) / 4); + DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp); + temp &= ~HCINT_SOFTWARE_ONLY; + sc->sc_chan_state[x].hcint |= temp; } - - /* check if we should dump the data */ - if (!(sc->sc_active_rx_ep & (1U << ep_no))) { - dwc_otg_common_rx_ack(sc); - goto repeat; - } - - got_rx_status = 1; - - DPRINTFN(5, "RX status = 0x%08x: ch=%d pid=%d bytes=%d sts=%d\n", - sc->sc_last_rx_status, ep_no, - (sc->sc_last_rx_status >> 15) & 3, - GRXSTSRD_BCNT_GET(sc->sc_last_rx_status), - (sc->sc_last_rx_status >> 17) & 15); - } else { - got_rx_status = 0; } - } else { - uint8_t ep_no; - - ep_no = GRXSTSRD_CHNUM_GET( - sc->sc_last_rx_status); - - /* check if we should dump the data */ - if (!(sc->sc_active_rx_ep & (1U << ep_no))) { - dwc_otg_common_rx_ack(sc); - goto repeat; - } - - got_rx_status = 1; } +#endif /* execute FIFOs */ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) dwc_otg_xfer_do_fifo(sc, xfer); - if (got_rx_status) { - /* check if data was consumed */ - if (sc->sc_last_rx_status == 0) - goto repeat; - - /* disable RX FIFO level interrupt */ - sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - } - - if (sc->sc_flags.status_device_mode == 0 && sc->sc_xfer_complete == 0) { - /* update host transfer schedule, so that new transfers can be issued */ + if (sc->sc_flags.status_device_mode == 0 && + sc->sc_xfer_complete == 0) { + /* + * update host transfer schedule, + * so that new transfers can be issued + */ if (dwc_otg_update_host_transfer_schedule_locked(sc)) - goto repeat; + sc->sc_req_q_start =1; } } @@ -2728,15 +3501,18 @@ { struct dwc_otg_softc *sc = arg; int retval = FILTER_HANDLED; - uint32_t status; + uint32_t status, mask, frame; USB_BUS_SPIN_LOCK(&sc->sc_bus); /* read and clear interrupt status */ status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + mask = DWC_OTG_READ_4(sc, DOTG_GINTMSK); + status &= mask; /* clear interrupts we are handling here */ - DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & ~DWC_OTG_MSK_GINT_THREAD_IRQ); + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, + status & ~DWC_OTG_MSK_GINT_THREAD_IRQ); /* check for USB state change interrupts */ if ((status & DWC_OTG_MSK_GINT_THREAD_IRQ) != 0) @@ -2756,9 +3532,155 @@ } } - /* poll FIFOs, if any */ - dwc_otg_interrupt_poll_locked(sc); +#ifdef DEBUG + /* don't print message by SOF intr (every 125us) */ + if (status & ~GINTSTS_SOF) { + DPRINTFN(5, "msk=0x%08x, sts=0x%08x, " + "hamsk=0x%08x, ha=0x%08x\n", + sc->sc_irq_mask, status, + DWC_OTG_READ_4(sc, DOTG_HAINTMSK), + DWC_OTG_READ_4(sc, DOTG_HAINT)); + } +#endif + /* check SOF first (schedule) */ + int req_schedule = 0; + if (status & GINTSTS_SOF) { +#if 0 +#ifdef DEBUG + DPRINTFN(12, "GINTSTS_SOF\n"); +#endif + /* update schedule */ + if (dwc_otg_update_host_transfer_schedule_locked(sc)) + req_schedule++; +#endif + /* clear SOF */ + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, GINTSTS_SOF); + } + + /* + * update schedule and restart td on host channel + * (HS mode micro frame = 125us) + * XXX should not call here + * but many functions depend on polling + */ + frame = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK; + /* update host transfer schedule */ + if (sc->sc_flags.status_device_mode == 0 && + sc->sc_last_frame_num != frame) { + if (dwc_otg_update_host_transfer_schedule_locked(sc)) + req_schedule++; + } + /* run scheduled transfer */ + if (sc->sc_req_q_start || req_schedule) { + struct usb_xfer *xfer; + struct dwc_otg_td *td; + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + td = xfer->td_transfer_cache; + if (td == NULL) continue; + if (td->tt_scheduled != 0 || + (sc->sc_req_q_start && + td->channel >= DWC_OTG_MAX_CHANNELS)) + dwc_otg_xfer_do_fifo(sc, xfer); + } + } + sc->sc_req_q_start = 0; + +#ifndef NO_INT_TXFEMP + /* start queue (periodic FIFO) */ + status |= DWC_OTG_READ_4(sc, DOTG_GINTSTS) & mask; + if (status & GINTSTS_PTXFEMP) { + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, GINTSTS_PTXFEMP); + struct usb_xfer *xfer; + struct dwc_otg_td *td; + DPRINTFN(9, "GINTSTS_PTXFEMP\n"); + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + td = xfer->td_transfer_cache; + if (td == NULL) continue; + if (td->ep_type == UE_INTERRUPT || + td->ep_type == UE_ISOCHRONOUS) + if (td->channel >= DWC_OTG_MAX_CHANNELS) + dwc_otg_xfer_do_fifo(sc, xfer); + } + /* disable Periodic TxFIFO Empty Mask */ + DPRINTFN(5, "disable Periodic TxFIFO Empty Mask\n"); + sc->sc_irq_mask &= ~GINTMSK_PTXFEMPMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } + + /* start queue (non-periodic FIFO) */ + status |= DWC_OTG_READ_4(sc, DOTG_GINTSTS) & mask; + if (status & GINTSTS_NPTXFEMP) { + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, GINTSTS_NPTXFEMP); + struct usb_xfer *xfer; + struct dwc_otg_td *td; + DPRINTFN(9, "GINTSTS_NPTXFEMP\n"); + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + td = xfer->td_transfer_cache; + if (td == NULL) continue; + if (td->ep_type == UE_CONTROL || + td->ep_type == UE_BULK) + if (td->channel >= DWC_OTG_MAX_CHANNELS) + dwc_otg_xfer_do_fifo(sc, xfer); + } + /* disable Non-Periodic TxFIFO Empty Mask */ + DPRINTFN(5, "disable Non-Periodic TxFIFO Empty Mask\n"); + sc->sc_irq_mask &= ~GINTMSK_NPTXFEMPMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } +#endif /* NO_INT_TXFEMP */ + + /* receive IN packet */ + status |= DWC_OTG_READ_4(sc, DOTG_GINTSTS) & mask; + if (status & GINTSTS_RXFLVL) { + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, GINTSTS_RXFLVL); + struct dwc_otg_td *td; + uint32_t chnum; + DPRINTFN(9, "GINTSTS_RXFLVL\n"); + chnum = dwc_otg_intr_rxflvl(sc); + /* set received packet */ + if (chnum != INVALID_CHNUM) { + td = sc->sc_chan_state[chnum].td; + if (td != NULL) + dwc_otg_xfer_do_fifo(sc, td->xfer); + } + } + + /* handle host channel intr */ + status |= DWC_OTG_READ_4(sc, DOTG_GINTSTS) & mask; + if (status & GINTSTS_HCHINT) { + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, GINTSTS_HCHINT); + struct dwc_otg_td *td; + uint32_t haint, hcint; + int i; + DPRINTFN(9, "GINTSTS_HCHINT\n"); + haint = DWC_OTG_READ_4(sc, DOTG_HAINT); + DWC_OTG_RMB(); + for (i = 0; i < sc->sc_host_ch_max; i++) { + if ((haint & (1 << i)) == 0) + continue; + td = sc->sc_chan_state[i].td; + hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(i)); + DWC_OTG_WRITE_4(sc, DOTG_HCINT(i), hcint); + hcint &= ~HCINT_SOFTWARE_ONLY; + DPRINTFN(2, "GINTSTS_HCHINT: CH%d, 0x%08x\n", + i, hcint); + sc->sc_chan_state[i].hcint |= hcint; +#ifndef NO_HCINT_CHHLTD + if (hcint & HCINT_CHHLTD && + !sc->sc_chan_state[i].allocated) { + /* closed host channel */ + DPRINTFN(5, "halted ch=%u,w=%u->0\n", + i, sc->sc_chan_state[i].wait_halted); + sc->sc_chan_state[i].wait_halted = 0; + DWC_OTG_WMB(); + } else +#endif + if (hcint && td != NULL) + dwc_otg_xfer_do_fifo(sc, td->xfer); + } + } + if (sc->sc_xfer_complete != 0) retval = FILTER_SCHEDULE_THREAD; @@ -2771,7 +3693,7 @@ dwc_otg_interrupt(void *arg) { struct dwc_otg_softc *sc = arg; - uint32_t status; + uint32_t status, mask; USB_BUS_LOCK(&sc->sc_bus); USB_BUS_SPIN_LOCK(&sc->sc_bus); @@ -2778,9 +3700,12 @@ /* read and clear interrupt status */ status = DWC_OTG_READ_4(sc, DOTG_GINTSTS); + mask = DWC_OTG_READ_4(sc, DOTG_GINTMSK); + status &= mask; /* clear interrupts we are handling here */ - DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & DWC_OTG_MSK_GINT_THREAD_IRQ); + DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, + status & DWC_OTG_MSK_GINT_THREAD_IRQ); DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n", status, DWC_OTG_READ_4(sc, DOTG_HAINT), @@ -2951,9 +3876,16 @@ dwc_otg_interrupt_complete_locked(sc); if (sc->sc_flags.status_device_mode == 0) { - /* update host transfer schedule, so that new transfers can be issued */ - if (dwc_otg_update_host_transfer_schedule_locked(sc)) - dwc_otg_interrupt_poll_locked(sc); + /* + * update host transfer schedule, + * so that new transfers can be issued + */ + if (dwc_otg_update_host_transfer_schedule_locked(sc)) { + //sc->sc_req_q_start =1; + //dwc_otg_enable_sof_irq(sc); + /* restart queue */ + dwc_otg_enable_txfemp_irq(sc); + } } } USB_BUS_SPIN_UNLOCK(&sc->sc_bus); @@ -2975,6 +3907,7 @@ /* fill out the Transfer Descriptor */ td->func = temp->func; td->pc = temp->pc; + td->xfer = temp->xfer; td->offset = temp->offset; td->remainder = temp->len; td->tx_bytes = 0; @@ -3016,6 +3949,7 @@ /* setup temp */ temp.pc = NULL; + temp.xfer = xfer; temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.offset = 0; @@ -3188,6 +4122,13 @@ td = temp.td; xfer->td_transfer_last = td; + /* make sure unused td->channel is invalid */ + td = xfer->td_transfer_first; + while (td != NULL) { + td->channel = DWC_OTG_MAX_CHANNELS; + td = td->obj_next; + } + if (is_host) { struct dwc_otg_softc *sc; @@ -3309,10 +4250,58 @@ { struct usb_xfer *xfer = arg; - DPRINTF("xfer=%p\n", xfer); + DPRINTF("xfer=%p, timeout=%d\n", xfer, xfer->timeout); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); +#ifdef DEBUG + struct usb_xfer *xfer2; + struct dwc_otg_td *td; + char *funcname = "unknown"; + uint8_t x; + + DWC_OTG_RMB(); + td = xfer->td_transfer_first; + if (td != NULL) { + if (td->func == &dwc_otg_host_setup_tx) + funcname = "host_setup_tx"; + else if (td->func == &dwc_otg_host_data_rx) + funcname = "host_data_rx"; + else if (td->func == &dwc_otg_host_data_tx) + funcname = "host_data_tx"; + } + printf("xfer=%p, timeout=%d, addr=%u, ep=%u, ept=%d, " + "func='%s', st=%d, hcsplt=%u\n", + xfer, xfer->timeout, xfer->address, xfer->endpointno, + (td != NULL ? td->ep_type : -1), funcname, + (td != NULL ? td->state : -1), + (td != NULL ? td->hcsplt : 0)); + + for (x = 0; x != sc->sc_host_ch_max; x++) { + printf("CH%2u: %s%s hci=0x%08x hcc=0x%08x, " + "hci=0x%08x, hct=0x%08x, ept=%d\n", + x, + (sc->sc_chan_state[x].allocated ? "A" : ""), + (sc->sc_chan_state[x].wait_halted ? "W" : ""), + (sc->sc_chan_state[x].hcint), + DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)), + DWC_OTG_READ_4(sc, DOTG_HCINT(x)), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(x)), + (sc->sc_chan_state[x].td != NULL ? + sc->sc_chan_state[x].td->ep_type : -1)); + } + TAILQ_FOREACH(xfer2, &sc->sc_bus.intr_q.head, wait_entry) { + td = xfer2->td_transfer_cache; + if (td != NULL) { + printf("xfer=%p, addr=%u, ept=%u, ch=%u\n", + xfer2, xfer2->address, td->ep_type, td->channel); + } else { + printf("xfer=%p, addr=%u, td=NULL\n", + xfer2, xfer2->address); + } + } +#endif + /* transfer is transferred */ dwc_otg_device_done(xfer, USB_ERR_TIMEOUT); } @@ -3322,7 +4311,7 @@ { struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus); struct usb_xfer_root *xroot; - struct dwc_otg_td *td; + struct dwc_otg_td *td, *td_req; DPRINTFN(9, "\n"); @@ -3336,9 +4325,12 @@ if (sc->sc_flags.status_device_mode != 0) { dwc_otg_xfer_do_fifo(sc, xfer); if (dwc_otg_xfer_do_complete_locked(sc, xfer)) - goto done; + goto done_noirq; } + /* save request td */ + td_req = xfer->td_transfer_cache; + /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); @@ -3351,8 +4343,11 @@ if (sc->sc_flags.status_device_mode != 0) goto done; +#ifdef NO_INT_TXFEMP /* enable SOF interrupt, if any */ + sc->sc_req_q_start = 1; dwc_otg_enable_sof_irq(sc); +#endif td = xfer->td_transfer_cache; if (td->ep_type != UE_BULK) @@ -3372,6 +4367,22 @@ td->did_nak = 0; } done: +#ifndef NO_INT_TXFEMP + if (td_req->ep_type == UE_INTERRUPT || + td_req->ep_type == UE_ISOCHRONOUS) { + /* enable Periodic TxFIFO Empty Mask */ + DPRINTFN(5, "enable Periodic TxFIFO Empty Mask\n"); + sc->sc_irq_mask |= GINTMSK_PTXFEMPMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } else if (td_req->ep_type == UE_CONTROL || + td_req->ep_type == UE_BULK) { + /* enable Non-Periodic TxFIFO Empty Mask */ + DPRINTFN(5, "enable Non-Periodic TxFIFO Empty Mask\n"); + sc->sc_irq_mask |= GINTMSK_NPTXFEMPMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } +#endif +done_noirq: USB_BUS_SPIN_UNLOCK(&sc->sc_bus); } @@ -3442,7 +4453,11 @@ error = 0; break; } - td = td->obj_next; + /* don't access beyond last pointer */ + if (td != xfer->td_transfer_last) + td = td->obj_next; + else + td = NULL; /* this USB frame is complete */ error = 0; @@ -3522,8 +4537,11 @@ struct dwc_otg_td *td; td = xfer->td_transfer_cache; - if (td != NULL) + if (td != NULL) { + if (td->channel < DWC_OTG_MAX_CHANNELS) + DPRINTF("free by done ch=%u\n", td->channel); dwc_otg_host_channel_free(sc, td); + } } /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); @@ -3745,8 +4763,9 @@ /* turn on clocks */ dwc_otg_clocks_on(sc); - temp = DWC_OTG_READ_4(sc, DOTG_GSNPSID); - DPRINTF("Version = 0x%08x\n", temp); + /* HW version */ + sc->sc_gsnpsid = DWC_OTG_READ_4(sc, DOTG_GSNPSID); + DPRINTF("Version = 0x%08x\n", sc->sc_gsnpsid); /* disconnect */ DWC_OTG_WRITE_4(sc, DOTG_DCTL, Index: dwc_otg.h =================================================================== --- dwc_otg.h (revision 279589) +++ dwc_otg.h (working copy) @@ -39,11 +39,13 @@ #define DWC_OTG_SLOT_IDLE_MIN 2 #define DWC_OTG_NAK_MAX 8 /* 1 ms */ +#if 0 #define DWC_OTG_READ_4(sc, reg) \ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) #define DWC_OTG_WRITE_4(sc, reg, data) \ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) +#endif struct dwc_otg_td; struct dwc_otg_softc; @@ -54,6 +56,7 @@ struct dwc_otg_td *obj_next; dwc_otg_cmd_t *func; struct usb_page_cache *pc; + struct usb_xfer *xfer; uint32_t tx_bytes; uint32_t offset; uint32_t remainder; @@ -90,6 +93,7 @@ uint8_t set_toggle:1; uint8_t got_short:1; uint8_t tt_scheduled:1; + uint8_t req_ping; }; struct dwc_otg_tt_info { @@ -99,6 +103,7 @@ struct dwc_otg_std_temp { dwc_otg_cmd_t *func; struct usb_page_cache *pc; + struct usb_xfer *xfer; struct dwc_otg_td *td; struct dwc_otg_td *td_next; uint32_t len; @@ -152,11 +157,10 @@ }; struct dwc_otg_chan_state { + struct dwc_otg_td *td; uint16_t allocated; - uint16_t wait_sof; + uint16_t wait_halted; uint32_t hcint; - uint16_t tx_p_size; /* periodic */ - uint16_t tx_np_size; /* non-periodic */ }; struct dwc_otg_softc { @@ -177,10 +181,11 @@ uint32_t sc_rx_bounce_buffer[1024 / 4]; uint32_t sc_tx_bounce_buffer[MAX(512 * DWC_OTG_MAX_TXP, 1024) / 4]; + uint32_t sc_gsnpsid; +#define OTG_CORE_REV_2_80a 0x4f54280a /* RPi model B */ +#define OTG_CORE_REV_3_10a 0x4f54310a /* ODROID-C1 */ uint32_t sc_fifo_size; uint32_t sc_tx_max_size; - uint32_t sc_tx_cur_p_level; /* periodic */ - uint32_t sc_tx_cur_np_level; /* non-periodic */ uint32_t sc_irq_mask; uint32_t sc_last_rx_status; uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS]; @@ -189,6 +194,7 @@ uint32_t sc_tmr_val; uint32_t sc_hprt_val; uint32_t sc_xfer_complete; + uint32_t sc_np_txq_cnt; uint16_t sc_active_rx_ep; uint16_t sc_last_frame_num; @@ -198,6 +204,7 @@ uint8_t sc_dev_in_ep_max; uint8_t sc_host_ch_max; uint8_t sc_needsof; + uint8_t sc_req_q_start; uint8_t sc_rt_addr; /* root HUB address */ uint8_t sc_conf; /* root HUB config */ uint8_t sc_mode; /* mode of operation */ Index: dwc_otgreg.h =================================================================== --- dwc_otgreg.h (revision 279589) +++ dwc_otgreg.h (working copy) @@ -287,6 +287,8 @@ #define GRXSTSRH_BCNT_SHIFT 4 #define GRXSTSRH_CHNUM_MASK 0x0000000f #define GRXSTSRH_CHNUM_SHIFT 0 +#define GRXSTSRH_BCNT_GET(x) (((x) >> 4) & 0x7FF) +#define GRXSTSRH_CHNUM_GET(x) ((x) & 15) #define GRXSTSRD_FN_MASK 0x01e00000 #define GRXSTSRD_FN_GET(x) (((x) >> 21) & 15) @@ -561,7 +563,7 @@ #define HCINT_DEFAULT_MASK \ (HCINT_STALL | HCINT_BBLERR | \ HCINT_XACTERR | HCINT_NAK | HCINT_ACK | HCINT_NYET | \ - HCINT_CHHLTD | HCINT_FRMOVRUN | \ + HCINT_XFERCOMPL | HCINT_CHHLTD | HCINT_FRMOVRUN | \ HCINT_DATATGLERR) #define HCINT_HCH_DONE_MASK \ (HCINT_ACK | HCINT_RETRY | HCINT_NYET | \