diff options
author | hselasky <hselasky@FreeBSD.org> | 2012-09-27 15:23:38 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2012-09-27 15:23:38 +0000 |
commit | b3f5fac122016c5fb684531f1d9a0f948f8736b0 (patch) | |
tree | 0393e0f01148ff6e65e103df8b404934f319cc0e /sys/dev | |
parent | d677fb07e182e385be1459f5c49b3c4a8676b360 (diff) | |
download | FreeBSD-src-b3f5fac122016c5fb684531f1d9a0f948f8736b0.zip FreeBSD-src-b3f5fac122016c5fb684531f1d9a0f948f8736b0.tar.gz |
Make sure we record NAK tokens in the TD structure for IN direction.
Improve host channel disabling. Wait two times 125us for channel to be
disabled. The DWC OTG doesn't like when channels are re-used too early.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.c | 103 | ||||
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.h | 2 | ||||
-rw-r--r-- | sys/dev/usb/controller/dwc_otgreg.h | 1 |
3 files changed, 43 insertions, 63 deletions
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c index 6c72399..d46d613 100644 --- a/sys/dev/usb/controller/dwc_otg.c +++ b/sys/dev/usb/controller/dwc_otg.c @@ -507,7 +507,6 @@ static uint8_t dwc_otg_host_channel_wait(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; - uint16_t frame; uint8_t x; x = td->channel; @@ -517,32 +516,20 @@ dwc_otg_host_channel_wait(struct dwc_otg_td *td) /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); - if (sc->sc_chan_state[x].hcint & HCINT_HALTED_ONLY) { + if (sc->sc_chan_state[x].wait_sof == 0) { dwc_otg_clear_hcint(sc, x); - return (1); + return (1); /* done */ } if (x == 0) return (0); /* wait */ - frame = DWC_OTG_READ_4(sc, DOTG_HFNUM) & HFNUM_FRNUM_MASK; - /* 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; - } - - if (sc->sc_chan_state[x].last_frame == frame) + if (sc->sc_chan_state[x].wait_sof != 0) continue; sc->sc_chan_state[td->channel].allocated = 0; @@ -556,9 +543,8 @@ dwc_otg_host_channel_wait(struct dwc_otg_td *td) /* 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); + DPRINTF("CH=%d HCCHAR=0x%08x " + "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt); /* ack any pending messages */ if (sc->sc_last_rx_status != 0 && @@ -583,7 +569,6 @@ static uint8_t dwc_otg_host_channel_alloc(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; - uint16_t frame; uint8_t x; uint8_t max_channel; @@ -601,23 +586,11 @@ dwc_otg_host_channel_alloc(struct dwc_otg_td *td) x = 1; } - frame = DWC_OTG_READ_4(sc, DOTG_HFNUM) & HFNUM_FRNUM_MASK; - for (; x != max_channel; 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; - } - - if (sc->sc_chan_state[x].last_frame == frame) + if (sc->sc_chan_state[x].wait_sof != 0) continue; sc->sc_chan_state[x].allocated = 1; @@ -625,9 +598,8 @@ dwc_otg_host_channel_alloc(struct dwc_otg_td *td) /* 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); + DPRINTF("CH=%d HCCHAR=0x%08x " + "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt); /* set active channel */ sc->sc_active_rx_ep |= (1 << x); @@ -644,12 +616,18 @@ static void dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x) { uint32_t hcchar; + if (sc->sc_chan_state[x].wait_sof != 0) + return; hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x)); if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) { + /* disable channel */ + DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), + HCCHAR_CHENA | HCCHAR_CHDIS); /* don't re-use channel until next SOF is transmitted */ - sc->sc_chan_state[x].last_frame = - DWC_OTG_READ_4(sc, DOTG_HFNUM) & HFNUM_FRNUM_MASK; - DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), HCCHAR_CHENA | HCCHAR_CHDIS); + sc->sc_chan_state[x].wait_sof = 2; + /* enable SOF interrupt */ + sc->sc_irq_mask |= GINTMSK_SOFMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } } @@ -736,16 +714,9 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td) 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; } @@ -1020,7 +991,7 @@ dwc_otg_host_rate_check(struct dwc_otg_td *td, td->tmr_val += td->tmr_res; } else if (td->did_nak != 0) { goto busy; - } + } if (ep_type == UE_ISOCHRONOUS) { td->toggle = 0; @@ -1088,12 +1059,6 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td) 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; } @@ -1170,6 +1135,8 @@ check_state: if (!dwc_otg_host_channel_wait(td)) break; + td->did_nak = 1; + if (td->hcsplt != 0) goto receive_spkt; else @@ -1202,6 +1169,9 @@ check_state: if (hcint & (HCINT_RETRY | HCINT_ERRORS)) { if (!dwc_otg_host_channel_wait(td)) break; + + td->did_nak = 1; + goto receive_spkt; } if (hcint & (HCINT_ACK | HCINT_NYET)) { @@ -1447,12 +1417,6 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) 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; } @@ -1989,8 +1953,7 @@ repeat: 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 | - HCINT_HALTED_ONLY); + temp &= ~HCINT_SOFTWARE_ONLY; sc->sc_chan_state[x].hcint |= temp; } } @@ -2286,6 +2249,24 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc) } } + /* check for SOF interrupt */ + if (status & GINTSTS_SOF) { + if (sc->sc_irq_mask & GINTMSK_SOFMSK) { + uint8_t x; + uint8_t y; + for (x = y = 0; x != sc->sc_host_ch_max; x++) { + if (sc->sc_chan_state[x].wait_sof != 0) { + if (--(sc->sc_chan_state[x].wait_sof) != 0) + y = 1; + } + } + if (y == 0) { + sc->sc_irq_mask &= ~GINTMSK_SOFMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + } + } + } + /* poll FIFO(s) */ dwc_otg_interrupt_poll(sc); diff --git a/sys/dev/usb/controller/dwc_otg.h b/sys/dev/usb/controller/dwc_otg.h index f58ddec..1ff2583 100644 --- a/sys/dev/usb/controller/dwc_otg.h +++ b/sys/dev/usb/controller/dwc_otg.h @@ -140,7 +140,7 @@ struct dwc_otg_profile { struct dwc_otg_chan_state { uint32_t hcint; - uint16_t last_frame; + uint8_t wait_sof; uint8_t allocated; uint8_t suspended; }; diff --git a/sys/dev/usb/controller/dwc_otgreg.h b/sys/dev/usb/controller/dwc_otgreg.h index ddc6fbe..a91f7dc 100644 --- a/sys/dev/usb/controller/dwc_otgreg.h +++ b/sys/dev/usb/controller/dwc_otgreg.h @@ -546,7 +546,6 @@ #define HCINT_RETRY \ (HCINT_DATATGLERR | HCINT_FRMOVRUN | HCINT_NAK) -#define HCINT_HALTED_ONLY (1<<21) /* BSD only */ #define HCINT_SOFTWARE_ONLY (1<<20) /* BSD only */ #define HCINT_DATATGLERR (1<<10) #define HCINT_FRMOVRUN (1<<9) |