MFC r283922:

Fix for control endpoint handling in the DWC OTG driver. The data
stage processing is only allowed after the setup complete event has
been received. Else a race may occur and the OUT data can be corrupted.
While at it ensure resetting a FIFO has the required wait loop.
This commit is contained in:
hselasky 2015-06-05 07:17:14 +00:00
parent 7094f6ff47
commit 8707ccbe03

View File

@ -181,6 +181,22 @@ dwc_otg_get_hw_ep_profile(struct usb_device *udev,
*ppf = NULL;
}
static void
dwc_otg_tx_fifo_reset(struct dwc_otg_softc *sc, uint32_t value)
{
uint32_t temp;
/* reset FIFO */
DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, value);
/* wait for reset to complete */
for (temp = 0; temp != 16; temp++) {
value = DWC_OTG_READ_4(sc, DOTG_GRSTCTL);
if (!(value & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)))
break;
}
}
static int
dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
{
@ -335,12 +351,11 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
}
/* reset RX FIFO */
DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
GRSTCTL_RXFFLSH);
dwc_otg_tx_fifo_reset(sc, GRSTCTL_RXFFLSH);
if (mode != DWC_MODE_OTG) {
/* reset all TX FIFOs */
DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
dwc_otg_tx_fifo_reset(sc,
GRSTCTL_TXFIFO(0x10) |
GRSTCTL_TXFFLSH);
} else {
@ -951,15 +966,21 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != 0)
goto not_complete;
if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
GRXSTSRD_DPID_DATA0) {
/* release FIFO */
dwc_otg_common_rx_ack(sc);
goto not_complete;
}
if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
GRXSTSRD_STP_DATA) {
if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
GRXSTSRD_STP_COMPLETE || td->remainder != 0) {
/* release FIFO */
dwc_otg_common_rx_ack(sc);
goto not_complete;
}
/* release FIFO */
dwc_otg_common_rx_ack(sc);
return (0); /* complete */
}
if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
GRXSTSRD_DPID_DATA0) {
/* release FIFO */
dwc_otg_common_rx_ack(sc);
goto not_complete;
@ -973,14 +994,6 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
/* get the packet byte count */
count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
/* verify data length */
if (count != td->remainder) {
DPRINTFN(0, "Invalid SETUP packet "
"length, %d bytes\n", count);
/* release FIFO */
dwc_otg_common_rx_ack(sc);
goto not_complete;
}
if (count != sizeof(req)) {
DPRINTFN(0, "Unsupported SETUP packet "
"length, %d bytes\n", count);
@ -1006,43 +1019,27 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
}
/* don't send any data by default */
DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0),
DXEPTSIZ_SET_NPKT(0) |
DXEPTSIZ_SET_NBYTES(0));
temp = sc->sc_in_ctl[0];
/* enable IN endpoint */
DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
temp | DIEPCTL_EPENA);
DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
temp | DIEPCTL_SNAK);
DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0), DIEPCTL_EPDIS);
DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DOEPCTL_EPDIS);
/* reset IN endpoint buffer */
DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
dwc_otg_tx_fifo_reset(sc,
GRSTCTL_TXFIFO(0) |
GRSTCTL_TXFFLSH);
/* acknowledge RX status */
dwc_otg_common_rx_ack(sc);
return (0); /* complete */
td->did_stall = 1;
not_complete:
/* abort any ongoing transfer, before enabling again */
temp = sc->sc_out_ctl[0];
temp |= DOEPCTL_EPENA |
DOEPCTL_SNAK;
/* enable OUT endpoint */
DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0), temp);
if (!td->did_stall) {
td->did_stall = 1;
DPRINTFN(5, "stalling IN and OUT direction\n");
temp = sc->sc_out_ctl[0];
/* set stall after enabling endpoint */
DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0),
temp | DOEPCTL_STALL);
@ -1053,13 +1050,6 @@ not_complete:
DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
temp | DIEPCTL_STALL);
}
/* setup number of buffers to receive */
DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
DXEPTSIZ_SET_MULTI(3) |
DXEPTSIZ_SET_NPKT(1) |
DXEPTSIZ_SET_NBYTES(sizeof(req)));
return (1); /* not complete */
}
@ -1503,7 +1493,9 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
/* check for SETUP packet */
if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
GRXSTSRD_STP_DATA) {
GRXSTSRD_STP_DATA ||
(sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
GRXSTSRD_STP_COMPLETE) {
if (td->remainder == 0) {
/*
* We are actually complete and have
@ -1588,15 +1580,10 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
not_complete:
temp = sc->sc_out_ctl[td->ep_no];
temp |= DOEPCTL_EPENA | DOEPCTL_CNAK;
DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp);
/* enable SETUP and transfer complete interrupt */
if (td->ep_no == 0) {
DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
DXEPTSIZ_SET_MULTI(3) |
DXEPTSIZ_SET_NPKT(1) |
DXEPTSIZ_SET_NBYTES(td->max_packet_size));
} else {
@ -1605,8 +1592,12 @@ not_complete:
DXEPTSIZ_SET_MULTI(1) |
DXEPTSIZ_SET_NPKT(4) |
DXEPTSIZ_SET_NBYTES(4 *
((td->max_packet_size + 3) & ~3)));
((td->max_packet_size + 3) & ~3)));
}
temp = sc->sc_out_ctl[td->ep_no];
DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp |
DOEPCTL_EPENA | DOEPCTL_CNAK);
return (1); /* not complete */
}
@ -2008,7 +1999,9 @@ repeat:
(GRXSTSRD_CHNUM_GET(temp) == 0)) {
if ((temp & GRXSTSRD_PKTSTS_MASK) !=
GRXSTSRD_STP_DATA) {
GRXSTSRD_STP_DATA &&
(temp & GRXSTSRD_PKTSTS_MASK) !=
GRXSTSRD_STP_COMPLETE) {
/* dump data - wrong direction */
dwc_otg_common_rx_ack(sc);
@ -2213,7 +2206,9 @@ not_complete:
(GRXSTSRD_CHNUM_GET(temp) == 0)) {
if ((temp & GRXSTSRD_PKTSTS_MASK) ==
GRXSTSRD_STP_DATA) {
GRXSTSRD_STP_DATA ||
(temp & GRXSTSRD_PKTSTS_MASK) ==
GRXSTSRD_STP_COMPLETE) {
DPRINTFN(5, "faking complete\n");
/*
* Race condition: We are complete!
@ -2638,6 +2633,7 @@ repeat:
/* non-data messages we simply skip */
if (temp != GRXSTSRD_STP_DATA &&
temp != GRXSTSRD_STP_COMPLETE &&
temp != GRXSTSRD_OUT_DATA) {
dwc_otg_common_rx_ack(sc);
goto repeat;
@ -3668,7 +3664,7 @@ dwc_otg_clear_stall_sub_locked(struct dwc_otg_softc *sc, uint32_t mps,
/* we only reset the transmit FIFO */
if (ep_dir) {
DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
dwc_otg_tx_fifo_reset(sc,
GRSTCTL_TXFIFO(ep_no) |
GRSTCTL_TXFFLSH);