Fix LOW and FULL speed USB INTERRUPT endpoint support for the

DWC OTG driver. Fix a hang issue when using LOW and FULL speed
BULK traffic. Make sure we don't ask for data in the last
microframe. This allows using devices like USB mice and USB
keyboards connected to the RPI-B.

Suggested by:	gonzo @
This commit is contained in:
hselasky 2012-11-09 16:28:58 +00:00
parent 3c8fa044cf
commit 6bb0563ddc
3 changed files with 159 additions and 41 deletions

View File

@ -371,6 +371,15 @@ dwc_otg_pull_down(struct dwc_otg_softc *sc)
}
}
static void
dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc)
{
if (sc->sc_irq_mask & GINTSTS_SOF)
return;
sc->sc_irq_mask |= GINTSTS_SOF;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
}
static void
dwc_otg_resume_irq(struct dwc_otg_softc *sc)
{
@ -523,10 +532,6 @@ dwc_otg_host_channel_wait(struct dwc_otg_td *td)
if (x == 0)
return (0); /* wait */
/* assume NAK-ing is next */
if (sc->sc_chan_state[x].hcint & HCINT_NYET)
return (0); /* wait */
/* find new disabled channel */
for (x = 1; x != sc->sc_host_ch_max; x++) {
@ -629,8 +634,7 @@ dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x)
/* don't re-use channel until next SOF is transmitted */
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);
dwc_otg_enable_sof_irq(sc);
}
}
@ -688,14 +692,15 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
if (hcint & HCINT_STALL) {
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", td->channel);
td->error_stall = 1;
td->error_any = 1;
return (0); /* complete */
}
if (hcint & HCINT_ERRORS) {
} else if (hcint & HCINT_ERRORS) {
DPRINTF("CH=%d ERROR\n", td->channel);
td->errcnt++;
if (td->hcsplt != 0 || td->errcnt >= 3) {
@ -769,6 +774,8 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
return (0); /* complete */
}
break;
case DWC_CHAN_ST_TX_PKT_SYNC:
goto send_pkt_sync;
default:
break;
}
@ -780,7 +787,21 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
return (0); /* complete */
}
send_pkt_sync:
if (td->hcsplt != 0) {
uint32_t count;
count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
/* check for not first microframe */
if (count != 0) {
/* enable SOF interrupt */
dwc_otg_enable_sof_irq(sc);
/* set state */
td->state = DWC_CHAN_ST_TX_PKT_SYNC;
dwc_otg_host_channel_free(td);
return (1); /* busy */
}
td->hcsplt &= ~HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_S_ANE;
} else {
@ -961,8 +982,7 @@ dwc_otg_setup_rx(struct dwc_otg_td *td)
}
static uint8_t
dwc_otg_host_rate_check(struct dwc_otg_td *td,
uint8_t do_inc)
dwc_otg_host_rate_check(struct dwc_otg_td *td)
{
struct dwc_otg_softc *sc;
uint8_t ep_type;
@ -981,13 +1001,14 @@ dwc_otg_host_rate_check(struct dwc_otg_td *td,
td->hcchar |= HCCHAR_ODDFRM;
else
td->hcchar &= ~HCCHAR_ODDFRM;
if (do_inc)
td->tmr_val += td->tmr_res;
td->tmr_val += td->tmr_res;
} else if (ep_type == UE_INTERRUPT) {
if ((sc->sc_tmr_val & 0xFF) != td->tmr_val)
uint8_t delta;
delta = sc->sc_tmr_val - td->tmr_val;
if (delta >= 128)
goto busy;
if (do_inc)
td->tmr_val += td->tmr_res;
td->tmr_val = sc->sc_tmr_val + td->tmr_res;
} else if (td->did_nak != 0) {
goto busy;
}
@ -1010,6 +1031,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
uint32_t hcint;
uint32_t hcchar;
uint32_t count;
uint8_t ep_type;
if (dwc_otg_host_channel_alloc(td))
return (1); /* busy */
@ -1017,6 +1039,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
/* get pointer to softc */
sc = DWC_OTG_PC2SC(td->pc);
ep_type = ((td->hcchar &
HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
hcint = sc->sc_chan_state[td->channel].hcint;
DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
@ -1026,14 +1051,15 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
/* check interrupt bits */
if (hcint & HCINT_STALL) {
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", td->channel);
td->error_stall = 1;
td->error_any = 1;
return (0); /* complete */
}
if (hcint & HCINT_ERRORS) {
} else if (hcint & HCINT_ERRORS) {
DPRINTF("CH=%d ERROR\n", td->channel);
td->errcnt++;
if (td->hcsplt != 0 || td->errcnt >= 3) {
@ -1063,7 +1089,17 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
case GRXSTSRH_IN_DATA:
DPRINTF("DATA\n");
DPRINTF("DATA ST=%d STATUS=0x%08x\n",
(int)td->state, (int)sc->sc_last_rx_status);
if (hcint & HCINT_SOFTWARE_ONLY) {
/*
* When using SPLIT transactions on interrupt
* endpoints, sometimes data occurs twice.
*/
DPRINTF("Data already received\n");
break;
}
td->toggle ^= 1;
@ -1131,12 +1167,16 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
else
goto receive_pkt;
}
if (hcint & HCINT_NYET) {
if (td->hcsplt != 0)
goto receive_pkt;
}
if (!(hcint & HCINT_SOFTWARE_ONLY))
if (!(hcint & HCINT_SOFTWARE_ONLY)) {
if (hcint & HCINT_NYET) {
if (td->hcsplt != 0) {
if (!dwc_otg_host_channel_wait(td))
break;
goto receive_pkt;
}
}
break;
}
if (hcint & (HCINT_ACK | HCINT_NYET)) {
if (!dwc_otg_host_channel_wait(td))
break;
@ -1179,20 +1219,38 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
case DWC_CHAN_ST_RX_SPKT:
goto receive_spkt;
case DWC_CHAN_ST_RX_SPKT_SYNC:
goto receive_spkt_sync;
default:
break;
}
goto busy;
receive_pkt:
if (dwc_otg_host_rate_check(td, 1)) {
if (td->hcsplt != 0) {
count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
/* check for even microframes */
if (count == td->curr_frame) {
td->state = DWC_CHAN_ST_RX_PKT;
dwc_otg_host_channel_free(td);
/* enable SOF interrupt */
dwc_otg_enable_sof_irq(sc);
goto busy;
} else if (count == 0) {
/* check for start split timeout */
goto receive_spkt;
}
td->curr_frame = count;
td->hcsplt |= HCSPLT_COMPSPLT;
} else if (dwc_otg_host_rate_check(td)) {
td->state = DWC_CHAN_ST_RX_PKT;
dwc_otg_host_channel_free(td);
goto busy;
}
if (td->hcsplt != 0)
td->hcsplt |= HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_ANE;
/* receive one packet */
@ -1213,12 +1271,42 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
goto busy;
receive_spkt:
if (dwc_otg_host_rate_check(td, 0)) {
if (dwc_otg_host_rate_check(td)) {
td->state = DWC_CHAN_ST_RX_SPKT;
dwc_otg_host_channel_free(td);
goto busy;
}
receive_spkt_sync:
if (ep_type == UE_INTERRUPT ||
ep_type == UE_ISOCHRONOUS) {
count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
td->curr_frame = count;
/* check for non-zero microframe */
if (count != 0) {
/* enable SOF interrupt */
dwc_otg_enable_sof_irq(sc);
/* set state */
td->state = DWC_CHAN_ST_RX_SPKT_SYNC;
dwc_otg_host_channel_free(td);
goto busy;
}
} else {
count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
td->curr_frame = count;
/* check for two last frames */
if (count >= 6) {
/* enable SOF interrupt */
dwc_otg_enable_sof_irq(sc);
/* set state */
td->state = DWC_CHAN_ST_RX_SPKT_SYNC;
dwc_otg_host_channel_free(td);
goto busy;
}
}
td->hcsplt &= ~HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_S_ANE;
@ -1377,14 +1465,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
if (hcint & HCINT_STALL) {
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", td->channel);
td->error_stall = 1;
td->error_any = 1;
return (0); /* complete */
}
if (hcint & HCINT_ERRORS) {
} else if (hcint & HCINT_ERRORS) {
DPRINTF("CH=%d ERROR\n", td->channel);
td->errcnt++;
if (td->hcsplt != 0 || td->errcnt >= 3) {
@ -1482,6 +1571,9 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
case DWC_CHAN_ST_TX_PKT:
goto send_pkt;
case DWC_CHAN_ST_TX_PKT_SYNC:
goto send_pkt_sync;
case DWC_CHAN_ST_TX_CPKT:
goto send_cpkt;
@ -1491,13 +1583,25 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
goto busy;
send_pkt:
if (dwc_otg_host_rate_check(td, 1)) {
if (dwc_otg_host_rate_check(td)) {
td->state = DWC_CHAN_ST_TX_PKT;
dwc_otg_host_channel_free(td);
goto busy;
}
send_pkt_sync:
if (td->hcsplt != 0) {
count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
/* check for first or last microframe */
if (count == 7 || count == 0) {
/* enable SOF interrupt */
dwc_otg_enable_sof_irq(sc);
/* set state */
td->state = DWC_CHAN_ST_TX_PKT_SYNC;
dwc_otg_host_channel_free(td);
goto busy;
}
td->hcsplt &= ~HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_S_ANE;
} else {
@ -1549,6 +1653,13 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
goto busy;
send_cpkt:
count = DWC_OTG_READ_4(sc, DOTG_HFNUM) & 7;
/* check for first microframe */
if (count == 0) {
/* send packet again */
goto send_pkt;
}
td->hcsplt |= HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_C_ANE;
@ -2242,6 +2353,9 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc)
if (sc->sc_irq_mask & GINTMSK_SOFMSK) {
uint8_t x;
uint8_t y;
DPRINTFN(12, "SOF interrupt\n");
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)
@ -2280,6 +2394,7 @@ dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp)
td->remainder = temp->len;
td->tx_bytes = 0;
td->error_any = 0;
td->error_stall = 0;
td->npkt = 0;
td->did_stall = temp->did_stall;
td->short_pkt = temp->short_pkt;
@ -2531,8 +2646,8 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
if (ival == 0)
ival = 1;
else if (ival > 255)
ival = 255;
else if (ival > 127)
ival = 127;
td->tmr_val = sc->sc_tmr_val + ival;
td->tmr_res = ival;
}
@ -2549,8 +2664,8 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
if (ival == 0)
ival = 1;
else if (ival > 255)
ival = 255;
else if (ival > 127)
ival = 127;
td->tmr_val = sc->sc_tmr_val + ival;
td->tmr_res = ival;
}

View File

@ -60,6 +60,7 @@ struct dwc_otg_td {
uint8_t errcnt;
uint8_t tmr_res;
uint8_t tmr_val;
uint8_t curr_frame;
uint8_t ep_no;
uint8_t channel;
uint8_t state;
@ -69,8 +70,10 @@ struct dwc_otg_td {
#define DWC_CHAN_ST_WAIT_C_ANE 3
#define DWC_CHAN_ST_RX_PKT 4
#define DWC_CHAN_ST_RX_SPKT 5
#define DWC_CHAN_ST_RX_SPKT_SYNC 6
#define DWC_CHAN_ST_TX_PKT 4
#define DWC_CHAN_ST_TX_CPKT 5
#define DWC_CHAN_ST_TX_PKT_SYNC 6
uint8_t error:1;
uint8_t error_any:1;
uint8_t error_stall:1;

View File

@ -299,7 +299,7 @@
#define GRXSTSRD_DPID_DATA0 (0<<15)
#define GRXSTSRD_DPID_DATA1 (2<<15)
#define GRXSTSRD_DPID_DATA2 (1<<15)
#define GRXSTSRD_PID_MDATA (3<<15)
#define GRXSTSRD_DPID_MDATA (3<<15)
#define GRXSTSRD_BCNT_MASK 0x00007ff0
#define GRXSTSRD_BCNT_GET(x) (((x) >> 4) & 0x7FF)
#define GRXSTSRD_BCNT_SHIFT 4