svn commit: r267242 - stable/10/sys/dev/usb/controller
Hans Petter Selasky
hselasky at FreeBSD.org
Sun Jun 8 20:39:39 UTC 2014
Author: hselasky
Date: Sun Jun 8 20:39:39 2014
New Revision: 267242
URL: http://svnweb.freebsd.org/changeset/base/267242
Log:
MFC r267044, r267120, r267210 and r267211:
Multiple fixes for FULL and LOW speed USB transfers going through
the DWC OTG as split transactions. INTERRUPT transfers should have
a higher chance of success after this series of patches and the
chance of data loss should be reduced.
Modified:
stable/10/sys/dev/usb/controller/dwc_otg.c
stable/10/sys/dev/usb/controller/dwc_otg.h
Directory Properties:
stable/10/ (props changed)
Modified: stable/10/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- stable/10/sys/dev/usb/controller/dwc_otg.c Sun Jun 8 20:20:08 2014 (r267241)
+++ stable/10/sys/dev/usb/controller/dwc_otg.c Sun Jun 8 20:39:39 2014 (r267242)
@@ -98,6 +98,8 @@
GINTMSK_USBSUSPMSK | \
GINTMSK_IEPINTMSK | \
GINTMSK_SESSREQINTMSK | \
+ GINTMSK_RXFLVLMSK | \
+ GINTMSK_HCHINTMSK | \
GINTMSK_OTGINTMSK | \
GINTMSK_PRTINTMSK)
@@ -239,7 +241,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
for (x = 0; x != sc->sc_host_ch_max; x++) {
/* disable all host interrupts */
- DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0);
+ DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+ HCINT_DEFAULT_MASK);
}
DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ,
@@ -256,8 +259,9 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
/* store maximum periodic and non-periodic FIFO TX size */
sc->sc_tx_max_size = fifo_size;
- /* disable all host channel interrupts */
- DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0);
+ /* enable all host channel interrupts */
+ DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
+ (1U << sc->sc_host_ch_max) - 1U);
}
if (mode == DWC_MODE_DEVICE) {
@@ -568,11 +572,9 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof
{
DPRINTFN(5, "RX status clear\n");
- if (sc->sc_flags.status_device_mode != 0) {
- /* enable RX FIFO level interrupt */
- sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
- DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
- }
+ /* enable RX FIFO level interrupt */
+ sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
/* clear cached status */
sc->sc_last_rx_status = 0;
@@ -592,13 +594,13 @@ dwc_otg_clear_hcint(struct dwc_otg_softc
}
static uint8_t
-dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which, 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[which] < DWC_OTG_MAX_CHANNELS)
+ if (td->channel < DWC_OTG_MAX_CHANNELS)
return (0); /* already allocated */
/* check if device is suspended */
@@ -607,8 +609,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot
/* compute needed TX FIFO size */
if (is_out != 0) {
- if (td->ep_type == UE_INTERRUPT ||
- td->ep_type == UE_ISOCHRONOUS) {
+ 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)
@@ -658,7 +659,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot
sc->sc_active_rx_ep |= (1 << x);
/* set channel */
- td->channel[which] = x;
+ td->channel = x;
return (0); /* allocated */
}
@@ -668,16 +669,16 @@ dwc_otg_host_channel_alloc(struct dwc_ot
}
static void
-dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t which)
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
uint8_t x;
- if (td->channel[which] >= DWC_OTG_MAX_CHANNELS)
+ if (td->channel >= DWC_OTG_MAX_CHANNELS)
return; /* already freed */
/* free channel */
- x = td->channel[which];
- td->channel[which] = DWC_OTG_MAX_CHANNELS;
+ x = td->channel;
+ td->channel = DWC_OTG_MAX_CHANNELS;
DPRINTF("CH=%d\n", x);
@@ -687,7 +688,7 @@ dwc_otg_host_channel_free(struct dwc_otg
* after a fixed given amount of time the host channel is no
* longer doing any USB traffic:
*/
- if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
+ if (td->ep_type == UE_ISOCHRONOUS) {
/* double buffered */
sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX;
} else {
@@ -707,6 +708,18 @@ dwc_otg_host_channel_free(struct dwc_otg
sc->sc_active_rx_ep &= ~(1 << x);
}
+static void
+dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ /* dump any pending messages */
+ if (sc->sc_last_rx_status != 0) {
+ if (td->channel < DWC_OTG_MAX_CHANNELS &&
+ td->channel == GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) {
+ dwc_otg_common_rx_ack(sc);
+ }
+ }
+}
+
static uint8_t
dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
@@ -715,13 +728,15 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
uint32_t hcchar;
uint8_t delta;
- if (td->channel[0] < DWC_OTG_MAX_CHANNELS) {
- hcint = sc->sc_chan_state[td->channel[0]].hcint;
+ dwc_otg_host_dump_rx(sc, td);
+
+ 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",
- td->channel[0], td->state, hcint,
- DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])),
- DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0])));
+ td->channel, td->state, hcint,
+ DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+ DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
} else {
hcint = 0;
goto check_state;
@@ -731,12 +746,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
HCINT_ACK | HCINT_NYET)) {
/* give success bits priority over failure bits */
} else if (hcint & HCINT_STALL) {
- DPRINTF("CH=%d STALL\n", td->channel[0]);
+ DPRINTF("CH=%d STALL\n", td->channel);
td->error_stall = 1;
td->error_any = 1;
goto complete;
} else if (hcint & HCINT_ERRORS) {
- DPRINTF("CH=%d ERROR\n", td->channel[0]);
+ DPRINTF("CH=%d ERROR\n", td->channel);
td->errcnt++;
if (td->hcsplt != 0 || td->errcnt >= 3) {
td->error_any = 1;
@@ -804,7 +819,7 @@ check_state:
send_pkt:
/* free existing channel, if any */
- dwc_otg_host_channel_free(sc, td, 0);
+ dwc_otg_host_channel_free(sc, td);
if (sizeof(req) != td->remainder) {
td->error_any = 1;
@@ -827,7 +842,7 @@ send_pkt:
}
/* allocate a new channel */
- if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) {
+ if (dwc_otg_host_channel_alloc(sc, td, 1)) {
td->state = DWC_CHAN_ST_START;
goto busy;
}
@@ -841,23 +856,26 @@ send_pkt:
usbd_copy_out(td->pc, 0, &req, sizeof(req));
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
(sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
- DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt);
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
hcchar = td->hcchar;
hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
/* must enable channel before writing data to FIFO */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
/* transfer data into FIFO */
bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
- DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4);
+ DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
+
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
/* store number of bytes transmitted */
td->tx_bytes = sizeof(req);
@@ -865,7 +883,7 @@ send_pkt:
send_cpkt:
/* free existing channel, if any */
- dwc_otg_host_channel_free(sc, td, 0);
+ dwc_otg_host_channel_free(sc, td);
delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
@@ -880,34 +898,34 @@ send_cpkt:
goto complete;
}
/* allocate a new channel */
- if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) {
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
td->state = DWC_CHAN_ST_WAIT_C_PKT;
goto busy;
}
- /* wait until next slot before trying again */
- td->tt_complete_slot++;
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
td->hcsplt |= HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_C_ANE;
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
(HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
- DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt);
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
hcchar = td->hcchar;
hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
/* must enable channel before writing data to FIFO */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
busy:
return (1); /* busy */
complete:
- dwc_otg_host_channel_free(sc, td, 0);
+ dwc_otg_host_channel_free(sc, td);
return (0); /* complete */
}
@@ -1085,55 +1103,21 @@ busy:
}
static uint8_t
-dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- uint32_t hcint;
- uint32_t hcchar;
uint32_t count;
- uint8_t delta;
uint8_t channel;
- channel = td->channel[td->tt_channel_tog];
-
- 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",
- channel, td->state, hcint,
- DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
- DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
- } else {
- hcint = 0;
- goto check_state;
- }
-
- /* check interrupt bits */
-
- if (hcint & (HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
- /* give success bits priority over failure bits */
- } else 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->hcsplt != 0 || td->errcnt >= 3) {
- if (td->ep_type != UE_ISOCHRONOUS) {
- td->error_any = 1;
- goto complete;
- }
- }
- }
-
/* check endpoint status */
if (sc->sc_last_rx_status == 0)
- goto check_state;
+ goto busy;
+
+ channel = td->channel;
+ if (channel >= DWC_OTG_MAX_CHANNELS)
+ goto busy;
if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel)
- goto check_state;
+ goto busy;
switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
case GRXSTSRH_IN_DATA:
@@ -1141,7 +1125,7 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
DPRINTF("DATA ST=%d STATUS=0x%08x\n",
(int)td->state, (int)sc->sc_last_rx_status);
- if (hcint & HCINT_SOFTWARE_ONLY) {
+ if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) {
/*
* When using SPLIT transactions on interrupt
* endpoints, sometimes data occurs twice.
@@ -1203,21 +1187,71 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
td->remainder -= count;
td->offset += count;
- hcint |= HCINT_SOFTWARE_ONLY;
- sc->sc_chan_state[channel].hcint = hcint;
+ sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY;
break;
-
default:
break;
}
/* release FIFO */
dwc_otg_common_rx_ack(sc);
+busy:
+ return (0);
+complete:
+ return (1);
+}
-check_state:
- if (hcint & (HCINT_ERRORS | HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
- if (!(hcint & HCINT_ERRORS))
- td->errcnt = 0;
+static uint8_t
+dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ uint32_t hcint;
+ uint32_t hcchar;
+ uint8_t delta;
+ uint8_t channel;
+
+ channel = td->channel;
+
+ 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",
+ channel, td->state, hcint,
+ DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+ DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
+
+ /* check interrupt bits */
+ if (hcint & (HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ /* give success bits priority over failure bits */
+ } else 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->hcsplt != 0 || td->errcnt >= 3) {
+ if (td->ep_type != UE_ISOCHRONOUS) {
+ td->error_any = 1;
+ goto complete;
+ }
+ }
+ }
+
+ /* check channels for data, if any */
+ if (dwc_otg_host_data_rx_sub(sc, td))
+ goto complete;
+
+ /* refresh interrupt status */
+ hcint = sc->sc_chan_state[channel].hcint;
+
+ if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+ } else {
+ hcint = 0;
}
switch (td->state) {
@@ -1229,6 +1263,22 @@ check_state:
case DWC_CHAN_ST_WAIT_ANE:
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ if (td->ep_type == UE_INTERRUPT) {
+ /*
+ * The USB specification does not
+ * mandate a particular data toggle
+ * value for USB INTERRUPT
+ * transfers. Switch the data toggle
+ * value to receive the packet
+ * correctly:
+ */
+ if (hcint & HCINT_DATATGLERR) {
+ DPRINTF("Retrying packet due to "
+ "data toggle error\n");
+ td->toggle ^= 1;
+ goto receive_pkt;
+ }
+ }
td->did_nak++;
td->tt_scheduled = 0;
if (td->hcsplt != 0)
@@ -1306,7 +1356,7 @@ check_state:
receive_pkt:
/* free existing channel, if any */
- dwc_otg_host_channel_free(sc, td, td->tt_channel_tog);
+ dwc_otg_host_channel_free(sc, td);
if (td->hcsplt != 0) {
delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
@@ -1316,9 +1366,10 @@ receive_pkt:
}
delta = sc->sc_last_frame_num - td->tt_start_slot;
if (delta > DWC_OTG_TT_SLOT_MAX) {
- /* we missed the service interval */
- if (td->ep_type != UE_ISOCHRONOUS)
+ if (td->ep_type != UE_ISOCHRONOUS) {
+ /* we missed the service interval */
td->error_any = 1;
+ }
goto complete;
}
/* complete split */
@@ -1330,12 +1381,12 @@ receive_pkt:
}
/* allocate a new channel */
- if (dwc_otg_host_channel_alloc(sc, td, td->tt_channel_tog, 0)) {
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
td->state = DWC_CHAN_ST_WAIT_C_PKT;
goto busy;
}
- channel = td->channel[td->tt_channel_tog];
+ channel = td->channel;
/* set toggle, if any */
if (td->set_toggle) {
@@ -1357,46 +1408,23 @@ receive_pkt:
hcchar = td->hcchar;
hcchar |= HCCHAR_EPDIR_IN;
- /* check if other channel is allocated */
- if (td->channel[td->tt_channel_tog ^ 1] < DWC_OTG_MAX_CHANNELS) {
- /* second - receive after next SOF event */
- if ((sc->sc_last_frame_num & 1) != 0)
- hcchar |= HCCHAR_ODDFRM;
- else
- hcchar &= ~HCCHAR_ODDFRM;
-
- /* other channel is next */
- td->tt_channel_tog ^= 1;
- td->tt_complete_slot++;
+ /* 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 */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
- } else {
- /* first - receive after second next SOF event */
- if ((sc->sc_last_frame_num & 1) == 0)
- hcchar |= HCCHAR_ODDFRM;
- else
- hcchar &= ~HCCHAR_ODDFRM;
+ /* must enable channel before data can be received */
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
- /* must enable channel before data can be received */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
- if (td->hcsplt != 0) {
- if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
- /* allocate a second channel */
- td->tt_channel_tog ^= 1;
- goto receive_pkt;
- } else {
- td->tt_complete_slot++;
- }
- }
- }
goto busy;
receive_spkt:
/* free existing channel(s), if any */
- dwc_otg_host_channel_free(sc, td, 0);
- dwc_otg_host_channel_free(sc, td, 1);
+ dwc_otg_host_channel_free(sc, td);
delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
@@ -1412,19 +1440,16 @@ receive_spkt:
}
/* allocate a new channel */
- if (dwc_otg_host_channel_alloc(sc, td, 0, 0)) {
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
td->state = DWC_CHAN_ST_START;
goto busy;
}
- channel = td->channel[0];
+ channel = td->channel;
td->hcsplt &= ~HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_S_ANE;
- /* reset channel toggle */
- td->tt_channel_tog = 0;
-
/* receive one packet */
DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
(HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
@@ -1440,14 +1465,16 @@ receive_spkt:
hcchar = td->hcchar;
hcchar |= HCCHAR_EPDIR_IN;
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
/* must enable channel before data can be received */
DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
busy:
return (1); /* busy */
complete:
- dwc_otg_host_channel_free(sc, td, 0);
- dwc_otg_host_channel_free(sc, td, 1);
+ dwc_otg_host_channel_free(sc, td);
return (0); /* complete */
}
@@ -1569,7 +1596,9 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
uint8_t delta;
uint8_t channel;
- channel = td->channel[td->tt_channel_tog];
+ dwc_otg_host_dump_rx(sc, td);
+
+ channel = td->channel;
if (channel < DWC_OTG_MAX_CHANNELS) {
hcint = sc->sc_chan_state[channel].hcint;
@@ -1578,36 +1607,34 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
channel, td->state, hcint,
DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
- } else {
- hcint = 0;
- goto check_state;
- }
- if (hcint & (HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
- /* give success bits priority over failure bits */
- } else 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->hcsplt != 0 || td->errcnt >= 3) {
+ if (hcint & (HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ /* give success bits priority over failure bits */
+ } else 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->hcsplt != 0 || td->errcnt >= 3) {
+ td->error_any = 1;
+ goto complete;
+ }
}
- }
- if (hcint & (HCINT_ERRORS | HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
+ if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
- if (!(hcint & HCINT_ERRORS))
- td->errcnt = 0;
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+ } else {
+ hcint = 0;
}
-check_state:
switch (td->state) {
case DWC_CHAN_ST_START:
goto send_pkt;
@@ -1694,16 +1721,16 @@ check_state:
td->tt_xactpos++;
/* free existing channel, if any */
- dwc_otg_host_channel_free(sc, td, td->tt_channel_tog);
+ dwc_otg_host_channel_free(sc, td);
td->state = DWC_CHAN_ST_TX_PKT_ISOC;
/* FALLTHROUGH */
case DWC_CHAN_ST_TX_PKT_ISOC:
- if (dwc_otg_host_channel_alloc(sc, td, 0, 1))
+ if (dwc_otg_host_channel_alloc(sc, td, 1))
break;
- channel = td->channel[0];
+ channel = td->channel;
goto send_isoc_pkt;
default:
break;
@@ -1712,8 +1739,7 @@ check_state:
send_pkt:
/* free existing channel(s), if any */
- dwc_otg_host_channel_free(sc, td, 0);
- dwc_otg_host_channel_free(sc, td, 1);
+ dwc_otg_host_channel_free(sc, td);
if (td->hcsplt != 0) {
delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
@@ -1734,12 +1760,12 @@ send_pkt:
}
/* allocate a new channel */
- if (dwc_otg_host_channel_alloc(sc, td, 0, 1)) {
+ if (dwc_otg_host_channel_alloc(sc, td, 1)) {
td->state = DWC_CHAN_ST_START;
goto busy;
}
- channel = td->channel[0];
+ channel = td->channel;
/* set toggle, if any */
if (td->set_toggle) {
@@ -1757,7 +1783,7 @@ send_isoc_pkt:
count = td->remainder;
if (count > HCSPLT_XACTLEN_BURST) {
DPRINTF("TT overflow\n");
- td->error = 1;
+ td->error_any = 1;
goto complete;
}
/* Update transaction position */
@@ -1883,7 +1909,7 @@ send_isoc_pkt:
send_cpkt:
/* free existing channel, if any */
- dwc_otg_host_channel_free(sc, td, td->tt_channel_tog);
+ dwc_otg_host_channel_free(sc, td);
delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
@@ -1899,12 +1925,12 @@ send_cpkt:
}
/* allocate a new channel */
- if (dwc_otg_host_channel_alloc(sc, td, td->tt_channel_tog, 0)) {
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
td->state = DWC_CHAN_ST_WAIT_C_PKT;
goto busy;
}
- channel = td->channel[td->tt_channel_tog];
+ channel = td->channel;
td->hcsplt |= HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_C_ANE;
@@ -1917,48 +1943,22 @@ send_cpkt:
hcchar = td->hcchar;
hcchar &= ~HCCHAR_EPDIR_IN;
- /* check if other channel is allocated */
- if (td->channel[td->tt_channel_tog ^ 1] < DWC_OTG_MAX_CHANNELS) {
- /* second - receive after next SOF event */
- if ((sc->sc_last_frame_num & 1) != 0)
- hcchar |= HCCHAR_ODDFRM;
- else
- hcchar &= ~HCCHAR_ODDFRM;
-
- /* other channel is next */
- td->tt_channel_tog ^= 1;
- /* wait until next slot before trying again */
- td->tt_complete_slot++;
-
- /* must enable channel before data can be received */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
- } else {
- /* first - receive after second next SOF event */
- if ((sc->sc_last_frame_num & 1) == 0)
- hcchar |= HCCHAR_ODDFRM;
- else
- hcchar &= ~HCCHAR_ODDFRM;
+ /* 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 */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+ /* must enable channel before data can be received */
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
- if (td->hcsplt != 0) {
- if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) {
- /* allocate a second channel */
- td->tt_channel_tog ^= 1;
- goto send_cpkt;
- } else {
- /* wait until next slot before trying again */
- td->tt_complete_slot++;
- }
- }
- }
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
busy:
return (1); /* busy */
complete:
- dwc_otg_host_channel_free(sc, td, 0);
- dwc_otg_host_channel_free(sc, td, 1);
+ dwc_otg_host_channel_free(sc, td);
return (0); /* complete */
}
@@ -2348,6 +2348,14 @@ dwc_otg_host_channel_disable(struct dwc_
sc->sc_chan_state[x].tx_np_size = 0;
}
+static uint16_t
+dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo)
+{
+ if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX)
+ pinfo->slot_index++;
+ return (pinfo->slot_index);
+}
+
static uint8_t
dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc)
{
@@ -2356,6 +2364,7 @@ dwc_otg_update_host_transfer_schedule_lo
struct usb_xfer *xfer_next;
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;
@@ -2377,11 +2386,53 @@ dwc_otg_update_host_transfer_schedule_lo
}
if ((temp & 7) == 0) {
+
+ /* reset the schedule */
+ memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
+
TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
td = xfer->td_transfer_cache;
if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
continue;
+ /* check for IN direction */
+ if ((td->hcchar & HCCHAR_EPDIR_IN) != 0)
+ continue;
+
+ /* execute more frames */
+ td->tmr_val = 0;
+
+ sc->sc_needsof = 1;
+
+ if (td->hcsplt == 0 || td->tt_scheduled != 0)
+ continue;
+
+ /* compute slot */
+ slot = dwc_otg_compute_isoc_rx_tt_slot(
+ sc->sc_tt_info + td->tt_index);
+ if (slot > 3) {
+ /*
+ * Not enough time to get complete
+ * split executed.
+ */
+ continue;
+ }
+ /* Delayed start */
+ td->tt_start_slot = temp + slot;
+ td->tt_scheduled = 1;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+ }
+
+ TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
+ td = xfer->td_transfer_cache;
+ if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
+ continue;
+
+ /* check for OUT direction */
+ if ((td->hcchar & HCCHAR_EPDIR_IN) == 0)
+ continue;
+
/* execute more frames */
td->tmr_val = 0;
@@ -2391,8 +2442,7 @@ dwc_otg_update_host_transfer_schedule_lo
continue;
/* Start ASAP */
- td->tt_start_slot = temp + 0;
- td->tt_complete_slot = temp + 2;
+ td->tt_start_slot = temp;
td->tt_scheduled = 1;
TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2418,8 +2468,7 @@ dwc_otg_update_host_transfer_schedule_lo
}
/* start ASAP */
- td->tt_start_slot = temp + 0;
- td->tt_complete_slot = temp + 2;
+ td->tt_start_slot = temp;
sc->sc_needsof = 1;
td->tt_scheduled = 1;
TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
@@ -2440,8 +2489,7 @@ dwc_otg_update_host_transfer_schedule_lo
continue;
/* start ASAP */
- td->tt_start_slot = temp + 0;
- td->tt_complete_slot = temp + 1;
+ td->tt_start_slot = temp;
td->tt_scheduled = 1;
TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2462,8 +2510,7 @@ dwc_otg_update_host_transfer_schedule_lo
continue;
/* start ASAP */
- td->tt_start_slot = temp + 0;
- td->tt_complete_slot = temp + 1;
+ td->tt_start_slot = temp;
td->tt_scheduled = 1;
TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2739,8 +2786,6 @@ dwc_otg_interrupt(void *arg)
/* Disable SOF interrupt */
sc->sc_irq_mask &= ~GINTMSK_SOFMSK;
- /* Enable RX frame interrupt */
- sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
/* complete root HUB interrupt endpoint */
@@ -2781,7 +2826,7 @@ dwc_otg_interrupt(void *arg)
* suspend and RX frame interrupt:
*/
sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK);
- sc->sc_irq_mask |= (GINTMSK_USBSUSPMSK | GINTMSK_RXFLVLMSK);
+ sc->sc_irq_mask |= GINTMSK_USBSUSPMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
/* complete root HUB interrupt endpoint */
@@ -2852,10 +2897,6 @@ dwc_otg_interrupt(void *arg)
/* complete root HUB interrupt endpoint */
dwc_otg_root_intr(sc);
- /* disable RX FIFO level interrupt */
- sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK;
- DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
-
/* update host frame interval */
dwc_otg_update_host_frame_interval(sc);
}
@@ -2935,12 +2976,10 @@ dwc_otg_setup_standard_chain_sub(struct
td->set_toggle = 0;
td->got_short = 0;
td->did_nak = 0;
- td->channel[0] = DWC_OTG_MAX_CHANNELS;
- td->channel[1] = DWC_OTG_MAX_CHANNELS;
+ td->channel = DWC_OTG_MAX_CHANNELS;
td->state = 0;
td->errcnt = 0;
td->tt_scheduled = 0;
- td->tt_channel_tog = 0;
td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
}
@@ -3156,9 +3195,21 @@ dwc_otg_setup_standard_chain(struct usb_
(xfer->max_packet_size << HCCHAR_MPS_SHIFT) |
HCCHAR_CHENA;
- /* XXX stability hack - possible HW issue */
- if (td->ep_type == UE_CONTROL)
- hcchar |= (UE_BULK << HCCHAR_EPTYPE_SHIFT);
+ /*
+ * We are not always able to meet the timing
+ * requirements of the USB interrupt endpoint's
+ * complete split token, when doing transfers going
+ * via a transaction translator. Use the CONTROL
+ * transfer type instead of the INTERRUPT transfer
+ * type in general, as a means to workaround
+ * that. This trick should work for both FULL and LOW
+ * speed USB traffic going through a TT. For non-TT
+ * traffic it works aswell. The reason for using
+ * CONTROL type instead of BULK is that some TTs might
+ * reject LOW speed BULK traffic.
+ */
+ if (td->ep_type == UE_INTERRUPT)
+ hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT);
else
hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT);
@@ -3199,13 +3250,12 @@ dwc_otg_setup_standard_chain(struct usb_
break;
case USB_SPEED_HIGH:
hcsplt = 0;
- if (td->ep_type == UE_ISOCHRONOUS ||
- td->ep_type == UE_INTERRUPT) {
- hcchar |= ((xfer->max_packet_count & 3)
- << HCCHAR_MC_SHIFT);
- }
if (td->ep_type == UE_INTERRUPT) {
uint32_t ival;
+#if 0
+ hcchar |= ((xfer->max_packet_count & 3)
+ << HCCHAR_MC_SHIFT);
+#endif
ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
if (ival == 0)
ival = 1;
@@ -3214,6 +3264,8 @@ dwc_otg_setup_standard_chain(struct usb_
td->tmr_val = sc->sc_tmr_val + ival;
td->tmr_res = ival;
} else if (td->ep_type == UE_ISOCHRONOUS) {
+ hcchar |= ((xfer->max_packet_count & 3)
+ << HCCHAR_MC_SHIFT);
td->tmr_val = 0;
td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer);
} else {
@@ -3458,12 +3510,9 @@ dwc_otg_device_done(struct usb_xfer *xfe
} else {
struct dwc_otg_td *td;
- td = xfer->td_transfer_first;
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable-10
mailing list