svn commit: r266241 - head/sys/dev/usb/controller
Hans Petter Selasky
hselasky at FreeBSD.org
Fri May 16 15:41:56 UTC 2014
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 ***
More information about the svn-src-all
mailing list