diff options
author | hselasky <hselasky@FreeBSD.org> | 2012-09-11 22:08:19 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2012-09-11 22:08:19 +0000 |
commit | dec93a046b551b1afbca5b1f6744bdbe589640a4 (patch) | |
tree | 7dbde2d21f6178b997df7fa9adb6e2e66d4b4ea0 /sys/dev/usb/controller/dwc_otg.c | |
parent | de36ad45b6ebd9f0826681e46179e93b08a3b6ab (diff) | |
download | FreeBSD-src-dec93a046b551b1afbca5b1f6744bdbe589640a4.zip FreeBSD-src-dec93a046b551b1afbca5b1f6744bdbe589640a4.tar.gz |
Fix missing parts of DWC OTG host mode support. The host mode support
of the DWC OTG is very simple in PIO mode, and we need to re-transmit
data when NAK is received among other things. We probably will need
to implement some kind of rate limitation on the NAK-ing.
Diffstat (limited to 'sys/dev/usb/controller/dwc_otg.c')
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.c | 356 |
1 files changed, 164 insertions, 192 deletions
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c index a2b7438..294e983 100644 --- a/sys/dev/usb/controller/dwc_otg.c +++ b/sys/dev/usb/controller/dwc_otg.c @@ -127,7 +127,6 @@ static dwc_otg_cmd_t dwc_otg_data_tx_sync; static dwc_otg_cmd_t dwc_otg_host_setup_tx; static dwc_otg_cmd_t dwc_otg_host_data_tx; static dwc_otg_cmd_t dwc_otg_host_data_rx; -static dwc_otg_cmd_t dwc_otg_host_data_tx_sync; static void dwc_otg_device_done(struct usb_xfer *, usb_error_t); static void dwc_otg_do_poll(struct usb_bus *); @@ -505,8 +504,9 @@ dwc_otg_host_channel_alloc(struct dwc_otg_td *td) /* enable interrupts */ DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_STALL | HCINT_BBLERR | - HCINT_AHBERR | HCINT_CHHLTD | HCINT_XACTERR | - HCINT_XFERCOMPL); + HCINT_AHBERR | HCINT_CHHLTD | + HCINT_XACTERR | HCINT_XFERCOMPL | + HCINT_NAK | HCINT_NYET); DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), td->hcsplt); DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0); @@ -544,6 +544,42 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td) /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); + temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel)); + DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp); + + DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", + td->channel, + temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), + DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + + if (temp & HCINT_NAK) + td->did_nak = 1; + + if (temp & HCINT_STALL) { + td->error_stall = 1; + td->error_any = 1; + return (0); /* complete */ + } + + if (temp & (HCINT_BBLERR | + HCINT_AHBERR | HCINT_CHHLTD | HCINT_XACTERR)) { + td->error_any = 1; + return (0); /* complete */ + } + + if (temp & HCINT_XFERCOMPL) + td->did_complete = 1; + + if (td->did_complete) { + if (td->did_nak == 0) { + td->offset += td->tx_bytes; + td->remainder -= td->tx_bytes; + td->toggle = 1; + return (0); /* complete */ + } + } else { + return (1); /* busy */ + } temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS); DPRINTF("HPTXSTS=0x%08x\n", temp); @@ -563,9 +599,6 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td) usbd_copy_out(td->pc, 0, &req, sizeof(req)); - td->offset = sizeof(req); - td->remainder = 0; - DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) | (1 << HCTSIZ_PKTCNT_SHIFT) | @@ -581,13 +614,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td) bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4); - td->toggle = 1; - - /* need to sync before complete */ - td->func = &dwc_otg_host_data_tx_sync; + /* reset statemachine */ + td->did_complete = 0; + td->did_nak = 0; + td->tx_bytes = sizeof(req); - /* check status */ - return (dwc_otg_host_data_tx_sync(td)); + return (1); /* busy */ } static uint8_t @@ -728,13 +760,11 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td) uint32_t temp; uint16_t count; uint8_t got_short; - uint8_t is_isoc; + uint8_t ep_type; if (dwc_otg_host_channel_alloc(td)) return (1); /* busy */ - got_short = 0; - /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); @@ -746,6 +776,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td) temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + if (temp & HCINT_NAK) + td->did_nak = 1; + if (temp & HCINT_STALL) { td->error_stall = 1; td->error_any = 1; @@ -758,6 +791,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td) return (0); /* complete */ } + if (temp & HCINT_XFERCOMPL) + td->did_complete = 1; + /* check endpoint status */ if (sc->sc_last_rx_status == 0) goto not_complete; @@ -770,92 +806,74 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td) DPRINTF("DATA\n"); - td->npkt = 0; + td->toggle ^= 1; - if ((sc->sc_last_rx_status & - GRXSTSRD_DPID_MASK) == GRXSTSRD_DPID_DATA0) { - if (td->toggle == 1) { - /* release FIFO - wrong toggle */ - DPRINTF("Wrong DT\n"); - dwc_otg_common_rx_ack(sc); - goto not_complete; - } - td->toggle = 1; - } else { - if (td->toggle == 0) { - /* release FIFO - wrong toggle */ - DPRINTF("Wrong DT\n"); + got_short = 0; + + /* get the packet byte count */ + count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); + + /* verify the packet byte count */ + if (count != td->max_packet_size) { + if (count < td->max_packet_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error_any = 1; + + /* release FIFO */ dwc_otg_common_rx_ack(sc); - goto not_complete; + return (0); /* we are complete */ } - td->toggle = 0; } - break; - - default: - DPRINTF("OTHER\n"); - /* release FIFO */ - dwc_otg_common_rx_ack(sc); - goto not_complete; - } - - /* get the packet byte count */ - count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status); - - /* verify the packet byte count */ - if (count != td->max_packet_size) { - if (count < td->max_packet_size) { - /* we have a short packet */ - td->short_pkt = 1; - got_short = 1; - } else { + /* verify the packet byte count */ + if (count > td->remainder) { /* invalid USB packet */ td->error_any = 1; /* release FIFO */ dwc_otg_common_rx_ack(sc); - return (0); /* we are complete */ + return (0); /* we are complete */ } - } - /* verify the packet byte count */ - if (count > td->remainder) { - /* invalid USB packet */ - td->error_any = 1; + usbd_copy_in(td->pc, td->offset, sc->sc_rx_bounce_buffer, count); + td->remainder -= count; + td->offset += count; /* release FIFO */ dwc_otg_common_rx_ack(sc); - return (0); /* we are complete */ - } - usbd_copy_in(td->pc, td->offset, sc->sc_rx_bounce_buffer, count); - td->remainder -= count; - td->offset += count; + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + return (0); + } + /* else need to receive a zero length packet */ + } + break; - /* release FIFO */ - dwc_otg_common_rx_ack(sc); + default: + DPRINTF("OTHER\n"); - /* check if we are complete */ - if ((td->remainder == 0) || got_short) { - if (td->short_pkt) { - /* we are complete */ - return (0); - } - /* else need to receive a zero length packet */ + /* release FIFO */ + dwc_otg_common_rx_ack(sc); + break; } not_complete: - - if (td->npkt != 0) + if (td->did_complete == 0 && td->did_nak == 0) return (1); /* busy */ temp = sc->sc_hcchar[td->channel]; - is_isoc = (((temp & HCCHAR_EPTYPE_MASK) >> - HCCHAR_EPTYPE_SHIFT) == UE_ISOCHRONOUS); + ep_type = ((temp & HCCHAR_EPTYPE_MASK) >> + HCCHAR_EPTYPE_SHIFT); - if (is_isoc != 0) { + if (ep_type == UE_ISOCHRONOUS) { if ((sc->sc_sof_val & 0xFF) != td->sof_val) return (1); /* busy */ if (td->sof_val & 1) @@ -863,17 +881,19 @@ not_complete: td->sof_val += td->sof_res; /* DATA 0 */ td->toggle = 0; + } else if (ep_type == UE_INTERRUPT) { + if ((sc->sc_interrupt_val & 0xFF) != td->sof_val) + return (1); /* busy */ + td->sof_val += td->sof_res; } else if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } - /* receive one packet at a time */ - td->npkt = 1; - + /* receive one packet */ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) | - (td->npkt << HCTSIZ_PKTCNT_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); @@ -882,6 +902,10 @@ not_complete: /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp); + /* reset statemachine */ + td->did_complete = 0; + td->did_nak = 0; + return (1); /* not complete */ } @@ -1004,9 +1028,8 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) struct dwc_otg_softc *sc; uint32_t max_buffer; uint32_t count; - uint32_t mpkt; uint32_t temp; - uint8_t is_isoc; + uint8_t ep_type; uint8_t max_frames; if (dwc_otg_host_channel_alloc(td)) @@ -1023,6 +1046,9 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); + if (temp & HCINT_NAK) + td->did_nak = 1; + if (temp & HCINT_STALL) { td->error_stall = 1; td->error_any = 1; @@ -1035,12 +1061,33 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) return (0); /* complete */ } + if (temp & HCINT_XFERCOMPL) + td->did_complete = 1; + + if (td->did_complete) { + if (td->did_nak == 0) { + td->offset += td->tx_bytes; + td->remainder -= td->tx_bytes; + td->toggle ^= 1; + td->did_nak = 1; + + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) + return (0); /* complete */ + + /* else we need to transmit a short packet */ + } + } + } else { + return (1); /* busy */ + } temp = sc->sc_hcchar[td->channel]; - is_isoc = (((temp & HCCHAR_EPTYPE_MASK) >> - HCCHAR_EPTYPE_SHIFT) == UE_ISOCHRONOUS); + ep_type = ((temp & HCCHAR_EPTYPE_MASK) >> + HCCHAR_EPTYPE_SHIFT); - if (is_isoc != 0) { + if (ep_type == UE_ISOCHRONOUS) { if ((sc->sc_sof_val & 0xFF) != td->sof_val) return (1); /* busy */ if (td->sof_val & 1) @@ -1050,20 +1097,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) td->sof_val += td->sof_res; td->toggle = 0; + } else if (ep_type == UE_INTERRUPT) { + if ((sc->sc_interrupt_val & 0xFF) != td->sof_val) + return (1); /* busy */ + td->sof_val += td->sof_res; } else if (td->set_toggle) { td->set_toggle = 0; td->toggle = 1; } - /* check if all packets have been transferred */ - temp = DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)); - if (temp & HCTSIZ_PKTCNT_MASK) { - DPRINTFN(5, "busy ch=%d npkt=%d HCTSIZ=0x%08x\n", - td->channel, (temp & HCTSIZ_PKTCNT_MASK) >> - HCTSIZ_PKTCNT_SHIFT, temp); - return (1); /* busy */ - } - temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS); max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK); @@ -1071,48 +1113,25 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) >> HPTXSTS_PTXQSPCAVAIL_SHIFT; max_buffer = max_buffer - (max_buffer % td->max_packet_size); - if (max_buffer == 0 || max_frames < 2) + if (max_buffer == 0 || max_frames == 0) return (1); /* busy */ - /* try to optimise by sending more data */ - if (td->channel != 0 && - (td->max_packet_size & 3) == 0 && is_isoc == 0) { - + /* send one packet at a time */ + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; count = td->remainder; - if (count > max_buffer) - count = max_buffer; - - mpkt = count / td->max_packet_size; - - if (mpkt > max_frames) { - mpkt = max_frames; - count = td->max_packet_size * mpkt; - } else if ((count == 0) || (count % td->max_packet_size)) { - /* we are transmitting a short packet */ - mpkt++; - td->short_pkt = 1; - } - } else { - /* send one packet at a time */ - mpkt = 1; - count = td->max_packet_size; - if (td->remainder < count) { - /* we have a short packet */ - td->short_pkt = 1; - count = td->remainder; - } } + /* TODO: HCTSIZ_DOPNG */ + DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel), (count << HCTSIZ_XFERSIZE_SHIFT) | - (mpkt << HCTSIZ_PKTCNT_SHIFT) | + (1 << HCTSIZ_PKTCNT_SHIFT) | (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) : (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT))); - td->toggle ^= (mpkt & 1); - - /* TODO: HCTSIZ_DOPNG */ - temp = sc->sc_hcchar[td->channel]; temp &= ~HCCHAR_EPDIR_IN; @@ -1132,19 +1151,14 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td) bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl, DOTG_DFIFO(td->channel), sc->sc_tx_bounce_buffer, (count + 3) / 4); - - td->remainder -= count; - td->offset += count; } - /* check remainder */ - if (td->remainder == 0) { - if (td->short_pkt) - return (0); /* complete */ + /* reset statemachine */ + td->did_complete = 0; + td->did_nak = 0; + td->tx_bytes = count; - /* else we need to transmit a short packet */ - } - return (1); /* not complete */ + return (1); /* busy */ } static uint8_t @@ -1340,44 +1354,6 @@ not_complete: } static uint8_t -dwc_otg_host_data_tx_sync(struct dwc_otg_td *td) -{ - struct dwc_otg_softc *sc; - uint32_t temp; - - if (dwc_otg_host_channel_alloc(td)) - return (1); /* busy */ - - /* get pointer to softc */ - sc = DWC_OTG_PC2SC(td->pc); - - temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel)); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp); - - DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n", - td->channel, - temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)), - DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel))); - - if (temp & HCINT_STALL) { - td->error_stall = 1; - td->error_any = 1; - return (0); /* complete */ - } - - if (temp & (HCINT_BBLERR | - HCINT_AHBERR | HCINT_CHHLTD | HCINT_XACTERR)) { - td->error_any = 1; - return (0); /* complete */ - } - - if (temp & HCINT_XFERCOMPL) - return (0); /* complete */ - - return (1); /* busy */ -} - -static uint8_t dwc_otg_data_tx_sync(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; @@ -1842,6 +1818,8 @@ dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp) td->error_any = 0; td->npkt = 0; td->did_stall = temp->did_stall; + td->did_nak = 1; + td->did_complete = 1; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; td->set_toggle = 0; @@ -1918,7 +1896,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) } else { if (is_host) { temp.func = &dwc_otg_host_data_tx; - need_sync = 1; + need_sync = 0; } else { temp.func = &dwc_otg_data_rx; need_sync = 0; @@ -1980,13 +1958,8 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) /* check if we need to sync */ if (need_sync) { - if (is_host) { - /* we need a SYNC point after TX */ - temp.func = &dwc_otg_host_data_tx_sync; - } else { - /* we need a SYNC point after TX */ - temp.func = &dwc_otg_data_tx_sync; - } + /* we need a SYNC point after TX */ + temp.func = &dwc_otg_data_tx_sync; dwc_otg_setup_standard_chain_sub(&temp); } @@ -2000,7 +1973,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) if (xfer->endpointno & UE_DIR_IN) { if (is_host) { temp.func = &dwc_otg_host_data_tx; - need_sync = 1; + need_sync = 0; } else { temp.func = &dwc_otg_data_rx; need_sync = 0; @@ -2023,10 +1996,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) if (need_sync) { /* we need a SYNC point after TX */ - if (is_host) - temp.func = &dwc_otg_host_data_tx_sync; - else - temp.func = &dwc_otg_data_tx_sync; + temp.func = &dwc_otg_data_tx_sync; dwc_otg_setup_standard_chain_sub(&temp); } } @@ -2040,10 +2010,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) temp.setup_alt_next = 0; /* we need a SYNC point after TX */ - if (is_host) - temp.func = &dwc_otg_host_data_tx_sync; - else - temp.func = &dwc_otg_data_tx_sync; + temp.func = &dwc_otg_data_tx_sync; dwc_otg_setup_standard_chain_sub(&temp); } } @@ -2105,8 +2072,13 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer) break; } - td->sof_val = xfer->endpoint->isoc_next & 0xFF; - td->sof_res = 1 << usbd_xfer_get_fps_shift(xfer); + if (xfer_type == UE_ISOCHRONOUS) { + td->sof_val = xfer->endpoint->isoc_next & 0xFF; + td->sof_res = 1 << usbd_xfer_get_fps_shift(xfer); + } else if (xfer_type == UE_INTERRUPT) { + td->sof_val = sc->sc_interrupt_val; + td->sof_res = 0; /* TODO */ + } } } |