Date: Fri, 16 May 2014 15:41:55 +0000 (UTC) From: Hans Petter Selasky <hselasky@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r266241 - head/sys/dev/usb/controller Message-ID: <201405161541.s4GFfteG009737@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Fri May 16 15:41:55 2014 New Revision: 266241 URL: http://svnweb.freebsd.org/changeset/base/266241 Log: Implement basic support for the USB host controller found in the SAF1761 chip, supporting BULK and CONTROL endpoints. This code is not yet tested. Sponsored by: DARPA, AFRL Modified: head/sys/dev/usb/controller/saf1761_dci.c head/sys/dev/usb/controller/saf1761_dci.h head/sys/dev/usb/controller/saf1761_dci_reg.h Modified: head/sys/dev/usb/controller/saf1761_dci.c ============================================================================== --- head/sys/dev/usb/controller/saf1761_dci.c Fri May 16 15:39:11 2014 (r266240) +++ head/sys/dev/usb/controller/saf1761_dci.c Fri May 16 15:41:55 2014 (r266241) @@ -103,13 +103,20 @@ SYSCTL_INT(_hw_usb_saf1761_dci, OID_AUTO /* prototypes */ static const struct usb_bus_methods saf1761_dci_bus_methods; -static const struct usb_pipe_methods saf1761_dci_device_non_isoc_methods; +static const struct usb_pipe_methods saf1761_otg_non_isoc_methods; static const struct usb_pipe_methods saf1761_dci_device_isoc_methods; -static saf1761_dci_cmd_t saf1761_dci_setup_rx; -static saf1761_dci_cmd_t saf1761_dci_data_rx; -static saf1761_dci_cmd_t saf1761_dci_data_tx; -static saf1761_dci_cmd_t saf1761_dci_data_tx_sync; +static saf1761_dci_cmd_t saf1761_host_setup_tx; +static saf1761_dci_cmd_t saf1761_host_bulk_data_rx; +static saf1761_dci_cmd_t saf1761_host_bulk_data_tx; +static saf1761_dci_cmd_t saf1761_host_intr_data_rx; +static saf1761_dci_cmd_t saf1761_host_intr_data_tx; +static saf1761_dci_cmd_t saf1761_host_isoc_data_rx; +static saf1761_dci_cmd_t saf1761_host_isoc_data_tx; +static saf1761_dci_cmd_t saf1761_device_setup_rx; +static saf1761_dci_cmd_t saf1761_device_data_rx; +static saf1761_dci_cmd_t saf1761_device_data_tx; +static saf1761_dci_cmd_t saf1761_device_data_tx_sync; static void saf1761_dci_device_done(struct usb_xfer *, usb_error_t); static void saf1761_dci_do_poll(struct usb_bus *); static void saf1761_dci_standard_done(struct usb_xfer *); @@ -197,6 +204,357 @@ saf1761_dci_wakeup_peer(struct saf1761_d } +static uint8_t +saf1761_host_channel_alloc(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + uint32_t x; + + if (td->channel < SOTG_HOST_CHANNEL_MAX) + return (0); + + switch (td->ep_type) { + case UE_INTERRUPT: + for (x = 0; x != 32; x++) { + if (sc->sc_host_intr_map & (1 << x)) + continue; + sc->sc_host_intr_map |= (1 << x); + td->channel = 32 + x; + return (0); + } + break; + case UE_ISOCHRONOUS: + for (x = 0; x != 32; x++) { + if (sc->sc_host_isoc_map & (1 << x)) + continue; + sc->sc_host_isoc_map |= (1 << x); + td->channel = 64 + x; + return (0); + } + break; + default: + for (x = 0; x != 32; x++) { + if (sc->sc_host_async_map & (1 << x)) + continue; + sc->sc_host_async_map |= (1 << x); + td->channel = x; + return (0); + } + break; + } + return (1); +} + +static void +saf1761_host_channel_free(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + uint32_t x; + + if (td->channel >= SOTG_HOST_CHANNEL_MAX) + return; + + /* disable channel */ + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 0), 0); + + switch (td->ep_type) { + case UE_INTERRUPT: + x = td->channel - 32; + sc->sc_host_intr_map &= ~(1 << x); + td->channel = SOTG_HOST_CHANNEL_MAX; + break; + case UE_ISOCHRONOUS: + x = td->channel - 64; + sc->sc_host_isoc_map &= ~(1 << x); + td->channel = SOTG_HOST_CHANNEL_MAX; + break; + default: + x = td->channel - 64; + sc->sc_host_async_map &= ~(1 << x); + td->channel = SOTG_HOST_CHANNEL_MAX; + break; + } +} + +static void +saf1761_read_host_fifo_1(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td, + void *buf, uint32_t len) +{ + bus_space_read_region_1((sc)->sc_io_tag, (sc)->sc_io_hdl, + SOTG_DATA_ADDR(td->channel), buf, len); +} + +static void +saf1761_write_host_fifo_1(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td, + void *buf, uint32_t len) +{ + bus_space_write_region_1((sc)->sc_io_tag, (sc)->sc_io_hdl, + SOTG_DATA_ADDR(td->channel), buf, len); +} + +static uint8_t +saf1761_host_setup_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + struct usb_device_request req __aligned(4); + uint32_t status; + uint32_t count; + + if (td->channel < SOTG_HOST_CHANNEL_MAX) { + status = SAF1761_READ_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3)); + if (status & (1 << 31)) { + goto busy; + } else if (status & (1 << 30)) { + td->error_stall = 1; + td->error_any = 1; + } else if (status & (3 << 28)) { + td->error_any = 1; + } + count = (status & 0x7FFF); + + saf1761_host_channel_free(sc, td); + goto complete; + } + if (saf1761_host_channel_alloc(sc, td)) + goto busy; + + if (sizeof(req) != td->remainder) { + td->error_any = 1; + goto complete; + } + + count = sizeof(req); + + usbd_copy_out(td->pc, 0, &req, count); + + saf1761_write_host_fifo_1(sc, td, &req, count); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 7), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 6), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 5), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 4), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3), + (1 << 31) | (td->toggle << 25) | (3 << 23)); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 2), + SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 1), + td->dw1_value | + (2 << 10) /* SETUP PID */ | + (td->ep_index >> 1)); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 0), + (td->ep_index << 31) | + (1 << 29) /* pkt-multiplier */ | + (td->max_packet_size << 18) /* wMaxPacketSize */ | + (count << 3) /* transfer count */ | + 1 /* valid */); + + td->offset += count; + td->remainder -= count; + td->toggle = 1; +busy: + return (1); /* busy */ +complete: + return (0); /* complete */ +} + +static uint8_t +saf1761_host_bulk_data_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + if (td->channel < SOTG_HOST_CHANNEL_MAX) { + uint32_t status; + uint32_t count; + uint8_t got_short; + + status = SAF1761_READ_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3)); + + if (status & (1 << 31)) { + goto busy; + } else if (status & (1 << 30)) { + td->error_stall = 1; + td->error_any = 1; + goto complete; + } else if (status & (3 << 28)) { + td->error_any = 1; + goto complete; + } + count = (status & 0x7FFF); + got_short = 0; + + /* 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; + goto complete; + } + } + td->toggle ^= 1; + + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error_any = 1; + goto complete; + } + + saf1761_read_host_fifo_1(sc, td, + sc->sc_bounce_buffer, count); + usbd_copy_in(td->pc, td->offset, + sc->sc_bounce_buffer, count); + + td->remainder -= count; + td->offset += count; + + saf1761_host_channel_free(sc, td); + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) + goto complete; + /* else need to receive a zero length packet */ + } + } + if (saf1761_host_channel_alloc(sc, td)) + goto busy; + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + + /* receive one more packet */ + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 7), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 6), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 5), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 4), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3), + (1 << 31) | (td->toggle << 25) | (3 << 23)); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 2), + SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 1), + td->dw1_value | + (1 << 10) /* IN-PID */ | + (td->ep_index >> 1)); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 0), + (td->ep_index << 31) | + (1 << 29) /* pkt-multiplier */ | + (td->max_packet_size << 18) /* wMaxPacketSize */ | + (td->max_packet_size << 3) /* transfer count */ | + 1 /* valid */); +busy: + return (1); /* busy */ +complete: + return (0); /* complete */ +} + +static uint8_t +saf1761_host_bulk_data_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + uint32_t count; + + if (td->channel < SOTG_HOST_CHANNEL_MAX) { + uint32_t status; + + status = SAF1761_READ_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3)); + if (status & (1 << 31)) { + goto busy; + } else if (status & (1 << 30)) { + td->error_stall = 1; + td->error_any = 1; + } else if (status & (3 << 28)) { + td->error_any = 1; + } + + saf1761_host_channel_free(sc, td); + + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) + goto complete; + /* else we need to transmit a short packet */ + } + } + if (saf1761_host_channel_alloc(sc, td)) + goto busy; + + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + + usbd_copy_out(td->pc, td->offset, sc->sc_bounce_buffer, count); + saf1761_write_host_fifo_1(sc, td, sc->sc_bounce_buffer, count); + + /* set toggle, if any */ + if (td->set_toggle) { + td->set_toggle = 0; + td->toggle = 1; + } + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 7), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 6), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 5), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 4), 0); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 3), + (1 << 31) | (td->toggle << 25) | (3 << 23)); + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 2), + SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 1), + td->dw1_value | + (0 << 10) /* OUT-PID */ | + (td->ep_index >> 1)); + + SAF1761_WRITE_4(sc, SOTG_ASYNC_PDT(td->channel) + (4 * 0), + (td->ep_index << 31) | + (1 << 29) /* pkt-multiplier */ | + (td->max_packet_size << 18) /* wMaxPacketSize */ | + (count << 3) /* transfer count */ | + 1 /* valid */); + + td->offset += count; + td->remainder -= count; + td->toggle ^= 1; +busy: + return (1); /* busy */ +complete: + return (0); /* complete */ +} + +static uint8_t +saf1761_host_intr_data_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + return (1); /* busy */ +} + +static uint8_t +saf1761_host_intr_data_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + return (1); /* busy */ +} + +static uint8_t +saf1761_host_isoc_data_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + return (1); /* busy */ +} + +static uint8_t +saf1761_host_isoc_data_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +{ + return (1); /* busy */ +} + static void saf1761_dci_set_address(struct saf1761_dci_softc *sc, uint8_t addr) { @@ -206,21 +564,21 @@ saf1761_dci_set_address(struct saf1761_d } static void -saf1761_read_fifo(struct saf1761_dci_softc *sc, void *buf, uint32_t len) +saf1761_read_device_fifo_1(struct saf1761_dci_softc *sc, void *buf, uint32_t len) { bus_space_read_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, SOTG_DATA_PORT, buf, len); } static void -saf1761_write_fifo(struct saf1761_dci_softc *sc, void *buf, uint32_t len) +saf1761_write_device_fifo_1(struct saf1761_dci_softc *sc, void *buf, uint32_t len) { bus_space_write_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, SOTG_DATA_PORT, buf, len); } static uint8_t -saf1761_dci_setup_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +saf1761_device_setup_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) { struct usb_device_request req; uint16_t count; @@ -256,7 +614,7 @@ saf1761_dci_setup_rx(struct saf1761_dci_ goto busy; } /* receive data */ - saf1761_read_fifo(sc, &req, sizeof(req)); + saf1761_read_device_fifo_1(sc, &req, sizeof(req)); /* copy data into real buffer */ usbd_copy_in(td->pc, 0, &req, sizeof(req)); @@ -288,7 +646,7 @@ busy: } static uint8_t -saf1761_dci_data_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +saf1761_device_data_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) { struct usb_page_search buf_res; uint16_t count; @@ -314,7 +672,7 @@ saf1761_dci_data_rx(struct saf1761_dci_s /* * USB Host Aborted the transfer. */ - td->error = 1; + td->error_any = 1; return (0); /* complete */ } } @@ -323,6 +681,12 @@ saf1761_dci_data_rx(struct saf1761_dci_s (td->ep_index << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) | SOTG_EP_INDEX_DIR_OUT); + /* enable data stage */ + if (td->set_toggle) { + td->set_toggle = 0; + SAF1761_WRITE_1(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_DSEN); + } + /* check buffer status */ if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) & SOTG_DCBUFFERSTATUS_FILLED_MASK) == 0) { @@ -341,14 +705,14 @@ saf1761_dci_data_rx(struct saf1761_dci_s got_short = 1; } else { /* invalid USB packet */ - td->error = 1; + td->error_any = 1; return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ - td->error = 1; + td->error_any = 1; return (0); /* we are complete */ } while (count > 0) { @@ -359,7 +723,7 @@ saf1761_dci_data_rx(struct saf1761_dci_s buf_res.length = count; /* receive data */ - saf1761_read_fifo(sc, buf_res.buffer, buf_res.length); + saf1761_read_device_fifo_1(sc, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; @@ -378,7 +742,7 @@ saf1761_dci_data_rx(struct saf1761_dci_s } static uint8_t -saf1761_dci_data_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +saf1761_device_data_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) { struct usb_page_search buf_res; uint16_t count; @@ -395,7 +759,7 @@ saf1761_dci_data_tx(struct saf1761_dci_s /* * USB Host Aborted the transfer. */ - td->error = 1; + td->error_any = 1; return (0); /* complete */ } } @@ -409,6 +773,13 @@ saf1761_dci_data_tx(struct saf1761_dci_s SOTG_DCBUFFERSTATUS_FILLED_MASK) != 0) { return (1); /* not complete */ } + + /* enable data stage */ + if (td->set_toggle) { + td->set_toggle = 0; + SAF1761_WRITE_1(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_DSEN); + } + DPRINTFN(5, "rem=%u\n", td->remainder); count = td->max_packet_size; @@ -428,7 +799,7 @@ saf1761_dci_data_tx(struct saf1761_dci_s buf_res.length = count; /* transmit data */ - saf1761_write_fifo(sc, buf_res.buffer, buf_res.length); + saf1761_write_device_fifo_1(sc, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; @@ -459,7 +830,7 @@ saf1761_dci_data_tx(struct saf1761_dci_s } static uint8_t -saf1761_dci_data_tx_sync(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) +saf1761_device_data_tx_sync(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td) { if (td->ep_index == 0) { /* select the correct endpoint */ @@ -493,6 +864,7 @@ static uint8_t saf1761_dci_xfer_do_fifo(struct saf1761_dci_softc *sc, struct usb_xfer *xfer) { struct saf1761_dci_td *td; + uint8_t toggle; DPRINTFN(9, "\n"); @@ -505,7 +877,7 @@ saf1761_dci_xfer_do_fifo(struct saf1761_ if (((void *)td) == xfer->td_transfer_last) { goto done; } - if (td->error) { + if (td->error_any) { goto done; } else if (td->remainder > 0) { /* @@ -519,7 +891,9 @@ saf1761_dci_xfer_do_fifo(struct saf1761_ /* * Fetch the next transfer descriptor. */ + toggle = td->toggle; td = td->obj_next; + td->toggle = toggle; xfer->td_transfer_cache = td; } return (1); /* not complete */ @@ -683,10 +1057,13 @@ saf1761_dci_setup_standard_chain_sub(str td->pc = temp->pc; td->offset = temp->offset; td->remainder = temp->len; - td->error = 0; + td->error_any = 0; + td->error_stall = 0; + td->set_toggle = 0; td->did_stall = temp->did_stall; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; + td->channel = SOTG_HOST_CHANNEL_MAX; } static void @@ -697,6 +1074,9 @@ saf1761_dci_setup_standard_chain(struct struct saf1761_dci_td *td; uint32_t x; uint8_t ep_no; + uint8_t ep_type; + uint8_t need_sync; + uint8_t is_host; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), @@ -717,15 +1097,22 @@ saf1761_dci_setup_standard_chain(struct temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.did_stall = !xfer->flags_int.control_stall; + is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST); + sc = SAF1761_DCI_BUS2SC(xfer->xroot->bus); ep_no = (xfer->endpointno & UE_ADDR); + ep_type = (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE); /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { - temp.func = &saf1761_dci_setup_rx; + if (is_host) + temp.func = &saf1761_host_setup_tx; + else + temp.func = &saf1761_device_setup_rx; + temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; @@ -744,14 +1131,39 @@ saf1761_dci_setup_standard_chain(struct if (x != xfer->nframes) { if (xfer->endpointno & UE_DIR_IN) { - temp.func = &saf1761_dci_data_tx; + if (is_host) { + if (ep_type == UE_INTERRUPT) + temp.func = &saf1761_host_intr_data_rx; + else if (ep_type == UE_ISOCHRONOUS) + temp.func = &saf1761_host_isoc_data_rx; + else + temp.func = &saf1761_host_bulk_data_rx; + need_sync = 0; + } else { + temp.func = &saf1761_device_data_tx; + need_sync = 1; + } } else { - temp.func = &saf1761_dci_data_rx; + if (is_host) { + if (ep_type == UE_INTERRUPT) + temp.func = &saf1761_host_intr_data_tx; + else if (ep_type == UE_ISOCHRONOUS) + temp.func = &saf1761_host_isoc_data_tx; + else + temp.func = &saf1761_host_bulk_data_tx; + need_sync = 0; + } else { + temp.func = &saf1761_device_data_rx; + need_sync = 0; + } } /* setup "pc" pointer */ temp.pc = xfer->frbuffers + x; + } else { + need_sync = 0; } + while (x != xfer->nframes) { /* DATA0 / DATA1 message */ @@ -794,8 +1206,6 @@ saf1761_dci_setup_standard_chain(struct /* check for control transfer */ if (xfer->flags_int.control_xfr) { - uint8_t need_sync; - /* always setup a valid "pc" pointer for status and sync */ temp.pc = xfer->frbuffers + 0; temp.len = 0; @@ -810,26 +1220,59 @@ saf1761_dci_setup_standard_chain(struct * endpoint direction. */ if (xfer->endpointno & UE_DIR_IN) { - temp.func = &saf1761_dci_data_rx; - need_sync = 0; + if (is_host) { + temp.func = &saf1761_host_bulk_data_tx; + need_sync = 0; + } else { + temp.func = &saf1761_device_data_rx; + need_sync = 0; + } } else { - temp.func = &saf1761_dci_data_tx; - need_sync = 1; + if (is_host) { + temp.func = &saf1761_host_bulk_data_rx; + need_sync = 0; + } else { + temp.func = &saf1761_device_data_tx; + need_sync = 1; + } } temp.len = 0; temp.short_pkt = 0; saf1761_dci_setup_standard_chain_sub(&temp); + + /* data toggle should be DATA1 */ + td = temp.td; + td->set_toggle = 1; + if (need_sync) { /* we need a SYNC point after TX */ - temp.func = &saf1761_dci_data_tx_sync; + temp.func = &saf1761_device_data_tx_sync; saf1761_dci_setup_standard_chain_sub(&temp); } } + } else { + if (need_sync) { + temp.pc = xfer->frbuffers + 0; + temp.len = 0; + temp.short_pkt = 0; + temp.setup_alt_next = 0; + + /* we need a SYNC point after TX */ + temp.func = &saf1761_device_data_tx_sync; + saf1761_dci_setup_standard_chain_sub(&temp); + } } + /* must have at least one frame! */ td = temp.td; xfer->td_transfer_last = td; + + if (is_host) { + /* get first again */ + td = xfer->td_transfer_first; + td->toggle = (xfer->endpoint->toggle_next ? 1 : 0); + } } static void @@ -919,7 +1362,7 @@ saf1761_dci_standard_done_sub(struct usb { struct saf1761_dci_td *td; uint32_t len; - uint8_t error; + usb_error_t error; DPRINTFN(9, "\n"); @@ -928,21 +1371,25 @@ saf1761_dci_standard_done_sub(struct usb do { len = td->remainder; + /* store last data toggle */ + xfer->endpoint->toggle_next = td->toggle; + if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract * the remainder from "frlengths[]": */ if (len > xfer->frlengths[xfer->aframes]) { - td->error = 1; + td->error_any = 1; } else { xfer->frlengths[xfer->aframes] -= len; } } /* Check for transfer error */ - if (td->error) { + if (td->error_any) { /* the transfer is finished */ - error = 1; + error = (td->error_stall ? + USB_ERR_STALLED : USB_ERR_IOERROR); td = NULL; break; } @@ -974,8 +1421,7 @@ saf1761_dci_standard_done_sub(struct usb xfer->td_transfer_cache = td; - return (error ? - USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); + return (error); } static void @@ -1030,13 +1476,23 @@ done: static void saf1761_dci_device_done(struct usb_xfer *xfer, usb_error_t error) { + struct saf1761_dci_softc *sc = SAF1761_DCI_BUS2SC(xfer->xroot->bus); + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); - if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { saf1761_dci_intr_set(xfer, 0); + } else { + struct saf1761_dci_td *td; + + td = xfer->td_transfer_first; + + if (td != NULL) + saf1761_host_channel_free(sc, td); + } /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); @@ -1059,6 +1515,12 @@ saf1761_dci_set_stall(struct usb_device USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); + /* check mode */ + if (udev->flags.usb_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + DPRINTFN(5, "endpoint=%p\n", ep); /* set FORCESTALL */ @@ -1138,7 +1600,7 @@ usb_error_t saf1761_dci_init(struct saf1761_dci_softc *sc) { const struct usb_hw_ep_profile *pf; - uint8_t x; + uint32_t x; DPRINTF("\n"); @@ -1161,13 +1623,26 @@ saf1761_dci_init(struct saf1761_dci_soft DPRINTF("DCID=0x%08x\n", SAF1761_READ_4(sc, SOTG_DCCHIP_ID)); - /* reset device */ + /* reset device controller */ SAF1761_WRITE_2(sc, SOTG_MODE, SOTG_MODE_SFRESET); SAF1761_WRITE_2(sc, SOTG_MODE, 0); /* wait a bit */ DELAY(1000); + /* reset host controller */ + SAF1761_WRITE_4(sc, SOTG_SW_RESET, SOTG_SW_RESET_HC); + SAF1761_WRITE_4(sc, SOTG_USBCMD, SOTG_USBCMD_HCRESET); + + /* wait a bit */ + DELAY(1000); + + SAF1761_WRITE_4(sc, SOTG_SW_RESET, 0); + SAF1761_WRITE_4(sc, SOTG_USBCMD, 0); + + /* wait a bit */ + DELAY(1000); + /* do a pulldown */ saf1761_dci_pull_down(sc); @@ -1225,6 +1700,22 @@ saf1761_dci_init(struct saf1761_dci_soft /* disable device address */ SAF1761_WRITE_1(sc, SOTG_ADDRESS, 0); + /* enable host controller clock */ + SAF1761_WRITE_4(sc, SOTG_POWER_DOWN, SOTG_POWER_DOWN_HC_CLK_EN); + + /* wait 10ms for clock */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); + + /* enable configuration flag */ + SAF1761_WRITE_4(sc, SOTG_CONFIGFLAG, SOTG_CONFIGFLAG_ENABLE); + + /* clear RAM block */ + for (x = 0x400; x != 0x10000; x += 4) + SAF1761_WRITE_4(sc, x, 0); + + /* start the HC */ + SAF1761_WRITE_4(sc, SOTG_USBCMD, SOTG_USBCMD_RS); + /* poll initial VBUS status */ saf1761_dci_update_vbus(sc); @@ -1309,7 +1800,7 @@ saf1761_dci_device_non_isoc_start(struct saf1761_dci_start_standard_chain(xfer); } -static const struct usb_pipe_methods saf1761_dci_device_non_isoc_methods = +static const struct usb_pipe_methods saf1761_otg_non_isoc_methods = { .open = saf1761_dci_device_non_isoc_open, .close = saf1761_dci_device_non_isoc_close, @@ -1465,7 +1956,7 @@ static const struct saf1761_dci_config_d static const struct usb_hub_descriptor_min saf1761_dci_hubd = { .bDescLength = sizeof(saf1761_dci_hubd), .bDescriptorType = UDESC_HUB, - .bNbrPorts = 1, + .bNbrPorts = SOTG_NUM_PORTS, HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)), .bPwrOn2PwrGood = 50, .bHubContrCurrent = 0, @@ -1490,6 +1981,8 @@ saf1761_dci_roothub_exec(struct usb_devi uint16_t len; uint16_t value; uint16_t index; + uint32_t temp; + uint32_t i; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); @@ -1620,9 +2113,19 @@ saf1761_dci_roothub_exec(struct usb_devi case UT_WRITE_CLASS_OTHER: switch (req->bRequest) { case UR_CLEAR_FEATURE: - goto tr_handle_clear_port_feature; + if (index == SOTG_HOST_PORT_NUM) + goto tr_handle_clear_port_feature_host; + else if (index == SOTG_DEVICE_PORT_NUM) + goto tr_handle_clear_port_feature_device; + else + goto tr_stalled; case UR_SET_FEATURE: - goto tr_handle_set_port_feature; + if (index == SOTG_HOST_PORT_NUM) + goto tr_handle_set_port_feature_host; + else if (index == SOTG_DEVICE_PORT_NUM) + goto tr_handle_set_port_feature_device; + else + goto tr_stalled; case UR_CLEAR_TT_BUFFER: case UR_RESET_TT: case UR_STOP_TT: @@ -1638,7 +2141,12 @@ saf1761_dci_roothub_exec(struct usb_devi case UR_GET_TT_STATE: goto tr_handle_get_tt_state; case UR_GET_STATUS: - goto tr_handle_get_port_status; + if (index == SOTG_HOST_PORT_NUM) + goto tr_handle_get_port_status_host; + else if (index == SOTG_DEVICE_PORT_NUM) + goto tr_handle_get_port_status_device; + else + goto tr_stalled; default: goto tr_stalled; } @@ -1748,10 +2256,8 @@ tr_handle_clear_wakeup: tr_handle_clear_halt: goto tr_valid; -tr_handle_clear_port_feature: - if (index != 1) - goto tr_stalled; - DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); +tr_handle_clear_port_feature_device: + DPRINTFN(9, "UR_CLEAR_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_SUSPEND: @@ -1785,10 +2291,52 @@ tr_handle_clear_port_feature: } goto tr_valid; -tr_handle_set_port_feature: - if (index != 1) - goto tr_stalled; - DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); +tr_handle_clear_port_feature_host: + DPRINTFN(9, "UR_CLEAR_FEATURE on port %d\n", index); + + temp = SAF1761_READ_4(sc, SOTG_PORTSC1); + + switch (value) { + case UHF_PORT_ENABLE: + SAF1761_WRITE_4(sc, SOTG_PORTSC1, temp & ~SOTG_PORTSC1_PED); + break; + case UHF_PORT_SUSPEND: + if ((temp & SOTG_PORTSC1_SUSP) && (!(temp & SOTG_PORTSC1_FPR))) + SAF1761_WRITE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_FPR); + + /* wait 20ms for resume sequence to complete */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); + + SAF1761_WRITE_4(sc, SOTG_PORTSC1, temp & ~(SOTG_PORTSC1_SUSP | + SOTG_PORTSC1_FPR | SOTG_PORTSC1_LS /* High Speed */ )); + + /* 4ms settle time */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); + break; + case UHF_PORT_INDICATOR: + SAF1761_WRITE_4(sc, SOTG_PORTSC1, temp & ~SOTG_PORTSC1_PIC); + break; + case UHF_PORT_TEST: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_OVER_CURRENT: + case UHF_C_PORT_RESET: + case UHF_C_PORT_SUSPEND: + /* NOPs */ + break; + case UHF_PORT_POWER: + SAF1761_WRITE_4(sc, SOTG_PORTSC1, temp & ~SOTG_PORTSC1_PP); + break; + case UHF_C_PORT_CONNECTION: + SAF1761_WRITE_4(sc, SOTG_PORTSC1, temp & ~SOTG_PORTSC1_ECSC); + break; + default: + err = USB_ERR_IOERROR; + goto tr_valid; + } + goto tr_valid; + +tr_handle_set_port_feature_device: + DPRINTFN(9, "UR_SET_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_ENABLE: @@ -1809,12 +2357,73 @@ tr_handle_set_port_feature: } *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405161541.s4GFfteG009737>