summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2012-09-27 15:23:38 +0000
committerhselasky <hselasky@FreeBSD.org>2012-09-27 15:23:38 +0000
commitb3f5fac122016c5fb684531f1d9a0f948f8736b0 (patch)
tree0393e0f01148ff6e65e103df8b404934f319cc0e /sys/dev
parentd677fb07e182e385be1459f5c49b3c4a8676b360 (diff)
downloadFreeBSD-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.c103
-rw-r--r--sys/dev/usb/controller/dwc_otg.h2
-rw-r--r--sys/dev/usb/controller/dwc_otgreg.h1
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)
OpenPOWER on IntegriCloud