Add basic support for isochronous transfers in host mode to the

ISP/SAF1761 driver.

Sponsored by:	DARPA, AFRL
This commit is contained in:
hselasky 2014-06-01 10:22:18 +00:00
parent 26c0c3b8dc
commit 29d155d293
2 changed files with 260 additions and 11 deletions

View File

@ -112,6 +112,7 @@ SYSCTL_INT(_hw_usb_saf1761_otg, OID_AUTO, forcefs, CTLFLAG_RW,
static const struct usb_bus_methods saf1761_otg_bus_methods;
static const struct usb_pipe_methods saf1761_otg_non_isoc_methods;
static const struct usb_pipe_methods saf1761_otg_device_isoc_methods;
static const struct usb_pipe_methods saf1761_otg_host_isoc_methods;
static saf1761_otg_cmd_t saf1761_host_setup_tx;
static saf1761_otg_cmd_t saf1761_host_bulk_data_rx;
@ -758,7 +759,8 @@ saf1761_host_intr_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *t
temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) | (td->interval & 0xF8);
temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
(td->interval & 0xF8);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
@ -847,7 +849,8 @@ saf1761_host_intr_data_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *t
temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) | (td->interval & 0xF8);
temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
(td->interval & 0xF8);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
@ -874,21 +877,160 @@ complete:
static uint8_t
saf1761_host_isoc_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
{
uint32_t pdt_addr;
uint32_t temp;
if (td->channel < SOTG_HOST_CHANNEL_MAX) {
uint32_t status;
uint32_t count;
pdt_addr = SOTG_PTD(td->channel);
status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
DPRINTFN(5, "STATUS=0x%08x\n", status);
if (status & SOTG_PTD_DW3_ACTIVE) {
goto busy;
} else if (status & SOTG_PTD_DW3_HALTED) {
goto complete;
}
count = (status & SOTG_PTD_DW3_XFER_COUNT);
/* 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;
} else {
/* invalid USB packet */
td->error_any = 1;
goto complete;
}
}
/* verify the packet byte count */
if (count > td->remainder) {
/* invalid USB packet */
td->error_any = 1;
goto complete;
}
saf1761_read_host_memory(sc, td, count);
goto complete;
}
if (saf1761_host_channel_alloc(sc, td))
goto busy;
/* receive one more packet */
pdt_addr = SOTG_PTD(td->channel);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
temp = (0xFC << td->uframe) & 0xFF; /* complete split */
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
temp = (1U << td->uframe); /* start split */
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
temp = SOTG_PTD_DW3_ACTIVE | SOTG_PTD_DW3_CERR_3;
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
(td->max_packet_size << 18) /* wMaxPacketSize */ |
(td->max_packet_size << 3) /* transfer count */ |
SOTG_PTD_DW0_VALID;
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
/* activate PTD */
SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
(~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
busy:
return (1); /* busy */
complete:
saf1761_host_channel_free(sc, td);
return (0); /* complete */
}
static uint8_t
saf1761_host_isoc_data_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
{
uint32_t pdt_addr;
uint32_t temp;
uint32_t count;
if (td->channel < SOTG_HOST_CHANNEL_MAX) {
uint32_t status;
pdt_addr = SOTG_PTD(td->channel);
status = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW3);
DPRINTFN(5, "STATUS=0x%08x\n", status);
if (status & SOTG_PTD_DW3_ACTIVE) {
goto busy;
} else if (status & SOTG_PTD_DW3_HALTED) {
goto complete;
}
goto complete;
}
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;
}
saf1761_write_host_memory(sc, td, count);
/* send one more packet */
pdt_addr = SOTG_PTD(td->channel);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, 0);
temp = (1U << td->uframe); /* start split */
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
temp = SOTG_PTD_DW3_ACTIVE | SOTG_PTD_DW3_CERR_3;
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
(count << 18) /* wMaxPacketSize */ |
(count << 3) /* transfer count */ |
SOTG_PTD_DW0_VALID;
SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
/* activate PTD */
SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
(~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
busy:
return (1); /* busy */
complete:
saf1761_host_channel_free(sc, td);
return (0); /* complete */
}
static void
@ -2371,7 +2513,7 @@ static const struct usb_pipe_methods saf1761_otg_non_isoc_methods =
};
/*------------------------------------------------------------------------*
* saf1761_otg isochronous support
* saf1761_otg device side isochronous support
*------------------------------------------------------------------------*/
static void
saf1761_otg_device_isoc_open(struct usb_xfer *xfer)
@ -2452,6 +2594,88 @@ static const struct usb_pipe_methods saf1761_otg_device_isoc_methods =
.start = saf1761_otg_device_isoc_start,
};
/*------------------------------------------------------------------------*
* saf1761_otg host side isochronous support
*------------------------------------------------------------------------*/
static void
saf1761_otg_host_isoc_open(struct usb_xfer *xfer)
{
return;
}
static void
saf1761_otg_host_isoc_close(struct usb_xfer *xfer)
{
saf1761_otg_device_done(xfer, USB_ERR_CANCELLED);
}
static void
saf1761_otg_host_isoc_enter(struct usb_xfer *xfer)
{
struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(xfer->xroot->bus);
uint32_t temp;
uint32_t nframes;
DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
xfer, xfer->endpoint->isoc_next, xfer->nframes);
/* get the current frame index - we don't need the high bits */
nframes = (SAF1761_READ_LE_4(sc, SOTG_FRINDEX) & SOTG_FRINDEX_MASK) >> 3;
/*
* check if the frame index is within the window where the
* frames will be inserted
*/
temp = (nframes - xfer->endpoint->isoc_next) & (SOTG_FRINDEX_MASK >> 3);
if ((xfer->endpoint->is_synced == 0) ||
(temp < xfer->nframes)) {
/*
* If there is data underflow or the pipe queue is
* empty we schedule the transfer a few frames ahead
* of the current frame position. Else two isochronous
* transfers might overlap.
*/
xfer->endpoint->isoc_next = (nframes + 3) & (SOTG_FRINDEX_MASK >> 3);
xfer->endpoint->is_synced = 1;
DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
}
/*
* compute how many milliseconds the insertion is ahead of the
* current frame position:
*/
temp = (xfer->endpoint->isoc_next - nframes) & (SOTG_FRINDEX_MASK >> 3);
/*
* pre-compute when the isochronous transfer will be finished:
*/
xfer->isoc_time_complete =
usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
xfer->nframes;
/* compute frame number for next insertion */
xfer->endpoint->isoc_next += xfer->nframes;
/* setup TDs */
saf1761_otg_setup_standard_chain(xfer);
}
static void
saf1761_otg_host_isoc_start(struct usb_xfer *xfer)
{
/* start TD chain */
saf1761_otg_start_standard_chain(xfer);
}
static const struct usb_pipe_methods saf1761_otg_host_isoc_methods =
{
.open = saf1761_otg_host_isoc_open,
.close = saf1761_otg_host_isoc_close,
.enter = saf1761_otg_host_isoc_enter,
.start = saf1761_otg_host_isoc_start,
};
/*------------------------------------------------------------------------*
* saf1761_otg root control support
*------------------------------------------------------------------------*
@ -3174,7 +3398,17 @@ saf1761_otg_xfer_setup(struct usb_setup_params *parm)
td->ep_index = ep_no;
td->ep_type = ep_type;
td->dw1_value = dw1;
td->uframe = 0;
if (ep_type == UE_ISOCHRONOUS) {
if (parm->udev->speed == USB_SPEED_HIGH) {
uint8_t uframe_index = (ntd - 1 - n);
uframe_index <<= usbd_xfer_get_fps_shift(xfer);
td->uframe = (uframe_index & 7);
} else {
td->uframe = 0;
}
} else {
td->uframe = 0;
}
if (ep_type == UE_INTERRUPT) {
if (xfer->interval > 32)
td->interval = (32 / 2) << 3;
@ -3202,6 +3436,8 @@ static void
saf1761_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
struct usb_endpoint *ep)
{
uint16_t mps;
DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d\n",
ep, udev->address,
edesc->bEndpointAddress, udev->flags.usb_mode);
@ -3211,6 +3447,20 @@ saf1761_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *ede
return;
}
/* Verify wMaxPacketSize */
mps = UGETW(edesc->wMaxPacketSize);
if (udev->speed == USB_SPEED_HIGH) {
if ((mps >> 11) & 3) {
DPRINTF("A packet multiplier different from "
"1 is not supported\n");
return;
}
}
if (mps > SOTG_HS_MAX_PACKET_SIZE) {
DPRINTF("Packet size %d bigger than %d\n",
(int)mps, SOTG_HS_MAX_PACKET_SIZE);
return;
}
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
if (udev->speed != USB_SPEED_FULL &&
udev->speed != USB_SPEED_HIGH) {
@ -3227,13 +3477,11 @@ saf1761_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *ede
}
} else {
switch (edesc->bmAttributes & UE_XFERTYPE) {
case UE_CONTROL:
case UE_BULK:
case UE_INTERRUPT:
ep->methods = &saf1761_otg_non_isoc_methods;
case UE_ISOCHRONOUS:
ep->methods = &saf1761_otg_host_isoc_methods;
break;
default:
/* TODO */
ep->methods = &saf1761_otg_non_isoc_methods;
break;
}
}

View File

@ -183,6 +183,7 @@
/* Host controller specific registers */
#define SOTG_FRINDEX 0x002c
#define SOTG_FRINDEX_MASK 0x3fff
#define SOTG_CONFIGFLAG 0x0060
#define SOTG_CONFIGFLAG_ENABLE (1 << 0)
#define SOTG_PORTSC1 0x0064