aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/usb/controller
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2014-08-19 06:50:54 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2014-08-19 06:50:54 +0000
commitee7b0571c2c18bdec848ed2044223cc88db29bd8 (patch)
treeb04f4bd7cd887f50e7d98af35f46b9834ff86c80 /sys/dev/usb/controller
parentffda191e301f128a62c152fde92b692548367fca (diff)
parent15fc2873832ea5b9b639e701bbbf2e73af8b6a88 (diff)
Notes
Diffstat (limited to 'sys/dev/usb/controller')
-rw-r--r--sys/dev/usb/controller/at91dci.c6
-rw-r--r--sys/dev/usb/controller/atmegadci.c16
-rw-r--r--sys/dev/usb/controller/avr32dci.c16
-rw-r--r--sys/dev/usb/controller/dwc_otg.c1782
-rw-r--r--sys/dev/usb/controller/dwc_otg.h39
-rw-r--r--sys/dev/usb/controller/dwc_otg_atmelarm.c200
-rw-r--r--sys/dev/usb/controller/dwc_otg_fdt.c4
-rw-r--r--sys/dev/usb/controller/dwc_otgreg.h5
-rw-r--r--sys/dev/usb/controller/ehci.c13
-rw-r--r--sys/dev/usb/controller/musb_otg.c69
-rw-r--r--sys/dev/usb/controller/musb_otg.h2
-rw-r--r--sys/dev/usb/controller/ohci.c3
-rw-r--r--sys/dev/usb/controller/saf1761_otg.c3633
-rw-r--r--sys/dev/usb/controller/saf1761_otg.h172
-rw-r--r--sys/dev/usb/controller/saf1761_otg_boot.c146
-rw-r--r--sys/dev/usb/controller/saf1761_otg_fdt.c274
-rw-r--r--sys/dev/usb/controller/saf1761_otg_reg.h273
-rw-r--r--sys/dev/usb/controller/uhci.c6
-rw-r--r--sys/dev/usb/controller/usb_controller.c29
-rw-r--r--sys/dev/usb/controller/uss820dci.c16
-rw-r--r--sys/dev/usb/controller/xhci.c64
-rw-r--r--sys/dev/usb/controller/xhci.h8
-rw-r--r--sys/dev/usb/controller/xhci_pci.c10
-rw-r--r--sys/dev/usb/controller/xhcireg.h2
24 files changed, 5587 insertions, 1201 deletions
diff --git a/sys/dev/usb/controller/at91dci.c b/sys/dev/usb/controller/at91dci.c
index 0d5f176a3589f..abcad6233d218 100644
--- a/sys/dev/usb/controller/at91dci.c
+++ b/sys/dev/usb/controller/at91dci.c
@@ -898,7 +898,8 @@ at91dci_setup_standard_chain(struct usb_xfer *xfer)
temp.td = NULL;
temp.td_next = xfer->td_start[0];
temp.offset = 0;
- temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr;
temp.did_stall = !xfer->flags_int.control_stall;
sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
@@ -1124,7 +1125,8 @@ at91dci_standard_done_sub(struct usb_xfer *xfer)
}
/* Check for short transfer */
if (len > 0) {
- if (xfer->flags_int.short_frames_ok) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
/* follow alt next */
if (td->alt_next) {
td = td->obj_next;
diff --git a/sys/dev/usb/controller/atmegadci.c b/sys/dev/usb/controller/atmegadci.c
index 856d0bd916cfd..8e08ff1bdca69 100644
--- a/sys/dev/usb/controller/atmegadci.c
+++ b/sys/dev/usb/controller/atmegadci.c
@@ -804,7 +804,8 @@ atmegadci_setup_standard_chain(struct usb_xfer *xfer)
temp.td = NULL;
temp.td_next = xfer->td_start[0];
temp.offset = 0;
- temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr;
temp.did_stall = !xfer->flags_int.control_stall;
sc = ATMEGA_BUS2SC(xfer->xroot->bus);
@@ -1010,7 +1011,8 @@ atmegadci_standard_done_sub(struct usb_xfer *xfer)
}
/* Check for short transfer */
if (len > 0) {
- if (xfer->flags_int.short_frames_ok) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
/* follow alt next */
if (td->alt_next) {
td = td->obj_next;
@@ -1380,9 +1382,9 @@ atmegadci_do_poll(struct usb_bus *bus)
}
/*------------------------------------------------------------------------*
- * at91dci bulk support
- * at91dci control support
- * at91dci interrupt support
+ * atmegadci bulk support
+ * atmegadci control support
+ * atmegadci interrupt support
*------------------------------------------------------------------------*/
static void
atmegadci_device_non_isoc_open(struct usb_xfer *xfer)
@@ -1419,7 +1421,7 @@ static const struct usb_pipe_methods atmegadci_device_non_isoc_methods =
};
/*------------------------------------------------------------------------*
- * at91dci full speed isochronous support
+ * atmegadci full speed isochronous support
*------------------------------------------------------------------------*/
static void
atmegadci_device_isoc_fs_open(struct usb_xfer *xfer)
@@ -1505,7 +1507,7 @@ static const struct usb_pipe_methods atmegadci_device_isoc_fs_methods =
};
/*------------------------------------------------------------------------*
- * at91dci root control support
+ * atmegadci root control support
*------------------------------------------------------------------------*
* Simulate a hardware HUB by handling all the necessary requests.
*------------------------------------------------------------------------*/
diff --git a/sys/dev/usb/controller/avr32dci.c b/sys/dev/usb/controller/avr32dci.c
index c7c25046a4450..f9b4c641c08af 100644
--- a/sys/dev/usb/controller/avr32dci.c
+++ b/sys/dev/usb/controller/avr32dci.c
@@ -771,7 +771,8 @@ avr32dci_setup_standard_chain(struct usb_xfer *xfer)
temp.td = NULL;
temp.td_next = xfer->td_start[0];
temp.offset = 0;
- temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr;
temp.did_stall = !xfer->flags_int.control_stall;
sc = AVR32_BUS2SC(xfer->xroot->bus);
@@ -979,7 +980,8 @@ avr32dci_standard_done_sub(struct usb_xfer *xfer)
}
/* Check for short transfer */
if (len > 0) {
- if (xfer->flags_int.short_frames_ok) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
/* follow alt next */
if (td->alt_next) {
td = td->obj_next;
@@ -1310,9 +1312,9 @@ avr32dci_do_poll(struct usb_bus *bus)
}
/*------------------------------------------------------------------------*
- * at91dci bulk support
- * at91dci control support
- * at91dci interrupt support
+ * avr32dci bulk support
+ * avr32dci control support
+ * avr32dci interrupt support
*------------------------------------------------------------------------*/
static void
avr32dci_device_non_isoc_open(struct usb_xfer *xfer)
@@ -1349,7 +1351,7 @@ static const struct usb_pipe_methods avr32dci_device_non_isoc_methods =
};
/*------------------------------------------------------------------------*
- * at91dci full speed isochronous support
+ * avr32dci full speed isochronous support
*------------------------------------------------------------------------*/
static void
avr32dci_device_isoc_fs_open(struct usb_xfer *xfer)
@@ -1434,7 +1436,7 @@ static const struct usb_pipe_methods avr32dci_device_isoc_fs_methods =
};
/*------------------------------------------------------------------------*
- * at91dci root control support
+ * avr32dci root control support
*------------------------------------------------------------------------*
* Simulate a hardware HUB by handling all the necessary requests.
*------------------------------------------------------------------------*/
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c
index d8bf7ea39be70..b90fbf56022d5 100644
--- a/sys/dev/usb/controller/dwc_otg.c
+++ b/sys/dev/usb/controller/dwc_otg.c
@@ -89,8 +89,8 @@
((struct dwc_otg_softc *)(((uint8_t *)(bus)) - \
((uint8_t *)&(((struct dwc_otg_softc *)0)->sc_bus))))
-#define DWC_OTG_PC2SC(pc) \
- DWC_OTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus)
+#define DWC_OTG_PC2UDEV(pc) \
+ (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev)
#define DWC_OTG_MSK_GINT_ENABLED \
(GINTMSK_ENUMDONEMSK | \
@@ -98,21 +98,27 @@
GINTMSK_USBSUSPMSK | \
GINTMSK_IEPINTMSK | \
GINTMSK_SESSREQINTMSK | \
+ GINTMSK_RXFLVLMSK | \
+ GINTMSK_HCHINTMSK | \
GINTMSK_OTGINTMSK | \
GINTMSK_PRTINTMSK)
+#define DWC_OTG_MSK_GINT_THREAD_IRQ \
+ (GINTSTS_USBRST | GINTSTS_ENUMDONE | GINTSTS_PRTINT | \
+ GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTMSK_OTGINTMSK | \
+ GINTSTS_SESSREQINT)
+
static int dwc_otg_use_hsic;
static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW, 0, "USB DWC OTG");
-SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, use_hsic, CTLFLAG_RD | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, use_hsic, CTLFLAG_RDTUN,
&dwc_otg_use_hsic, 0, "DWC OTG uses HSIC interface");
-TUNABLE_INT("hw.usb.dwc_otg.use_hsic", &dwc_otg_use_hsic);
#ifdef USB_DEBUG
static int dwc_otg_debug;
-SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RWTUN,
&dwc_otg_debug, 0, "DWC OTG debug level");
#endif
@@ -136,8 +142,9 @@ static dwc_otg_cmd_t dwc_otg_host_data_rx;
static void dwc_otg_device_done(struct usb_xfer *, usb_error_t);
static void dwc_otg_do_poll(struct usb_bus *);
static void dwc_otg_standard_done(struct usb_xfer *);
-static void dwc_otg_root_intr(struct dwc_otg_softc *sc);
-static void dwc_otg_interrupt_poll(struct dwc_otg_softc *sc);
+static void dwc_otg_root_intr(struct dwc_otg_softc *);
+static void dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *);
+static void dwc_otg_host_channel_disable(struct dwc_otg_softc *, uint8_t);
/*
* Here is a configuration that the chip supports.
@@ -177,42 +184,54 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
fifo_size = sc->sc_fifo_size;
- fifo_regs = 4 * (sc->sc_dev_ep_max + sc->sc_dev_in_ep_max);
+ /*
+ * NOTE: Reserved fixed size area at end of RAM, which must
+ * not be allocated to the FIFOs:
+ */
+ fifo_regs = 4 * 16;
- if (fifo_size >= fifo_regs)
- fifo_size -= fifo_regs;
- else
- fifo_size = 0;
+ if (fifo_size < fifo_regs) {
+ DPRINTF("Too little FIFO\n");
+ return (EINVAL);
+ }
+
+ /* subtract FIFO regs from total once */
+ fifo_size -= fifo_regs;
/* split equally for IN and OUT */
fifo_size /= 2;
- DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4);
-
- /* align to 4-bytes */
+ /* align to 4 bytes boundary */
fifo_size &= ~3;
+ /* set global receive FIFO size */
+ DWC_OTG_WRITE_4(sc, DOTG_GRXFSIZ, fifo_size / 4);
+
tx_start = fifo_size;
- if (fifo_size < 0x40) {
+ if (fifo_size < 64) {
DPRINTFN(-1, "Not enough data space for EP0 FIFO.\n");
- USB_BUS_UNLOCK(&sc->sc_bus);
return (EINVAL);
}
+ /* disable any leftover host channels */
+ for (x = 0; x != sc->sc_host_ch_max; x++) {
+ if (sc->sc_chan_state[x].wait_sof == 0)
+ continue;
+ dwc_otg_host_channel_disable(sc, x);
+ }
+
if (mode == DWC_MODE_HOST) {
/* reset active endpoints */
sc->sc_active_rx_ep = 0;
- /* reset TX size */
- sc->sc_tx_cur_size = 0;
-
- /* reset TT info */
- memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
-
+ /* split equally for periodic and non-periodic */
fifo_size /= 2;
+ /* align to 4 bytes boundary */
+ fifo_size &= ~3;
+
DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
((fifo_size / 4) << 16) |
(tx_start / 4));
@@ -221,18 +240,27 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
for (x = 0; x != sc->sc_host_ch_max; x++) {
/* disable all host interrupts */
- DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0);
+ DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+ HCINT_DEFAULT_MASK);
}
DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ,
((fifo_size / 4) << 16) |
(tx_start / 4));
- /* store maximum TX FIFO size */
+ /* reset host channel state */
+ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
+
+ /* reset FIFO TX levels */
+ sc->sc_tx_cur_p_level = 0;
+ sc->sc_tx_cur_np_level = 0;
+
+ /* store maximum periodic and non-periodic FIFO TX size */
sc->sc_tx_max_size = fifo_size;
- /* disable all host channel interrupts */
- DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0);
+ /* enable all host channel interrupts */
+ DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
+ (1U << sc->sc_host_ch_max) - 1U);
}
if (mode == DWC_MODE_DEVICE) {
@@ -311,11 +339,15 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
/* reset active endpoints */
sc->sc_active_rx_ep = 0;
- /* reset TX size */
- sc->sc_tx_cur_size = 0;
+ /* reset periodic and non-periodic FIFO TX size */
+ sc->sc_tx_max_size = fifo_size;
- /* reset TT info */
- memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
+ /* reset host channel state */
+ memset(sc->sc_chan_state, 0, sizeof(sc->sc_chan_state));
+
+ /* reset FIFO TX levels */
+ sc->sc_tx_cur_p_level = 0;
+ sc->sc_tx_cur_np_level = 0;
}
return (0);
}
@@ -323,6 +355,12 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
static void
dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc)
{
+
+ /*
+ * Disabled until further. Assuming that the register is already
+ * programmed correctly by the boot loader.
+ */
+#if 0
uint32_t temp;
/* setup HOST frame interval register, based on existing value */
@@ -348,6 +386,7 @@ dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc)
DPRINTF("HFIR=0x%08x\n", temp);
DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp);
+#endif
}
static void
@@ -532,11 +571,9 @@ dwc_otg_common_rx_ack(struct dwc_otg_softc *sc)
{
DPRINTFN(5, "RX status clear\n");
- if (sc->sc_flags.status_device_mode != 0) {
- /* enable RX FIFO level interrupt */
- sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
- DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
- }
+ /* enable RX FIFO level interrupt */
+ sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
/* clear cached status */
sc->sc_last_rx_status = 0;
@@ -555,125 +592,61 @@ dwc_otg_clear_hcint(struct dwc_otg_softc *sc, uint8_t x)
sc->sc_chan_state[x].hcint = 0;
}
-/*
- * This function waits until a DWC OTG host channel is ready to be
- * used again:
- */
static uint8_t
-dwc_otg_host_channel_wait(struct dwc_otg_td *td)
+dwc_otg_host_channel_alloc(struct dwc_otg_softc *sc, struct dwc_otg_td *td, uint8_t is_out)
{
- struct dwc_otg_softc *sc;
+ uint32_t tx_p_size;
+ uint32_t tx_np_size;
uint8_t x;
- x = td->channel;
-
- DPRINTF("CH=%d\n", x);
-
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
- if (sc->sc_chan_state[x].wait_sof == 0) {
- dwc_otg_clear_hcint(sc, x);
- return (1); /* done */
- }
-
- if (x == 0)
- return (0); /* wait */
-
- /* find new disabled channel */
- for (x = 1; x != sc->sc_host_ch_max; x++) {
-
- if (sc->sc_chan_state[x].allocated)
- continue;
- if (sc->sc_chan_state[x].wait_sof != 0)
- continue;
-
- sc->sc_chan_state[td->channel].allocated = 0;
- sc->sc_chan_state[x].allocated = 1;
-
- sc->sc_chan_state[x].tx_size =
- sc->sc_chan_state[td->channel].tx_size;
-
- if (sc->sc_chan_state[td->channel].suspended) {
- sc->sc_chan_state[td->channel].suspended = 0;
- sc->sc_chan_state[x].suspended = 1;
- }
-
- /* clear interrupts */
- dwc_otg_clear_hcint(sc, x);
-
- DPRINTF("CH=%d HCCHAR=0x%08x "
- "HCSPLT=0x%08x\n", x, td->hcchar, td->hcsplt);
-
- /* ack any pending messages */
- if (sc->sc_last_rx_status != 0 &&
- GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == td->channel) {
- /* get rid of message */
- dwc_otg_common_rx_ack(sc);
- }
-
- /* move active channel */
- sc->sc_active_rx_ep &= ~(1 << td->channel);
- sc->sc_active_rx_ep |= (1 << x);
-
- /* set channel */
- td->channel = x;
-
- return (1); /* new channel allocated */
- }
- return (0); /* wait */
-}
-
-static uint8_t
-dwc_otg_host_channel_alloc(struct dwc_otg_td *td)
-{
- struct dwc_otg_softc *sc;
- uint32_t tx_size;
- uint8_t x;
- uint8_t max_channel;
-
if (td->channel < DWC_OTG_MAX_CHANNELS)
return (0); /* already allocated */
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
+ /* check if device is suspended */
+ if (DWC_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0)
+ return (1); /* busy - cannot transfer data */
- if ((td->hcchar & HCCHAR_EPNUM_MASK) == 0) {
- max_channel = 1;
- x = 0;
- tx_size = td->max_packet_size;
- if ((sc->sc_tx_cur_size + tx_size) > sc->sc_tx_max_size) {
- DPRINTF("Too little FIFO space\n");
- return (1); /* too little FIFO */
- }
- } else {
- max_channel = sc->sc_host_ch_max;
- x = 1;
- if ((td->hcchar & HCCHAR_EPDIR) == HCCHAR_EPDIR_OUT) {
- tx_size = td->max_packet_size;
- if (td->hcsplt != 0 && tx_size > HCSPLT_XACTLEN_MAX)
- tx_size = HCSPLT_XACTLEN_MAX;
- if ((sc->sc_tx_cur_size + tx_size) > sc->sc_tx_max_size) {
+ /* compute needed TX FIFO size */
+ if (is_out != 0) {
+ if (td->ep_type == UE_ISOCHRONOUS) {
+ tx_p_size = td->max_packet_size;
+ tx_np_size = 0;
+ if (td->hcsplt != 0 && tx_p_size > HCSPLT_XACTLEN_BURST)
+ tx_p_size = HCSPLT_XACTLEN_BURST;
+ if ((sc->sc_tx_cur_p_level + tx_p_size) > sc->sc_tx_max_size) {
DPRINTF("Too little FIFO space\n");
return (1); /* too little FIFO */
}
} else {
- tx_size = 0;
+ tx_p_size = 0;
+ tx_np_size = td->max_packet_size;
+ if (td->hcsplt != 0 && tx_np_size > HCSPLT_XACTLEN_BURST)
+ tx_np_size = HCSPLT_XACTLEN_BURST;
+ if ((sc->sc_tx_cur_np_level + tx_np_size) > sc->sc_tx_max_size) {
+ DPRINTF("Too little FIFO space\n");
+ return (1); /* too little FIFO */
+ }
}
+ } else {
+ /* not a TX transaction */
+ tx_p_size = 0;
+ tx_np_size = 0;
}
- for (; x != max_channel; x++) {
-
- if (sc->sc_chan_state[x].allocated)
+ for (x = 0; x != sc->sc_host_ch_max; x++) {
+ if (sc->sc_chan_state[x].allocated != 0)
continue;
+ /* check if channel is still enabled */
if (sc->sc_chan_state[x].wait_sof != 0)
continue;
sc->sc_chan_state[x].allocated = 1;
- sc->sc_chan_state[x].tx_size = tx_size;
+ sc->sc_chan_state[x].tx_p_size = tx_p_size;
+ sc->sc_chan_state[x].tx_np_size = tx_np_size;
- /* keep track of used FIFO */
- sc->sc_tx_cur_size += tx_size;
+ /* keep track of used TX FIFO, if any */
+ sc->sc_tx_cur_p_level += tx_p_size;
+ sc->sc_tx_cur_np_level += tx_np_size;
/* clear interrupts */
dwc_otg_clear_hcint(sc, x);
@@ -689,29 +662,14 @@ dwc_otg_host_channel_alloc(struct dwc_otg_td *td)
return (0); /* allocated */
}
+ /* wait a bit */
+ dwc_otg_enable_sof_irq(sc);
return (1); /* busy */
}
static void
-dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x)
-{
- uint32_t hcchar;
- if (sc->sc_chan_state[x].wait_sof != 0)
- return;
- hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
- if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) {
- /* disable channel */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
- HCCHAR_CHENA | HCCHAR_CHDIS);
- /* don't re-use channel until next SOF is transmitted */
- sc->sc_chan_state[x].wait_sof = 2;
- }
-}
-
-static void
-dwc_otg_host_channel_free(struct dwc_otg_td *td)
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
uint8_t x;
if (td->channel >= DWC_OTG_MAX_CHANNELS)
@@ -723,16 +681,21 @@ dwc_otg_host_channel_free(struct dwc_otg_td *td)
DPRINTF("CH=%d\n", x);
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
- dwc_otg_host_channel_disable(sc, x);
+ /*
+ * We need to let programmed host channels run till complete
+ * else the host channel will stop functioning. Assume that
+ * after a fixed given amount of time the host channel is no
+ * longer doing any USB traffic:
+ */
+ if (td->ep_type == UE_ISOCHRONOUS) {
+ /* double buffered */
+ sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX;
+ } else {
+ /* single buffered */
+ sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MIN;
+ }
sc->sc_chan_state[x].allocated = 0;
- sc->sc_chan_state[x].suspended = 0;
-
- /* keep track of used FIFO */
- sc->sc_tx_cur_size -= sc->sc_chan_state[x].tx_size;
/* ack any pending messages */
if (sc->sc_last_rx_status != 0 &&
@@ -744,26 +707,39 @@ dwc_otg_host_channel_free(struct dwc_otg_td *td)
sc->sc_active_rx_ep &= ~(1 << x);
}
+static void
+dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ /* dump any pending messages */
+ if (sc->sc_last_rx_status != 0) {
+ if (td->channel < DWC_OTG_MAX_CHANNELS &&
+ td->channel == GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) {
+ dwc_otg_common_rx_ack(sc);
+ }
+ }
+}
+
static uint8_t
-dwc_otg_host_setup_tx(struct dwc_otg_td *td)
+dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
struct usb_device_request req __aligned(4);
- struct dwc_otg_softc *sc;
uint32_t hcint;
uint32_t hcchar;
+ uint8_t delta;
- if (dwc_otg_host_channel_alloc(td))
- goto busy;
-
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
+ dwc_otg_host_dump_rx(sc, td);
- hcint = sc->sc_chan_state[td->channel].hcint;
+ if (td->channel < DWC_OTG_MAX_CHANNELS) {
+ hcint = sc->sc_chan_state[td->channel].hcint;
- DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
- td->channel, td->state, hcint,
- DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
- DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+ DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+ td->channel, td->state, hcint,
+ DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+ DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+ } else {
+ hcint = 0;
+ goto check_state;
+ }
if (hcint & (HCINT_RETRY |
HCINT_ACK | HCINT_NYET)) {
@@ -782,34 +758,23 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
}
}
- /* channel must be disabled before we can complete the transfer */
-
if (hcint & (HCINT_ERRORS | HCINT_RETRY |
HCINT_ACK | HCINT_NYET)) {
-
- dwc_otg_host_channel_disable(sc, td->channel);
-
if (!(hcint & HCINT_ERRORS))
td->errcnt = 0;
}
+check_state:
switch (td->state) {
case DWC_CHAN_ST_START:
- if (!dwc_otg_host_channel_wait(td))
- break;
goto send_pkt;
case DWC_CHAN_ST_WAIT_ANE:
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
- td->did_nak = 1;
+ td->did_nak++;
td->tt_scheduled = 0;
goto send_pkt;
- }
- if (hcint & (HCINT_ACK | HCINT_NYET)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
td->offset += td->tx_bytes;
td->remainder -= td->tx_bytes;
td->toggle = 1;
@@ -820,35 +785,22 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
case DWC_CHAN_ST_WAIT_S_ANE:
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
- td->did_nak = 1;
+ td->did_nak++;
td->tt_scheduled = 0;
goto send_pkt;
- }
- if (hcint & (HCINT_ACK | HCINT_NYET)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
goto send_cpkt;
}
break;
case DWC_CHAN_ST_WAIT_C_ANE:
if (hcint & HCINT_NYET) {
- if (!dwc_otg_host_channel_wait(td))
- break;
goto send_cpkt;
- }
- if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
- td->did_nak = 1;
+ } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ td->did_nak++;
td->tt_scheduled = 0;
goto send_pkt;
- }
- if (hcint & HCINT_ACK) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & HCINT_ACK) {
td->offset += td->tx_bytes;
td->remainder -= td->tx_bytes;
td->toggle = 1;
@@ -857,8 +809,6 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
break;
case DWC_CHAN_ST_WAIT_C_PKT:
- if (!dwc_otg_host_channel_wait(td))
- break;
goto send_cpkt;
default:
@@ -867,20 +817,36 @@ dwc_otg_host_setup_tx(struct dwc_otg_td *td)
goto busy;
send_pkt:
+ /* free existing channel, if any */
+ dwc_otg_host_channel_free(sc, td);
+
if (sizeof(req) != td->remainder) {
td->error_any = 1;
goto complete;
}
if (td->hcsplt != 0) {
- /* Wait for our turn, if TT transfer */
- if (td->tt_scheduled == 0 ||
- (sc->sc_last_frame_num & 7) < td->tt_start_slot) {
- /* set return state */
+ delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+ if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+ td->state = DWC_CHAN_ST_START;
+ goto busy;
+ }
+ delta = sc->sc_last_frame_num - td->tt_start_slot;
+ if (delta > 5) {
+ /* missed it */
+ td->tt_scheduled = 0;
td->state = DWC_CHAN_ST_START;
- goto tt_wait;
+ goto busy;
}
+ }
+
+ /* allocate a new channel */
+ if (dwc_otg_host_channel_alloc(sc, td, 1)) {
+ td->state = DWC_CHAN_ST_START;
+ goto busy;
+ }
+ if (td->hcsplt != 0) {
td->hcsplt &= ~HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_S_ANE;
} else {
@@ -897,7 +863,8 @@ send_pkt:
DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
hcchar = td->hcchar;
- hcchar &= ~HCCHAR_EPDIR_IN;
+ hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
+ hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
/* must enable channel before writing data to FIFO */
DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
@@ -906,21 +873,37 @@ send_pkt:
bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
/* store number of bytes transmitted */
td->tx_bytes = sizeof(req);
-
goto busy;
send_cpkt:
- /* Wait for our turn, if TT transfer */
- if (td->tt_scheduled == 0 ||
- (sc->sc_last_frame_num & 7) < td->tt_complete_slot) {
- /* set return state */
+ /* free existing channel, if any */
+ dwc_otg_host_channel_free(sc, td);
+
+ delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+ if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
td->state = DWC_CHAN_ST_WAIT_C_PKT;
- goto tt_wait;
+ goto busy;
+ }
+ delta = sc->sc_last_frame_num - td->tt_start_slot;
+ if (delta > DWC_OTG_TT_SLOT_MAX) {
+ /* we missed the service interval */
+ if (td->ep_type != UE_ISOCHRONOUS)
+ td->error_any = 1;
+ goto complete;
+ }
+ /* allocate a new channel */
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+ td->state = DWC_CHAN_ST_WAIT_C_PKT;
+ goto busy;
}
- /* wait until next slot before trying again */
- td->tt_complete_slot++;
+
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
td->hcsplt |= HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_C_ANE;
@@ -931,32 +914,27 @@ send_cpkt:
DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
hcchar = td->hcchar;
- hcchar &= ~HCCHAR_EPDIR_IN;
+ hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
+ hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
/* must enable channel before writing data to FIFO */
DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
- goto busy;
-tt_wait:
- /* free allocated channel */
- dwc_otg_host_channel_free(td);
busy:
return (1); /* busy */
+
complete:
+ dwc_otg_host_channel_free(sc, td);
return (0); /* complete */
}
static uint8_t
-dwc_otg_setup_rx(struct dwc_otg_td *td)
+dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
struct usb_device_request req __aligned(4);
uint32_t temp;
uint16_t count;
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
/* check endpoint status */
if (sc->sc_last_rx_status == 0)
@@ -1097,23 +1075,9 @@ dwc_otg_host_rate_check_interrupt(struct dwc_otg_softc *sc, struct dwc_otg_td *t
}
static uint8_t
-dwc_otg_host_rate_check(struct dwc_otg_td *td)
+dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
- uint8_t ep_type;
-
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
- if (td->channel < DWC_OTG_MAX_CHANNELS &&
- sc->sc_chan_state[td->channel].suspended)
- goto busy;
-
- ep_type = ((td->hcchar &
- HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
-
- if (ep_type == UE_ISOCHRONOUS) {
-
+ if (td->ep_type == UE_ISOCHRONOUS) {
/* non TT isochronous traffic */
if ((td->tmr_val != 0) ||
(sc->sc_last_frame_num & (td->tmr_res - 1))) {
@@ -1122,11 +1086,11 @@ dwc_otg_host_rate_check(struct dwc_otg_td *td)
td->tmr_val = 1; /* executed */
td->toggle = 0;
- } else if (ep_type == UE_INTERRUPT) {
+ } else if (td->ep_type == UE_INTERRUPT) {
if (!td->tt_scheduled)
goto busy;
td->tt_scheduled = 0;
- } else if (td->did_nak != 0) {
+ } else if (td->did_nak >= DWC_OTG_NAK_MAX) {
goto busy;
} else if (td->set_toggle) {
td->set_toggle = 0;
@@ -1138,68 +1102,21 @@ busy:
}
static uint8_t
-dwc_otg_host_data_rx(struct dwc_otg_td *td)
+dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
- uint32_t hcint;
- uint32_t hcchar;
uint32_t count;
- uint8_t ep_type;
-
- if (dwc_otg_host_channel_alloc(td))
- goto busy;
-
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
- ep_type = ((td->hcchar &
- HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
-
- hcint = sc->sc_chan_state[td->channel].hcint;
-
- DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
- td->channel, td->state, hcint,
- DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
- DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
-
- /* check interrupt bits */
-
- if (hcint & (HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
- /* give success bits priority over failure bits */
- } else if (hcint & HCINT_STALL) {
- DPRINTF("CH=%d STALL\n", td->channel);
- td->error_stall = 1;
- td->error_any = 1;
- goto complete;
- } else if (hcint & HCINT_ERRORS) {
- DPRINTF("CH=%d ERROR\n", td->channel);
- td->errcnt++;
- if (td->hcsplt != 0 || td->errcnt >= 3) {
- if (ep_type != UE_ISOCHRONOUS) {
- td->error_any = 1;
- goto complete;
- }
- }
- }
-
- /* channel must be disabled before we can complete the transfer */
-
- if (hcint & (HCINT_ERRORS | HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
-
- dwc_otg_host_channel_disable(sc, td->channel);
-
- if (!(hcint & HCINT_ERRORS))
- td->errcnt = 0;
- }
+ uint8_t channel;
/* check endpoint status */
if (sc->sc_last_rx_status == 0)
- goto check_state;
+ goto busy;
- if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->channel)
- goto check_state;
+ channel = td->channel;
+ if (channel >= DWC_OTG_MAX_CHANNELS)
+ goto busy;
+
+ if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != channel)
+ goto busy;
switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
case GRXSTSRH_IN_DATA:
@@ -1207,7 +1124,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
DPRINTF("DATA ST=%d STATUS=0x%08x\n",
(int)td->state, (int)sc->sc_last_rx_status);
- if (hcint & HCINT_SOFTWARE_ONLY) {
+ if (sc->sc_chan_state[channel].hcint & HCINT_SOFTWARE_ONLY) {
/*
* When using SPLIT transactions on interrupt
* endpoints, sometimes data occurs twice.
@@ -1220,7 +1137,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
/* check for isochronous transfer or high-speed bandwidth endpoint */
- if (ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) {
+ if (td->ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) {
if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != GRXSTSRD_DPID_DATA0) {
td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
} else {
@@ -1269,21 +1186,75 @@ dwc_otg_host_data_rx(struct dwc_otg_td *td)
td->remainder -= count;
td->offset += count;
- hcint |= HCINT_SOFTWARE_ONLY | HCINT_ACK;
- sc->sc_chan_state[td->channel].hcint = hcint;
+ sc->sc_chan_state[channel].hcint |= HCINT_SOFTWARE_ONLY;
break;
-
default:
break;
}
/* release FIFO */
dwc_otg_common_rx_ack(sc);
+busy:
+ return (0);
+complete:
+ return (1);
+}
+
+static uint8_t
+dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+ uint32_t hcint;
+ uint32_t hcchar;
+ uint8_t delta;
+ uint8_t channel;
+
+ channel = td->channel;
+
+ if (channel < DWC_OTG_MAX_CHANNELS) {
+ hcint = sc->sc_chan_state[channel].hcint;
+
+ DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+ channel, td->state, hcint,
+ DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+ DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
+
+ /* check interrupt bits */
+ if (hcint & (HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ /* give success bits priority over failure bits */
+ } else if (hcint & HCINT_STALL) {
+ DPRINTF("CH=%d STALL\n", channel);
+ td->error_stall = 1;
+ td->error_any = 1;
+ goto complete;
+ } else if (hcint & HCINT_ERRORS) {
+ DPRINTF("CH=%d ERROR\n", channel);
+ td->errcnt++;
+ if (td->hcsplt != 0 || td->errcnt >= 3) {
+ if (td->ep_type != UE_ISOCHRONOUS) {
+ td->error_any = 1;
+ goto complete;
+ }
+ }
+ }
+
+ /* check channels for data, if any */
+ if (dwc_otg_host_data_rx_sub(sc, td))
+ goto complete;
+
+ /* refresh interrupt status */
+ hcint = sc->sc_chan_state[channel].hcint;
+
+ if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+ } else {
+ hcint = 0;
+ }
-check_state:
switch (td->state) {
case DWC_CHAN_ST_START:
- if (!dwc_otg_host_channel_wait(td))
- break;
if (td->hcsplt != 0)
goto receive_spkt;
else
@@ -1291,38 +1262,49 @@ check_state:
case DWC_CHAN_ST_WAIT_ANE:
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
-
- td->did_nak = 1;
+ if (td->ep_type == UE_INTERRUPT) {
+ /*
+ * The USB specification does not
+ * mandate a particular data toggle
+ * value for USB INTERRUPT
+ * transfers. Switch the data toggle
+ * value to receive the packet
+ * correctly:
+ */
+ if (hcint & HCINT_DATATGLERR) {
+ DPRINTF("Retrying packet due to "
+ "data toggle error\n");
+ td->toggle ^= 1;
+ goto receive_pkt;
+ }
+ }
+ td->did_nak++;
td->tt_scheduled = 0;
if (td->hcsplt != 0)
goto receive_spkt;
else
goto receive_pkt;
- }
- if (!(hcint & HCINT_SOFTWARE_ONLY)) {
- if (hcint & HCINT_NYET) {
- if (ep_type == UE_ISOCHRONOUS) {
- /* we missed the service interval */
- goto complete;
- }
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & HCINT_NYET) {
+ if (td->hcsplt != 0) {
+ /* try again */
goto receive_pkt;
+ } else {
+ /* not a valid token for IN endpoints */
+ td->error_any = 1;
+ goto complete;
}
- break;
- }
- if (hcint & (HCINT_ACK | HCINT_NYET)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & HCINT_ACK) {
+ /* wait for data - ACK arrived first */
+ if (!(hcint & HCINT_SOFTWARE_ONLY))
+ goto busy;
- if (ep_type == UE_ISOCHRONOUS) {
+ if (td->ep_type == UE_ISOCHRONOUS) {
/* check if we are complete */
if ((td->remainder == 0) ||
- (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN))
+ (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) {
goto complete;
-
+ }
+ /* get another packet */
goto receive_pkt;
} else {
/* check if we are complete */
@@ -1336,6 +1318,7 @@ check_state:
*/
}
td->tt_scheduled = 0;
+ td->did_nak = 0;
if (td->hcsplt != 0)
goto receive_spkt;
else
@@ -1350,23 +1333,19 @@ check_state:
* case of interrupt and isochronous transfers:
*/
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
-
- td->did_nak = 1;
+ td->did_nak++;
td->tt_scheduled = 0;
goto receive_spkt;
- }
- if (hcint & (HCINT_ACK | HCINT_NYET)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & HCINT_NYET) {
+ td->tt_scheduled = 0;
+ goto receive_spkt;
+ } else if (hcint & HCINT_ACK) {
+ td->did_nak = 0;
goto receive_pkt;
}
break;
case DWC_CHAN_ST_WAIT_C_PKT:
- if (!dwc_otg_host_channel_wait(td))
- break;
goto receive_pkt;
default:
@@ -1375,119 +1354,138 @@ check_state:
goto busy;
receive_pkt:
+ /* free existing channel, if any */
+ dwc_otg_host_channel_free(sc, td);
+
if (td->hcsplt != 0) {
- /* Wait for our turn, if TT transfer */
- if (td->tt_scheduled == 0 ||
- (sc->sc_last_frame_num & 7) < td->tt_complete_slot) {
- /* set return state */
+ delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+ if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
td->state = DWC_CHAN_ST_WAIT_C_PKT;
- goto tt_wait;
+ goto busy;
}
- /* wait until next slot before trying again */
- td->tt_complete_slot++;
-
- /* set toggle, if any */
- if (td->set_toggle) {
- td->set_toggle = 0;
- td->toggle = 1;
+ delta = sc->sc_last_frame_num - td->tt_start_slot;
+ if (delta > DWC_OTG_TT_SLOT_MAX) {
+ if (td->ep_type != UE_ISOCHRONOUS) {
+ /* we missed the service interval */
+ td->error_any = 1;
+ }
+ goto complete;
}
+ /* complete split */
td->hcsplt |= HCSPLT_COMPSPLT;
- count = HCSPLT_XACTLEN_MAX;
} else if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN &&
- dwc_otg_host_rate_check(td)) {
- td->state = DWC_CHAN_ST_START;
- dwc_otg_host_channel_free(td);
+ dwc_otg_host_rate_check(sc, td)) {
+ td->state = DWC_CHAN_ST_WAIT_C_PKT;
+ goto busy;
+ }
+
+ /* allocate a new channel */
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+ td->state = DWC_CHAN_ST_WAIT_C_PKT;
goto busy;
- } else {
- count = td->max_packet_size;
}
+
+ channel = td->channel;
+
+ /* set toggle, if any */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+
td->state = DWC_CHAN_ST_WAIT_ANE;
/* receive one packet */
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
- (count << HCTSIZ_XFERSIZE_SHIFT) |
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+ (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
(HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
- DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
-
- /* send ASAP */
- if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1))
- td->hcchar |= HCCHAR_ODDFRM;
- else
- td->hcchar &= ~HCCHAR_ODDFRM;
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
hcchar = td->hcchar;
hcchar |= HCCHAR_EPDIR_IN;
+ /* receive complete split ASAP */
+ if ((sc->sc_last_frame_num & 1) != 0)
+ hcchar |= HCCHAR_ODDFRM;
+ else
+ hcchar &= ~HCCHAR_ODDFRM;
+
/* must enable channel before data can be received */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
+
goto busy;
receive_spkt:
- /* Wait for our turn, if TT transfer */
- if (td->tt_scheduled == 0) {
- if (ep_type == UE_INTERRUPT) {
- td->state = DWC_CHAN_ST_START;
- dwc_otg_host_channel_free(td);
- goto busy;
- }
- /* set return state */
+ /* free existing channel(s), if any */
+ dwc_otg_host_channel_free(sc, td);
+
+ delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+ if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
td->state = DWC_CHAN_ST_START;
- goto tt_wait;
+ goto busy;
}
- if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) {
- /* set return state */
+ delta = sc->sc_last_frame_num - td->tt_start_slot;
+ if (delta > 5) {
+ /* missed it */
+ td->tt_scheduled = 0;
td->state = DWC_CHAN_ST_START;
- goto tt_wait;
+ goto busy;
}
- /* send ASAP */
- if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1))
- td->hcchar |= HCCHAR_ODDFRM;
- else
- td->hcchar &= ~HCCHAR_ODDFRM;
+ /* allocate a new channel */
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
+ td->state = DWC_CHAN_ST_START;
+ goto busy;
+ }
+
+ channel = td->channel;
td->hcsplt &= ~HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_S_ANE;
/* receive one packet */
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
- (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
- (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+ (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
- DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+
+ /* send after next SOF event */
+ if ((sc->sc_last_frame_num & 1) == 0)
+ td->hcchar |= HCCHAR_ODDFRM;
+ else
+ td->hcchar &= ~HCCHAR_ODDFRM;
hcchar = td->hcchar;
hcchar |= HCCHAR_EPDIR_IN;
- /* must enable channel before data can be received */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
- goto busy;
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
-tt_wait:
- /* free allocated channel */
- dwc_otg_host_channel_free(td);
+ /* must enable channel before data can be received */
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
busy:
return (1); /* busy */
+
complete:
+ dwc_otg_host_channel_free(sc, td);
return (0); /* complete */
}
static uint8_t
-dwc_otg_data_rx(struct dwc_otg_td *td)
+dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
uint32_t temp;
uint16_t count;
uint8_t got_short;
got_short = 0;
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
/* check endpoint status */
if (sc->sc_last_rx_status == 0)
goto not_complete;
@@ -1589,79 +1587,67 @@ not_complete:
}
static uint8_t
-dwc_otg_host_data_tx(struct dwc_otg_td *td)
+dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
uint32_t count;
uint32_t hcint;
uint32_t hcchar;
- uint8_t ep_type;
-
- if (dwc_otg_host_channel_alloc(td))
- goto busy;
+ uint8_t delta;
+ uint8_t channel;
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
+ dwc_otg_host_dump_rx(sc, td);
- ep_type = ((td->hcchar &
- HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
+ channel = td->channel;
- hcint = sc->sc_chan_state[td->channel].hcint;
+ if (channel < DWC_OTG_MAX_CHANNELS) {
+ hcint = sc->sc_chan_state[channel].hcint;
- DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
- td->channel, td->state, hcint,
- DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
- DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+ DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+ channel, td->state, hcint,
+ DWC_OTG_READ_4(sc, DOTG_HCCHAR(channel)),
+ DWC_OTG_READ_4(sc, DOTG_HCTSIZ(channel)));
- if (hcint & (HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
- /* give success bits priority over failure bits */
- } else if (hcint & HCINT_STALL) {
- DPRINTF("CH=%d STALL\n", td->channel);
- td->error_stall = 1;
- td->error_any = 1;
- goto complete;
- } else if (hcint & HCINT_ERRORS) {
- DPRINTF("CH=%d ERROR\n", td->channel);
- td->errcnt++;
- if (td->hcsplt != 0 || td->errcnt >= 3) {
+ if (hcint & (HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ /* give success bits priority over failure bits */
+ } else if (hcint & HCINT_STALL) {
+ DPRINTF("CH=%d STALL\n", channel);
+ td->error_stall = 1;
td->error_any = 1;
goto complete;
+ } else if (hcint & HCINT_ERRORS) {
+ DPRINTF("CH=%d ERROR\n", channel);
+ td->errcnt++;
+ if (td->hcsplt != 0 || td->errcnt >= 3) {
+ td->error_any = 1;
+ goto complete;
+ }
}
- }
-
- /* channel must be disabled before we can complete the transfer */
-
- if (hcint & (HCINT_ERRORS | HCINT_RETRY |
- HCINT_ACK | HCINT_NYET)) {
- dwc_otg_host_channel_disable(sc, td->channel);
+ if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
- if (!(hcint & HCINT_ERRORS))
- td->errcnt = 0;
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+ } else {
+ hcint = 0;
}
switch (td->state) {
case DWC_CHAN_ST_START:
- if (!dwc_otg_host_channel_wait(td))
- break;
goto send_pkt;
case DWC_CHAN_ST_WAIT_ANE:
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
- td->did_nak = 1;
+ td->did_nak++;
td->tt_scheduled = 0;
goto send_pkt;
- }
- if (hcint & (HCINT_ACK | HCINT_NYET)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
-
+ } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
td->offset += td->tx_bytes;
td->remainder -= td->tx_bytes;
td->toggle ^= 1;
+ td->did_nak = 0;
td->tt_scheduled = 0;
/* check remainder */
@@ -1680,38 +1666,27 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
case DWC_CHAN_ST_WAIT_S_ANE:
if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
- td->did_nak = 1;
+ td->did_nak++;
td->tt_scheduled = 0;
goto send_pkt;
- }
- if (hcint & (HCINT_ACK | HCINT_NYET)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ td->did_nak = 0;
goto send_cpkt;
}
break;
case DWC_CHAN_ST_WAIT_C_ANE:
if (hcint & HCINT_NYET) {
- if (!dwc_otg_host_channel_wait(td))
- break;
goto send_cpkt;
- }
- if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
- if (!dwc_otg_host_channel_wait(td))
- break;
- td->did_nak = 1;
+ } else if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ td->did_nak++;
td->tt_scheduled = 0;
goto send_pkt;
- }
- if (hcint & HCINT_ACK) {
- if (!dwc_otg_host_channel_wait(td))
- break;
+ } else if (hcint & HCINT_ACK) {
td->offset += td->tx_bytes;
td->remainder -= td->tx_bytes;
td->toggle ^= 1;
+ td->did_nak = 0;
td->tt_scheduled = 0;
/* check remainder */
@@ -1726,46 +1701,35 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
break;
case DWC_CHAN_ST_WAIT_C_PKT:
- if (!dwc_otg_host_channel_wait(td))
- break;
goto send_cpkt;
case DWC_CHAN_ST_TX_WAIT_ISOC:
/* Check if isochronous OUT traffic is complete */
- if ((hcint & HCINT_ACK) == 0)
+ if ((hcint & HCINT_HCH_DONE_MASK) == 0)
break;
td->offset += td->tx_bytes;
td->remainder -= td->tx_bytes;
- /* Update split token according to specification */
- if (td->hcsplt != 0) {
- if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)
- td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
- } else if (td->max_packet_count > 1) {
- td->tt_xactpos++;
- }
+ if (td->hcsplt != 0 || td->remainder == 0)
+ goto complete;
- dwc_otg_host_channel_disable(sc, td->channel);
+ /* check for next packet */
+ if (td->max_packet_count > 1)
+ td->tt_xactpos++;
- if (td->remainder == 0)
- goto complete;
+ /* free existing channel, if any */
+ dwc_otg_host_channel_free(sc, td);
td->state = DWC_CHAN_ST_TX_PKT_ISOC;
/* FALLTHROUGH */
case DWC_CHAN_ST_TX_PKT_ISOC:
- if (!dwc_otg_host_channel_wait(td))
+ if (dwc_otg_host_channel_alloc(sc, td, 1))
break;
-
- if (td->hcsplt != 0) {
- if ((sc->sc_last_frame_num & 7) < td->tt_start_slot)
- goto tt_wait;
- /* packets must be 125us apart */
- td->tt_start_slot++;
- }
+ channel = td->channel;
goto send_isoc_pkt;
default:
break;
@@ -1773,39 +1737,42 @@ dwc_otg_host_data_tx(struct dwc_otg_td *td)
goto busy;
send_pkt:
+ /* free existing channel(s), if any */
+ dwc_otg_host_channel_free(sc, td);
+
if (td->hcsplt != 0) {
- /* Wait for our turn, if TT transfer */
- if (td->tt_scheduled == 0) {
- if (ep_type == UE_INTERRUPT) {
- td->state = DWC_CHAN_ST_START;
- dwc_otg_host_channel_free(td);
- goto busy;
- }
- /* set return state */
+ delta = td->tt_start_slot - sc->sc_last_frame_num - 1;
+ if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
td->state = DWC_CHAN_ST_START;
- goto tt_wait;
+ goto busy;
}
- if ((sc->sc_last_frame_num & 7) < td->tt_start_slot) {
- /* set return state */
+ delta = sc->sc_last_frame_num - td->tt_start_slot;
+ if (delta > 5) {
+ /* missed it */
+ td->tt_scheduled = 0;
td->state = DWC_CHAN_ST_START;
- goto tt_wait;
+ goto busy;
}
+ } else if (dwc_otg_host_rate_check(sc, td)) {
+ td->state = DWC_CHAN_ST_START;
+ goto busy;
+ }
- /* packets must be 125us apart */
- td->tt_start_slot++;
-
- /* set toggle, if any */
- if (td->set_toggle) {
- td->set_toggle = 0;
- td->toggle = 1;
- }
- } else if (dwc_otg_host_rate_check(td)) {
+ /* allocate a new channel */
+ if (dwc_otg_host_channel_alloc(sc, td, 1)) {
td->state = DWC_CHAN_ST_START;
- dwc_otg_host_channel_free(td);
goto busy;
}
- if (ep_type == UE_ISOCHRONOUS) {
+ channel = td->channel;
+
+ /* set toggle, if any */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+
+ if (td->ep_type == UE_ISOCHRONOUS) {
send_isoc_pkt:
/* Isochronous OUT transfers don't have any ACKs */
td->state = DWC_CHAN_ST_TX_WAIT_ISOC;
@@ -1813,23 +1780,14 @@ send_isoc_pkt:
if (td->hcsplt != 0) {
/* get maximum transfer length */
count = td->remainder;
-
- /* Update split token according to specification */
- if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) {
- if (count <= HCSPLT_XACTLEN_MAX)
- td->tt_xactpos = HCSPLT_XACTPOS_ALL;
- else
- count = HCSPLT_XACTLEN_MAX;
- } else if (td->tt_xactpos == HCSPLT_XACTPOS_MIDDLE) {
- if (count <= HCSPLT_XACTLEN_MAX)
- td->tt_xactpos = HCSPLT_XACTPOS_LAST;
- else
- count = HCSPLT_XACTLEN_MAX;
+ if (count > HCSPLT_XACTLEN_BURST) {
+ DPRINTF("TT overflow\n");
+ td->error_any = 1;
+ goto complete;
}
-
/* Update transaction position */
td->hcsplt &= ~HCSPLT_XACTPOS_MASK;
- td->hcsplt |= ((uint32_t)td->tt_xactpos << HCSPLT_XACTPOS_SHIFT);
+ td->hcsplt |= (HCSPLT_XACTPOS_ALL << HCSPLT_XACTPOS_SHIFT);
} else {
/* send one packet at a time */
count = td->max_packet_size;
@@ -1883,24 +1841,24 @@ send_isoc_pkt:
}
if (td->tt_xactpos == td->npkt) {
if (td->npkt == 1) {
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
(count << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
} else if (td->npkt == 2) {
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
(count << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT));
} else {
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
(count << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT));
}
td->npkt = 0;
} else {
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
(count << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT));
@@ -1908,26 +1866,26 @@ send_isoc_pkt:
} else {
/* TODO: HCTSIZ_DOPNG */
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
(count << HCTSIZ_XFERSIZE_SHIFT) |
(1 << HCTSIZ_PKTCNT_SHIFT) |
(td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
(HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
}
- DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
-
- /* send ASAP */
- if ((ep_type == UE_ISOCHRONOUS) && !(sc->sc_last_frame_num & 1))
- td->hcchar |= HCCHAR_ODDFRM;
- else
- td->hcchar &= ~HCCHAR_ODDFRM;
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
hcchar = td->hcchar;
hcchar &= ~HCCHAR_EPDIR_IN;
+ /* send after next SOF event */
+ if ((sc->sc_last_frame_num & 1) == 0)
+ hcchar |= HCCHAR_ODDFRM;
+ else
+ hcchar &= ~HCCHAR_ODDFRM;
+
/* must enable before writing data to FIFO */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
if (count != 0) {
@@ -1940,7 +1898,7 @@ send_isoc_pkt:
/* transfer data into FIFO */
bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
- DOTG_DFIFO(td->channel),
+ DOTG_DFIFO(channel),
sc->sc_tx_bounce_buffer, (count + 3) / 4);
}
@@ -1949,45 +1907,63 @@ send_isoc_pkt:
goto busy;
send_cpkt:
- /* Wait for our turn, if TT transfer */
- if (td->tt_scheduled == 0 ||
- (sc->sc_last_frame_num & 7) < td->tt_complete_slot) {
- /* set return state */
+ /* free existing channel, if any */
+ dwc_otg_host_channel_free(sc, td);
+
+ delta = td->tt_complete_slot - sc->sc_last_frame_num - 1;
+ if (td->tt_scheduled == 0 || delta < DWC_OTG_TT_SLOT_MAX) {
+ td->state = DWC_CHAN_ST_WAIT_C_PKT;
+ goto busy;
+ }
+ delta = sc->sc_last_frame_num - td->tt_start_slot;
+ if (delta > DWC_OTG_TT_SLOT_MAX) {
+ /* we missed the service interval */
+ if (td->ep_type != UE_ISOCHRONOUS)
+ td->error_any = 1;
+ goto complete;
+ }
+
+ /* allocate a new channel */
+ if (dwc_otg_host_channel_alloc(sc, td, 0)) {
td->state = DWC_CHAN_ST_WAIT_C_PKT;
- goto tt_wait;
+ goto busy;
}
- /* wait until next slot before trying again */
- td->tt_complete_slot++;
+
+ channel = td->channel;
td->hcsplt |= HCSPLT_COMPSPLT;
td->state = DWC_CHAN_ST_WAIT_C_ANE;
- DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
- (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
- (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+ (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
- DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
hcchar = td->hcchar;
hcchar &= ~HCCHAR_EPDIR_IN;
- /* must enable channel before writing data to FIFO */
- DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
- goto busy;
+ /* receive complete split ASAP */
+ if ((sc->sc_last_frame_num & 1) != 0)
+ hcchar |= HCCHAR_ODDFRM;
+ else
+ hcchar &= ~HCCHAR_ODDFRM;
+
+ /* must enable channel before data can be received */
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
-tt_wait:
- /* free allocated channel */
- dwc_otg_host_channel_free(td);
+ /* wait until next slot before trying complete split */
+ td->tt_complete_slot = sc->sc_last_frame_num + 1;
busy:
return (1); /* busy */
+
complete:
+ dwc_otg_host_channel_free(sc, td);
return (0); /* complete */
}
static uint8_t
-dwc_otg_data_tx(struct dwc_otg_td *td)
+dwc_otg_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
uint32_t max_buffer;
uint32_t count;
uint32_t fifo_left;
@@ -1997,9 +1973,6 @@ dwc_otg_data_tx(struct dwc_otg_td *td)
to = 3; /* don't loop forever! */
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
max_buffer = sc->sc_hw_ep_profile[td->ep_no].max_buffer;
repeat:
@@ -2177,14 +2150,10 @@ not_complete:
}
static uint8_t
-dwc_otg_data_tx_sync(struct dwc_otg_td *td)
+dwc_otg_data_tx_sync(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
{
- struct dwc_otg_softc *sc;
uint32_t temp;
- /* get pointer to softc */
- sc = DWC_OTG_PC2SC(td->pc);
-
/*
* If all packets are transferred we are complete:
*/
@@ -2221,30 +2190,22 @@ not_complete:
return (1); /* not complete */
}
-static uint8_t
-dwc_otg_xfer_do_fifo(struct usb_xfer *xfer)
+static void
+dwc_otg_xfer_do_fifo(struct dwc_otg_softc *sc, struct usb_xfer *xfer)
{
struct dwc_otg_td *td;
uint8_t toggle;
- uint8_t channel;
uint8_t tmr_val;
uint8_t tmr_res;
DPRINTFN(9, "\n");
td = xfer->td_transfer_cache;
+ if (td == NULL)
+ return;
- /*
- * If we are suspended in host mode and no channel is
- * allocated, simply return:
- */
- if (xfer->xroot->udev->flags.self_suspended != 0 &&
- xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST &&
- td->channel >= DWC_OTG_MAX_CHANNELS) {
- return (1); /* not complete */
- }
while (1) {
- if ((td->func) (td)) {
+ if ((td->func) (sc, td)) {
/* operation in progress */
break;
}
@@ -2269,21 +2230,33 @@ dwc_otg_xfer_do_fifo(struct usb_xfer *xfer)
tmr_res = td->tmr_res;
tmr_val = td->tmr_val;
toggle = td->toggle;
- channel = td->channel;
td = td->obj_next;
xfer->td_transfer_cache = td;
td->toggle = toggle; /* transfer toggle */
- td->channel = channel; /* transfer channel */
td->tmr_res = tmr_res;
td->tmr_val = tmr_val;
}
- return (1); /* not complete */
+ return;
done:
- /* compute all actual lengths */
+ xfer->td_transfer_cache = NULL;
+ sc->sc_xfer_complete = 1;
+}
- dwc_otg_standard_done(xfer);
- return (0); /* complete */
+static uint8_t
+dwc_otg_xfer_do_complete_locked(struct dwc_otg_softc *sc, struct usb_xfer *xfer)
+{
+ struct dwc_otg_td *td;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+ if (td == NULL) {
+ /* compute all actual lengths */
+ dwc_otg_standard_done(xfer);
+ return (1);
+ }
+ return (0);
}
static void
@@ -2297,18 +2270,24 @@ dwc_otg_timer(void *_sc)
DPRINTF("\n");
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
/* increment timer value */
sc->sc_tmr_val++;
TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
td = xfer->td_transfer_cache;
- if (td != NULL)
+ if (td != NULL) {
+ /* reset NAK counter */
td->did_nak = 0;
+ }
}
/* enable SOF interrupt, which will poll jobs */
dwc_otg_enable_sof_irq(sc);
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+
if (sc->sc_timer_active) {
/* restart timer */
usb_callout_reset(&sc->sc_timer,
@@ -2344,239 +2323,249 @@ dwc_otg_timer_stop(struct dwc_otg_softc *sc)
}
static void
-dwc_otg_update_host_transfer_schedule(struct dwc_otg_softc *sc)
+dwc_otg_host_channel_disable(struct dwc_otg_softc *sc, uint8_t x)
+{
+ uint32_t hcchar;
+
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
+
+ /* disable host channel, if any */
+ if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) {
+ /* disable channel */
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
+ HCCHAR_CHENA | HCCHAR_CHDIS);
+ /* wait for chip to get its brains in order */
+ sc->sc_chan_state[x].wait_sof = 2;
+ }
+
+ /* release TX FIFO usage, if any */
+ sc->sc_tx_cur_p_level -= sc->sc_chan_state[x].tx_p_size;
+ sc->sc_tx_cur_np_level -= sc->sc_chan_state[x].tx_np_size;
+
+ /* don't release TX FIFO usage twice */
+ sc->sc_chan_state[x].tx_p_size = 0;
+ sc->sc_chan_state[x].tx_np_size = 0;
+}
+
+static uint16_t
+dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo)
+{
+ if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX)
+ pinfo->slot_index++;
+ return (pinfo->slot_index);
+}
+
+static uint8_t
+dwc_otg_update_host_transfer_schedule_locked(struct dwc_otg_softc *sc)
{
TAILQ_HEAD(, usb_xfer) head;
struct usb_xfer *xfer;
struct usb_xfer *xfer_next;
struct dwc_otg_td *td;
- uint8_t needsof;
uint16_t temp;
+ uint16_t slot;
+ uint8_t x;
- /* FS/LS TT frames are one behind, so add one here */
- temp = (DWC_OTG_READ_4(sc, DOTG_HFNUM) + 1) & HFNUM_FRNUM_MASK;
+ temp = DWC_OTG_READ_4(sc, DOTG_HFNUM) & DWC_OTG_FRAME_MASK;
if (sc->sc_last_frame_num == temp)
- return;
+ return (0);
sc->sc_last_frame_num = temp;
- needsof = 0;
-
TAILQ_INIT(&head);
- if (sc->sc_irq_mask & GINTMSK_SOFMSK) {
- uint8_t x;
+ for (x = 0; x != sc->sc_host_ch_max; x++) {
+ if (sc->sc_chan_state[x].wait_sof == 0)
+ continue;
- for (x = 0; x != sc->sc_host_ch_max; x++) {
- if (sc->sc_chan_state[x].wait_sof != 0) {
- if (--(sc->sc_chan_state[x].wait_sof) != 0)
- needsof = 1;
- }
- }
+ sc->sc_needsof = 1;
+ if (--(sc->sc_chan_state[x].wait_sof) == 0)
+ dwc_otg_host_channel_disable(sc, x);
}
if ((temp & 7) == 0) {
- /* reset TT info */
+ /* reset the schedule */
memset(sc->sc_tt_info, 0, sizeof(sc->sc_tt_info));
- /*
- * Plan ahead FULL speed transfers going through the
- * transaction translator, according to the USB
- * specified priority:
- */
TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
- struct dwc_otg_tt_info *pinfo;
-
td = xfer->td_transfer_cache;
- if (td == NULL || td->did_nak != 0 ||
- (td->hcchar & HCCHAR_EPTYPE_MASK) !=
- (UE_CONTROL << HCCHAR_EPTYPE_SHIFT))
+ if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
continue;
- needsof = 1;
-
- if (td->hcsplt == 0)
+ /* check for IN direction */
+ if ((td->hcchar & HCCHAR_EPDIR_IN) != 0)
continue;
- /* Reset state if stuck waiting for complete split */
- if (td->state == DWC_CHAN_ST_WAIT_C_PKT)
- td->state = DWC_CHAN_ST_START;
+ /* execute more frames */
+ td->tmr_val = 0;
- pinfo = sc->sc_tt_info + td->tt_index;
+ sc->sc_needsof = 1;
- td->tt_start_slot = pinfo->slot_index;
- pinfo->bytes_used += td->max_packet_size;
- while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) {
- pinfo->bytes_used -= HCSPLT_XACTLEN_MAX;
- pinfo->slot_index ++;
- }
+ if (td->hcsplt == 0 || td->tt_scheduled != 0)
+ continue;
- td->tt_complete_slot = pinfo->slot_index + 2;
- if (td->tt_complete_slot < 8) {
- td->tt_scheduled = 1;
- TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
- TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
- } else {
- td->tt_scheduled = 0;
+ /* compute slot */
+ slot = dwc_otg_compute_isoc_rx_tt_slot(
+ sc->sc_tt_info + td->tt_index);
+ if (slot > 3) {
+ /*
+ * Not enough time to get complete
+ * split executed.
+ */
+ continue;
}
+ /* Delayed start */
+ td->tt_start_slot = temp + slot;
+ td->tt_scheduled = 1;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
}
TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
- struct dwc_otg_tt_info *pinfo;
-
td = xfer->td_transfer_cache;
- if (td == NULL ||
- (td->hcchar & HCCHAR_EPTYPE_MASK) !=
- (UE_ISOCHRONOUS << HCCHAR_EPTYPE_SHIFT))
+ if (td == NULL || td->ep_type != UE_ISOCHRONOUS)
+ continue;
+
+ /* check for OUT direction */
+ if ((td->hcchar & HCCHAR_EPDIR_IN) == 0)
continue;
/* execute more frames */
td->tmr_val = 0;
- needsof = 1;
+ sc->sc_needsof = 1;
- if (td->hcsplt == 0)
+ if (td->hcsplt == 0 || td->tt_scheduled != 0)
continue;
- /* Reset state if stuck waiting for complete split */
- if (td->state == DWC_CHAN_ST_WAIT_C_PKT)
- td->state = DWC_CHAN_ST_START;
-
- pinfo = sc->sc_tt_info + td->tt_index;
-
- td->tt_start_slot = pinfo->slot_index;
- pinfo->bytes_used += td->remainder;
- while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) {
- pinfo->bytes_used -= HCSPLT_XACTLEN_MAX;
- pinfo->slot_index ++;
- }
-
- td->tt_complete_slot = pinfo->slot_index + 2;
- if (td->tt_complete_slot < 8) {
- td->tt_scheduled = 1;
- TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
- TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
- } else {
- td->tt_scheduled = 0;
- }
+ /* Start ASAP */
+ td->tt_start_slot = temp;
+ td->tt_scheduled = 1;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
}
TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
- struct dwc_otg_tt_info *pinfo;
-
td = xfer->td_transfer_cache;
- if (td == NULL ||
- (td->hcchar & HCCHAR_EPTYPE_MASK) !=
- (UE_INTERRUPT << HCCHAR_EPTYPE_SHIFT)) {
+ if (td == NULL || td->ep_type != UE_INTERRUPT)
+ continue;
+
+ if (td->tt_scheduled != 0) {
+ sc->sc_needsof = 1;
continue;
}
if (dwc_otg_host_rate_check_interrupt(sc, td))
continue;
- needsof = 1;
-
if (td->hcsplt == 0) {
+ sc->sc_needsof = 1;
td->tt_scheduled = 1;
continue;
}
- /* Reset state if stuck waiting for complete split */
- if (td->state == DWC_CHAN_ST_WAIT_C_PKT)
- td->state = DWC_CHAN_ST_START;
-
- pinfo = sc->sc_tt_info + td->tt_index;
+ /* start ASAP */
+ td->tt_start_slot = temp;
+ sc->sc_needsof = 1;
+ td->tt_scheduled = 1;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+ }
- td->tt_start_slot = pinfo->slot_index;
- pinfo->bytes_used += td->remainder;
- while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) {
- pinfo->bytes_used -= HCSPLT_XACTLEN_MAX;
- pinfo->slot_index ++;
+ TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
+ td = xfer->td_transfer_cache;
+ if (td == NULL ||
+ td->ep_type != UE_CONTROL ||
+ td->did_nak >= DWC_OTG_NAK_MAX) {
+ continue;
}
- td->tt_complete_slot = pinfo->slot_index + 2;
- if (td->tt_complete_slot < 8) {
- td->tt_scheduled = 1;
- TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
- TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
- } else {
- td->tt_scheduled = 0;
- }
+ sc->sc_needsof = 1;
+
+ if (td->hcsplt == 0 || td->tt_scheduled != 0)
+ continue;
+
+ /* start ASAP */
+ td->tt_start_slot = temp;
+ td->tt_scheduled = 1;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
}
}
-
if ((temp & 7) < 6) {
TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
- struct dwc_otg_tt_info *pinfo;
-
td = xfer->td_transfer_cache;
- if (td == NULL || td->did_nak != 0 ||
- (td->hcchar & HCCHAR_EPTYPE_MASK) !=
- (UE_BULK << HCCHAR_EPTYPE_SHIFT)) {
+ if (td == NULL ||
+ td->ep_type != UE_BULK ||
+ td->did_nak >= DWC_OTG_NAK_MAX) {
continue;
}
- needsof = 1;
+ sc->sc_needsof = 1;
- if (td->hcsplt == 0)
+ if (td->hcsplt == 0 || td->tt_scheduled != 0)
continue;
- if ((temp & 7) == 0) {
- /* Reset state if stuck waiting for complete split */
- if (td->state == DWC_CHAN_ST_WAIT_C_PKT)
- td->state = DWC_CHAN_ST_START;
- } else if (td->tt_scheduled != 0)
- continue; /* already scheduled */
-
- pinfo = sc->sc_tt_info + td->tt_index;
-
- td->tt_start_slot = pinfo->slot_index;
- pinfo->bytes_used += td->remainder;
- while (pinfo->bytes_used >= HCSPLT_XACTLEN_MAX) {
- pinfo->bytes_used -= HCSPLT_XACTLEN_MAX;
- pinfo->slot_index ++;
- }
-
- td->tt_complete_slot = pinfo->slot_index + 2;
- if (td->tt_complete_slot < 8) {
- td->tt_scheduled = 1;
- TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
- TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
- } else {
- td->tt_scheduled = 0;
- }
+ /* start ASAP */
+ td->tt_start_slot = temp;
+ td->tt_scheduled = 1;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
}
}
- /* TT transfers need to be executed in a specific order */
+ /* Put TT transfers in execution order at the end */
+ TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
+
+ /* move all TT transfers in front, keeping the current order */
+ TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
+ td = xfer->td_transfer_cache;
+ if (td == NULL || td->hcsplt == 0)
+ continue;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+ }
TAILQ_CONCAT(&head, &sc->sc_bus.intr_q.head, wait_entry);
+ TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
- /* Put TT transfers first in the queue */
+ /* put non-TT BULK transfers last */
+ TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, xfer_next) {
+ td = xfer->td_transfer_cache;
+ if (td == NULL || td->hcsplt != 0 || td->ep_type != UE_BULK)
+ continue;
+ TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
+ TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
+ }
TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
if ((temp & 7) == 0) {
+
DPRINTFN(12, "SOF interrupt #%d, needsof=%d\n",
- (int)temp, (int)needsof);
+ (int)temp, (int)sc->sc_needsof);
/* update SOF IRQ mask */
if (sc->sc_irq_mask & GINTMSK_SOFMSK) {
- if (needsof == 0) {
+ if (sc->sc_needsof == 0) {
sc->sc_irq_mask &= ~GINTMSK_SOFMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
}
} else {
- if (needsof != 0) {
+ if (sc->sc_needsof != 0) {
sc->sc_irq_mask |= GINTMSK_SOFMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
}
}
+
+ /* clear need SOF flag */
+ sc->sc_needsof = 0;
}
+ return (1);
}
static void
-dwc_otg_interrupt_poll(struct dwc_otg_softc *sc)
+dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc)
{
struct usb_xfer *xfer;
uint32_t temp;
@@ -2661,26 +2650,9 @@ repeat:
got_rx_status = 1;
}
- /* scan for completion events first */
- TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
- if (!dwc_otg_xfer_do_fifo(xfer)) {
- /* queue has been modified */
- goto repeat;
- }
- }
-
- if (sc->sc_flags.status_device_mode == 0) {
- /* update host transfer schedule, so that new transfers can be issued */
- dwc_otg_update_host_transfer_schedule(sc);
-
- /* start re-scheduled transfers */
- TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
- if (!dwc_otg_xfer_do_fifo(xfer)) {
- /* queue has been modified */
- goto repeat;
- }
- }
- }
+ /* execute FIFOs */
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry)
+ dwc_otg_xfer_do_fifo(sc, xfer);
if (got_rx_status) {
/* check if data was consumed */
@@ -2691,6 +2663,24 @@ repeat:
sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
}
+
+ if (sc->sc_flags.status_device_mode == 0 && sc->sc_xfer_complete == 0) {
+ /* update host transfer schedule, so that new transfers can be issued */
+ if (dwc_otg_update_host_transfer_schedule_locked(sc))
+ goto repeat;
+ }
+}
+
+static void
+dwc_otg_interrupt_complete_locked(struct dwc_otg_softc *sc)
+{
+ struct usb_xfer *xfer;
+repeat:
+ /* scan for completion events */
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (dwc_otg_xfer_do_complete_locked(sc, xfer))
+ goto repeat;
+ }
}
static void
@@ -2726,16 +2716,64 @@ dwc_otg_vbus_interrupt(struct dwc_otg_softc *sc, uint8_t is_on)
}
}
+int
+dwc_otg_filter_interrupt(void *arg)
+{
+ struct dwc_otg_softc *sc = arg;
+ int retval = FILTER_HANDLED;
+ uint32_t status;
+
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ /* read and clear interrupt status */
+ status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
+
+ /* clear interrupts we are handling here */
+ DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & ~DWC_OTG_MSK_GINT_THREAD_IRQ);
+
+ /* check for USB state change interrupts */
+ if ((status & DWC_OTG_MSK_GINT_THREAD_IRQ) != 0)
+ retval = FILTER_SCHEDULE_THREAD;
+
+ /* clear all IN endpoint interrupts */
+ if (status & GINTSTS_IEPINT) {
+ uint32_t temp;
+ uint8_t x;
+
+ for (x = 0; x != sc->sc_dev_in_ep_max; x++) {
+ temp = DWC_OTG_READ_4(sc, DOTG_DIEPINT(x));
+ if (temp & DIEPMSK_XFERCOMPLMSK) {
+ DWC_OTG_WRITE_4(sc, DOTG_DIEPINT(x),
+ DIEPMSK_XFERCOMPLMSK);
+ }
+ }
+ }
+
+ /* poll FIFOs, if any */
+ dwc_otg_interrupt_poll_locked(sc);
+
+ if (sc->sc_xfer_complete != 0)
+ retval = FILTER_SCHEDULE_THREAD;
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+
+ return (retval);
+}
+
void
-dwc_otg_interrupt(struct dwc_otg_softc *sc)
+dwc_otg_interrupt(void *arg)
{
+ struct dwc_otg_softc *sc = arg;
uint32_t status;
USB_BUS_LOCK(&sc->sc_bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
/* read and clear interrupt status */
status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
- DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status);
+
+ /* clear interrupts we are handling here */
+ DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status & DWC_OTG_MSK_GINT_THREAD_IRQ);
DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n",
status, DWC_OTG_READ_4(sc, DOTG_HAINT),
@@ -2752,8 +2790,6 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc)
/* Disable SOF interrupt */
sc->sc_irq_mask &= ~GINTMSK_SOFMSK;
- /* Enable RX frame interrupt */
- sc->sc_irq_mask |= GINTMSK_RXFLVLMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
/* complete root HUB interrupt endpoint */
@@ -2777,7 +2813,7 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc)
sc->sc_flags.port_enabled = 1;
/* reset FIFOs */
- dwc_otg_init_fifo(sc, DWC_MODE_DEVICE);
+ (void) dwc_otg_init_fifo(sc, DWC_MODE_DEVICE);
/* reset function address */
dwc_otg_set_address(sc, 0);
@@ -2794,7 +2830,7 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc)
* suspend and RX frame interrupt:
*/
sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK);
- sc->sc_irq_mask |= (GINTMSK_USBSUSPMSK | GINTMSK_RXFLVLMSK);
+ sc->sc_irq_mask |= GINTMSK_USBSUSPMSK;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
/* complete root HUB interrupt endpoint */
@@ -2865,10 +2901,6 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc)
/* complete root HUB interrupt endpoint */
dwc_otg_root_intr(sc);
- /* disable RX FIFO level interrupt */
- sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK;
- DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
-
/* update host frame interval */
dwc_otg_update_host_frame_interval(sc);
}
@@ -2905,23 +2937,19 @@ dwc_otg_interrupt(struct dwc_otg_softc *sc)
(temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0);
}
- /* clear all IN endpoint interrupts */
- if (status & GINTSTS_IEPINT) {
- uint32_t temp;
- uint8_t x;
+ if (sc->sc_xfer_complete != 0) {
+ sc->sc_xfer_complete = 0;
- for (x = 0; x != sc->sc_dev_in_ep_max; x++) {
- temp = DWC_OTG_READ_4(sc, DOTG_DIEPINT(x));
- if (temp & DIEPMSK_XFERCOMPLMSK) {
- DWC_OTG_WRITE_4(sc, DOTG_DIEPINT(x),
- DIEPMSK_XFERCOMPLMSK);
- }
+ /* complete FIFOs, if any */
+ dwc_otg_interrupt_complete_locked(sc);
+
+ if (sc->sc_flags.status_device_mode == 0) {
+ /* update host transfer schedule, so that new transfers can be issued */
+ if (dwc_otg_update_host_transfer_schedule_locked(sc))
+ dwc_otg_interrupt_poll_locked(sc);
}
}
-
- /* poll FIFO(s) */
- dwc_otg_interrupt_poll(sc);
-
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
USB_BUS_UNLOCK(&sc->sc_bus);
}
@@ -2956,7 +2984,6 @@ dwc_otg_setup_standard_chain_sub(struct dwc_otg_std_temp *temp)
td->state = 0;
td->errcnt = 0;
td->tt_scheduled = 0;
- td->tt_index = temp->tt_index;
td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
}
@@ -2965,7 +2992,6 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
{
struct dwc_otg_std_temp temp;
struct dwc_otg_td *td;
- struct usb_device *udev;
uint32_t x;
uint8_t need_sync;
uint8_t is_host;
@@ -2976,16 +3002,6 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
temp.max_frame_size = xfer->max_frame_size;
- udev = xfer->xroot->udev;
- if (udev->parent_hs_hub != NULL && udev->speed != USB_SPEED_HIGH) {
- if (udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT)
- temp.tt_index = udev->device_index;
- else
- temp.tt_index = udev->parent_hs_hub->device_index;
- } else {
- temp.tt_index = udev->device_index;
- }
-
td = xfer->td_start[0];
xfer->td_transfer_first = td;
xfer->td_transfer_cache = td;
@@ -3170,10 +3186,8 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
struct dwc_otg_softc *sc;
uint32_t hcchar;
uint32_t hcsplt;
- uint8_t xfer_type;
sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
- xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
/* get first again */
td = xfer->td_transfer_first;
@@ -3181,11 +3195,28 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
hcchar =
(xfer->address << HCCHAR_DEVADDR_SHIFT) |
- (xfer_type << HCCHAR_EPTYPE_SHIFT) |
((xfer->endpointno & UE_ADDR) << HCCHAR_EPNUM_SHIFT) |
(xfer->max_packet_size << HCCHAR_MPS_SHIFT) |
HCCHAR_CHENA;
+ /*
+ * We are not always able to meet the timing
+ * requirements of the USB interrupt endpoint's
+ * complete split token, when doing transfers going
+ * via a transaction translator. Use the CONTROL
+ * transfer type instead of the INTERRUPT transfer
+ * type in general, as a means to workaround
+ * that. This trick should work for both FULL and LOW
+ * speed USB traffic going through a TT. For non-TT
+ * traffic it works aswell. The reason for using
+ * CONTROL type instead of BULK is that some TTs might
+ * reject LOW speed BULK traffic.
+ */
+ if (td->ep_type == UE_INTERRUPT)
+ hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT);
+ else
+ hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT);
+
if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_LOW)
hcchar |= HCCHAR_LSPDDEV;
if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN)
@@ -3204,7 +3235,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
} else {
hcsplt = 0;
}
- if (xfer_type == UE_INTERRUPT) {
+ if (td->ep_type == UE_INTERRUPT) {
uint32_t ival;
ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
if (ival == 0)
@@ -3213,7 +3244,7 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
ival = 127;
td->tmr_val = sc->sc_tmr_val + ival;
td->tmr_res = ival;
- } else if (xfer_type == UE_ISOCHRONOUS) {
+ } else if (td->ep_type == UE_ISOCHRONOUS) {
td->tmr_val = 0;
td->tmr_res = 1;
} else {
@@ -3223,13 +3254,12 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
break;
case USB_SPEED_HIGH:
hcsplt = 0;
- if (xfer_type == UE_ISOCHRONOUS ||
- xfer_type == UE_INTERRUPT) {
+ if (td->ep_type == UE_INTERRUPT) {
+ uint32_t ival;
+#if 0
hcchar |= ((xfer->max_packet_count & 3)
<< HCCHAR_MC_SHIFT);
- }
- if (xfer_type == UE_INTERRUPT) {
- uint32_t ival;
+#endif
ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
if (ival == 0)
ival = 1;
@@ -3237,7 +3267,9 @@ dwc_otg_setup_standard_chain(struct usb_xfer *xfer)
ival = 127;
td->tmr_val = sc->sc_tmr_val + ival;
td->tmr_res = ival;
- } else if (xfer_type == UE_ISOCHRONOUS) {
+ } else if (td->ep_type == UE_ISOCHRONOUS) {
+ hcchar |= ((xfer->max_packet_count & 3)
+ << HCCHAR_MC_SHIFT);
td->tmr_val = 0;
td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer);
} else {
@@ -3282,24 +3314,58 @@ static void
dwc_otg_start_standard_chain(struct usb_xfer *xfer)
{
struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
+ struct usb_xfer_root *xroot;
+ struct dwc_otg_td *td;
DPRINTFN(9, "\n");
- /* poll one time - will turn on interrupts */
- if (dwc_otg_xfer_do_fifo(xfer)) {
+ /*
+ * Poll one time in device mode, which will turn on the
+ * endpoint interrupts. Else wait for SOF interrupt in host
+ * mode.
+ */
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
- /* put transfer on interrupt queue */
- usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+ if (sc->sc_flags.status_device_mode != 0) {
+ dwc_otg_xfer_do_fifo(sc, xfer);
+ if (dwc_otg_xfer_do_complete_locked(sc, xfer))
+ goto done;
+ }
- /* start timeout, if any */
- if (xfer->timeout != 0) {
- usbd_transfer_timeout_ms(xfer,
- &dwc_otg_timeout, xfer->timeout);
- }
+ /* put transfer on interrupt queue */
+ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
- /* enable SOF interrupt, if any */
- dwc_otg_enable_sof_irq(sc);
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usbd_transfer_timeout_ms(xfer,
+ &dwc_otg_timeout, xfer->timeout);
}
+
+ if (sc->sc_flags.status_device_mode != 0)
+ goto done;
+
+ /* enable SOF interrupt, if any */
+ dwc_otg_enable_sof_irq(sc);
+
+ td = xfer->td_transfer_cache;
+ if (td->ep_type != UE_BULK)
+ goto done;
+
+ xroot = xfer->xroot;
+
+ /*
+ * Optimise the ping-pong effect by waking up other BULK
+ * transfers belonging to the same device group:
+ */
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ td = xfer->td_transfer_cache;
+ if (td == NULL || td->ep_type != UE_BULK || xfer->xroot != xroot)
+ continue;
+ /* reset NAK counter */
+ td->did_nak = 0;
+ }
+done:
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
}
static void
@@ -3436,21 +3502,26 @@ done:
static void
dwc_otg_device_done(struct usb_xfer *xfer, usb_error_t error)
{
+ struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
+
DPRINTFN(9, "xfer=%p, endpoint=%p, error=%d\n",
xfer, xfer->endpoint, error);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
- DPRINTFN(15, "disabled interrupts!\n");
+ /* Interrupts are cleared by the interrupt handler */
} else {
struct dwc_otg_td *td;
- td = xfer->td_transfer_first;
-
- if (td != NULL)
- dwc_otg_host_channel_free(td);
+ td = xfer->td_transfer_cache;
+ if (td != NULL)
+ dwc_otg_host_channel_free(sc, td);
}
/* dequeue transfer and start next transfer */
usbd_transfer_done(xfer, error);
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
}
static void
@@ -3478,6 +3549,8 @@ dwc_otg_set_stall(struct usb_device *udev,
sc = DWC_OTG_BUS2SC(udev->bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
/* get endpoint address */
ep_no = ep->edesc->bEndpointAddress;
@@ -3506,13 +3579,15 @@ dwc_otg_set_stall(struct usb_device *udev,
/* dump data */
dwc_otg_common_rx_ack(sc);
/* poll interrupt */
- dwc_otg_interrupt_poll(sc);
+ dwc_otg_interrupt_poll_locked(sc);
+ dwc_otg_interrupt_complete_locked(sc);
}
}
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
}
static void
-dwc_otg_clear_stall_sub(struct dwc_otg_softc *sc, uint32_t mps,
+dwc_otg_clear_stall_sub_locked(struct dwc_otg_softc *sc, uint32_t mps,
uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
{
uint32_t reg;
@@ -3570,7 +3645,8 @@ dwc_otg_clear_stall_sub(struct dwc_otg_softc *sc, uint32_t mps,
}
/* poll interrupt */
- dwc_otg_interrupt_poll(sc);
+ dwc_otg_interrupt_poll_locked(sc);
+ dwc_otg_interrupt_complete_locked(sc);
}
static void
@@ -3591,15 +3667,19 @@ dwc_otg_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
/* get softc */
sc = DWC_OTG_BUS2SC(udev->bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
/* get endpoint descriptor */
ed = ep->edesc;
/* reset endpoint */
- dwc_otg_clear_stall_sub(sc,
+ dwc_otg_clear_stall_sub_locked(sc,
UGETW(ed->wMaxPacketSize),
(ed->bEndpointAddress & UE_ADDR),
(ed->bmAttributes & UE_XFERTYPE),
(ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
}
static void
@@ -3755,8 +3835,10 @@ dwc_otg_init(struct dwc_otg_softc *sc)
sc->sc_host_ch_max);
/* setup FIFO */
- if (dwc_otg_init_fifo(sc, sc->sc_mode))
+ if (dwc_otg_init_fifo(sc, sc->sc_mode)) {
+ USB_BUS_UNLOCK(&sc->sc_bus);
return (EINVAL);
+ }
/* enable interrupts */
sc->sc_irq_mask = DWC_OTG_MSK_GINT_ENABLED;
@@ -3867,7 +3949,15 @@ dwc_otg_do_poll(struct usb_bus *bus)
struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(bus);
USB_BUS_LOCK(&sc->sc_bus);
- dwc_otg_interrupt_poll(sc);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+ dwc_otg_interrupt_poll_locked(sc);
+ dwc_otg_interrupt_complete_locked(sc);
+ if (sc->sc_flags.status_device_mode == 0) {
+ /* update host transfer schedule, so that new transfers can be issued */
+ if (dwc_otg_update_host_transfer_schedule_locked(sc))
+ dwc_otg_interrupt_poll_locked(sc);
+ }
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
USB_BUS_UNLOCK(&sc->sc_bus);
}
@@ -4445,7 +4535,7 @@ tr_handle_set_port_feature:
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16);
/* reset FIFOs */
- dwc_otg_init_fifo(sc, DWC_MODE_HOST);
+ (void) dwc_otg_init_fifo(sc, DWC_MODE_HOST);
sc->sc_flags.change_reset = 1;
} else {
@@ -4555,6 +4645,7 @@ dwc_otg_xfer_setup(struct usb_setup_params *parm)
uint32_t ntd;
uint32_t n;
uint8_t ep_no;
+ uint8_t ep_type;
xfer = parm->curr_xfer;
@@ -4572,7 +4663,9 @@ dwc_otg_xfer_setup(struct usb_setup_params *parm)
/*
* compute maximum number of TDs
*/
- if ((xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) {
+ ep_type = (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE);
+
+ if (ep_type == UE_CONTROL) {
ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */
+ 1 /* SYNC 2 */ + 1 /* SYNC 3 */;
@@ -4620,10 +4713,21 @@ dwc_otg_xfer_setup(struct usb_setup_params *parm)
td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+ /* compute shared bandwidth resource index for TT */
+ if (parm->udev->parent_hs_hub != NULL && parm->udev->speed != USB_SPEED_HIGH) {
+ if (parm->udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT)
+ td->tt_index = parm->udev->device_index;
+ else
+ td->tt_index = parm->udev->parent_hs_hub->device_index;
+ } else {
+ td->tt_index = parm->udev->device_index;
+ }
+
/* init TD */
td->max_packet_size = xfer->max_packet_size;
td->max_packet_count = xfer->max_packet_count;
td->ep_no = ep_no;
+ td->ep_type = ep_type;
td->obj_next = last_obj;
last_obj = td;
@@ -4659,6 +4763,14 @@ dwc_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
/* not supported */
return;
}
+ } else {
+ if (udev->speed == USB_SPEED_HIGH) {
+ if ((UGETW(edesc->wMaxPacketSize) >> 11) & 3) {
+ /* high bandwidth endpoint - not tested */
+ DPRINTF("High Bandwidth Endpoint - not tested\n");
+ return;
+ }
+ }
}
if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS)
ep->methods = &dwc_otg_device_isoc_methods;
@@ -4697,29 +4809,8 @@ dwc_otg_get_dma_delay(struct usb_device *udev, uint32_t *pus)
static void
dwc_otg_device_resume(struct usb_device *udev)
{
- struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus);
- struct usb_xfer *xfer;
- struct dwc_otg_td *td;
-
DPRINTF("\n");
- /* Enable relevant Host channels before resuming */
-
- USB_BUS_LOCK(udev->bus);
-
- TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
-
- if (xfer->xroot->udev == udev) {
-
- td = xfer->td_transfer_cache;
- if (td != NULL &&
- td->channel < DWC_OTG_MAX_CHANNELS)
- sc->sc_chan_state[td->channel].suspended = 0;
- }
- }
-
- USB_BUS_UNLOCK(udev->bus);
-
/* poll all transfers again to restart resumed ones */
dwc_otg_do_poll(udev->bus);
}
@@ -4727,28 +4818,7 @@ dwc_otg_device_resume(struct usb_device *udev)
static void
dwc_otg_device_suspend(struct usb_device *udev)
{
- struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(udev->bus);
- struct usb_xfer *xfer;
- struct dwc_otg_td *td;
-
DPRINTF("\n");
-
- /* Disable relevant Host channels before going to suspend */
-
- USB_BUS_LOCK(udev->bus);
-
- TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
-
- if (xfer->xroot->udev == udev) {
-
- td = xfer->td_transfer_cache;
- if (td != NULL &&
- td->channel < DWC_OTG_MAX_CHANNELS)
- sc->sc_chan_state[td->channel].suspended = 1;
- }
- }
-
- USB_BUS_UNLOCK(udev->bus);
}
static const struct usb_bus_methods dwc_otg_bus_methods =
diff --git a/sys/dev/usb/controller/dwc_otg.h b/sys/dev/usb/controller/dwc_otg.h
index 2ae2eaeb60141..1fa1bbf670511 100644
--- a/sys/dev/usb/controller/dwc_otg.h
+++ b/sys/dev/usb/controller/dwc_otg.h
@@ -34,6 +34,10 @@
#define DWC_OTG_MAX_CHANNELS 16
#define DWC_OTG_MAX_ENDPOINTS 16
#define DWC_OTG_HOST_TIMER_RATE 10 /* ms */
+#define DWC_OTG_TT_SLOT_MAX 8
+#define DWC_OTG_SLOT_IDLE_MAX 3
+#define DWC_OTG_SLOT_IDLE_MIN 2
+#define DWC_OTG_NAK_MAX 8 /* 1 ms */
#define DWC_OTG_READ_4(sc, reg) \
bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
@@ -44,7 +48,7 @@
struct dwc_otg_td;
struct dwc_otg_softc;
-typedef uint8_t (dwc_otg_cmd_t)(struct dwc_otg_td *td);
+typedef uint8_t (dwc_otg_cmd_t)(struct dwc_otg_softc *sc, struct dwc_otg_td *td);
struct dwc_otg_td {
struct dwc_otg_td *obj_next;
@@ -61,7 +65,9 @@ struct dwc_otg_td {
uint8_t errcnt;
uint8_t tmr_res;
uint8_t tmr_val;
+ uint8_t did_nak; /* NAK counter */
uint8_t ep_no;
+ uint8_t ep_type;
uint8_t channel;
uint8_t tt_index; /* TT data */
uint8_t tt_start_slot; /* TT data */
@@ -75,7 +81,6 @@ struct dwc_otg_td {
#define DWC_CHAN_ST_WAIT_C_PKT 4
#define DWC_CHAN_ST_TX_PKT_ISOC 5
#define DWC_CHAN_ST_TX_WAIT_ISOC 6
- uint8_t error:1;
uint8_t error_any:1;
uint8_t error_stall:1;
uint8_t alt_next:1;
@@ -84,10 +89,13 @@ struct dwc_otg_td {
uint8_t toggle:1;
uint8_t set_toggle:1;
uint8_t got_short:1;
- uint8_t did_nak:1;
uint8_t tt_scheduled:1;
};
+struct dwc_otg_tt_info {
+ uint8_t slot_index;
+};
+
struct dwc_otg_std_temp {
dwc_otg_cmd_t *func;
struct usb_page_cache *pc;
@@ -105,7 +113,6 @@ struct dwc_otg_std_temp {
uint8_t setup_alt_next;
uint8_t did_stall;
uint8_t bulk_or_control;
- uint8_t tt_index;
};
struct dwc_otg_config_desc {
@@ -145,23 +152,18 @@ struct dwc_otg_profile {
};
struct dwc_otg_chan_state {
+ uint16_t allocated;
+ uint16_t wait_sof;
uint32_t hcint;
- uint32_t tx_size;
- uint8_t wait_sof;
- uint8_t allocated;
- uint8_t suspended;
-};
-
-struct dwc_otg_tt_info {
- uint16_t bytes_used;
- uint8_t slot_index;
- uint8_t dummy;
+ uint16_t tx_p_size; /* periodic */
+ uint16_t tx_np_size; /* non-periodic */
};
struct dwc_otg_softc {
struct usb_bus sc_bus;
union dwc_otg_hub_temp sc_hub_temp;
struct dwc_otg_profile sc_hw_ep_profile[DWC_OTG_MAX_ENDPOINTS];
+ struct dwc_otg_tt_info sc_tt_info[DWC_OTG_MAX_DEVICES];
struct usb_callout sc_timer;
struct usb_device *sc_devices[DWC_OTG_MAX_DEVICES];
@@ -177,7 +179,8 @@ struct dwc_otg_softc {
uint32_t sc_fifo_size;
uint32_t sc_tx_max_size;
- uint32_t sc_tx_cur_size;
+ uint32_t sc_tx_cur_p_level; /* periodic */
+ uint32_t sc_tx_cur_np_level; /* non-periodic */
uint32_t sc_irq_mask;
uint32_t sc_last_rx_status;
uint32_t sc_out_ctl[DWC_OTG_MAX_ENDPOINTS];
@@ -185,8 +188,8 @@ struct dwc_otg_softc {
struct dwc_otg_chan_state sc_chan_state[DWC_OTG_MAX_CHANNELS];
uint32_t sc_tmr_val;
uint32_t sc_hprt_val;
+ uint32_t sc_xfer_complete;
- struct dwc_otg_tt_info sc_tt_info[DWC_OTG_MAX_DEVICES];
uint16_t sc_active_rx_ep;
uint16_t sc_last_frame_num;
@@ -194,6 +197,7 @@ struct dwc_otg_softc {
uint8_t sc_dev_ep_max;
uint8_t sc_dev_in_ep_max;
uint8_t sc_host_ch_max;
+ uint8_t sc_needsof;
uint8_t sc_rt_addr; /* root HUB address */
uint8_t sc_conf; /* root HUB config */
uint8_t sc_mode; /* mode of operation */
@@ -208,7 +212,8 @@ struct dwc_otg_softc {
/* prototypes */
-void dwc_otg_interrupt(struct dwc_otg_softc *);
+driver_filter_t dwc_otg_filter_interrupt;
+driver_intr_t dwc_otg_interrupt;
int dwc_otg_init(struct dwc_otg_softc *);
void dwc_otg_uninit(struct dwc_otg_softc *);
diff --git a/sys/dev/usb/controller/dwc_otg_atmelarm.c b/sys/dev/usb/controller/dwc_otg_atmelarm.c
deleted file mode 100644
index 9dc072d82a248..0000000000000
--- a/sys/dev/usb/controller/dwc_otg_atmelarm.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*-
- * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/stdint.h>
-#include <sys/stddef.h>
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/types.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/bus.h>
-#include <sys/module.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/condvar.h>
-#include <sys/sysctl.h>
-#include <sys/sx.h>
-#include <sys/unistd.h>
-#include <sys/callout.h>
-#include <sys/malloc.h>
-#include <sys/priv.h>
-#include <sys/rman.h>
-
-#include <dev/usb/usb.h>
-#include <dev/usb/usbdi.h>
-
-#include <dev/usb/usb_core.h>
-#include <dev/usb/usb_busdma.h>
-#include <dev/usb/usb_process.h>
-#include <dev/usb/usb_util.h>
-
-#include <dev/usb/usb_controller.h>
-#include <dev/usb/usb_bus.h>
-
-#include <dev/usb/controller/dwc_otg.h>
-
-static device_probe_t dwc_otg_probe;
-static device_attach_t dwc_otg_attach;
-static device_detach_t dwc_otg_detach;
-
-struct dwc_otg_super_softc {
- struct dwc_otg_softc sc_otg; /* must be first */
-};
-
-static int
-dwc_otg_probe(device_t dev)
-{
- device_set_desc(dev, "DWC OTG 2.0 integrated USB controller");
- return (0);
-}
-
-static int
-dwc_otg_attach(device_t dev)
-{
- struct dwc_otg_super_softc *sc = device_get_softc(dev);
- int err;
- int rid;
-
- /* initialise some bus fields */
- sc->sc_otg.sc_bus.parent = dev;
- sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices;
- sc->sc_otg.sc_bus.devices_max = DWC_OTG_MAX_DEVICES;
-
- /* get all DMA memory */
- if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus,
- USB_GET_DMA_TAG(dev), NULL)) {
- return (ENOMEM);
- }
- rid = 0;
- sc->sc_otg.sc_io_res =
- bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
-
- if (!(sc->sc_otg.sc_io_res)) {
- err = ENOMEM;
- goto error;
- }
- sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res);
- sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res);
- sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res);
-
- rid = 0;
- sc->sc_otg.sc_irq_res =
- bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
- if (sc->sc_otg.sc_irq_res == NULL)
- goto error;
-
- sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1);
- if (sc->sc_otg.sc_bus.bdev == NULL)
- goto error;
-
- device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus);
-
- err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
- NULL, (driver_intr_t *)dwc_otg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
- if (err) {
- sc->sc_otg.sc_intr_hdl = NULL;
- goto error;
- }
- err = dwc_otg_init(&sc->sc_otg);
- if (err == 0) {
- err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev);
- }
- if (err)
- goto error;
- return (0);
-
-error:
- dwc_otg_detach(dev);
- return (ENXIO);
-}
-
-static int
-dwc_otg_detach(device_t dev)
-{
- struct dwc_otg_super_softc *sc = device_get_softc(dev);
- device_t bdev;
- int err;
-
- if (sc->sc_otg.sc_bus.bdev) {
- bdev = sc->sc_otg.sc_bus.bdev;
- device_detach(bdev);
- device_delete_child(dev, bdev);
- }
- /* during module unload there are lots of children leftover */
- device_delete_children(dev);
-
- if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) {
- /*
- * only call dwc_otg_uninit() after dwc_otg_init()
- */
- dwc_otg_uninit(&sc->sc_otg);
-
- err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res,
- sc->sc_otg.sc_intr_hdl);
- sc->sc_otg.sc_intr_hdl = NULL;
- }
- /* free IRQ channel, if any */
- if (sc->sc_otg.sc_irq_res) {
- bus_release_resource(dev, SYS_RES_IRQ, 0,
- sc->sc_otg.sc_irq_res);
- sc->sc_otg.sc_irq_res = NULL;
- }
- /* free memory resource, if any */
- if (sc->sc_otg.sc_io_res) {
- bus_release_resource(dev, SYS_RES_MEMORY, 0,
- sc->sc_otg.sc_io_res);
- sc->sc_otg.sc_io_res = NULL;
- }
- usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL);
-
- return (0);
-}
-
-static device_method_t dwc_otg_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, dwc_otg_probe),
- DEVMETHOD(device_attach, dwc_otg_attach),
- DEVMETHOD(device_detach, dwc_otg_detach),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
-
- DEVMETHOD_END
-};
-
-static driver_t dwc_otg_driver = {
- .name = "dwc_otg",
- .methods = dwc_otg_methods,
- .size = sizeof(struct dwc_otg_super_softc),
-};
-
-static devclass_t dwc_otg_devclass;
-
-DRIVER_MODULE(dwcotg, atmelarm, dwc_otg_driver, dwc_otg_devclass, 0, 0);
-MODULE_DEPEND(dwcotg, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/dwc_otg_fdt.c b/sys/dev/usb/controller/dwc_otg_fdt.c
index 78bb801edf5ba..938ef35546aaa 100644
--- a/sys/dev/usb/controller/dwc_otg_fdt.c
+++ b/sys/dev/usb/controller/dwc_otg_fdt.c
@@ -146,8 +146,8 @@ dwc_otg_attach(device_t dev)
device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus);
- err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
- NULL, (driver_intr_t *)dwc_otg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
+ err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE,
+ &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
if (err) {
sc->sc_otg.sc_intr_hdl = NULL;
goto error;
diff --git a/sys/dev/usb/controller/dwc_otgreg.h b/sys/dev/usb/controller/dwc_otgreg.h
index cd2f45de32a60..344d36a28534f 100644
--- a/sys/dev/usb/controller/dwc_otgreg.h
+++ b/sys/dev/usb/controller/dwc_otgreg.h
@@ -540,7 +540,7 @@
#define HCSPLT_XACTPOS_LAST 1
#define HCSPLT_XACTPOS_BEGIN 2
#define HCSPLT_XACTPOS_ALL 3
-#define HCSPLT_XACTLEN_MAX 188 /* bytes */
+#define HCSPLT_XACTLEN_BURST 1023 /* bytes */
#define HCSPLT_HUBADDR_SHIFT 7
#define HCSPLT_HUBADDR_MASK 0x00003f80
#define HCSPLT_PRTADDR_SHIFT 0
@@ -555,6 +555,9 @@
HCINT_XACTERR | HCINT_NAK | HCINT_ACK | HCINT_NYET | \
HCINT_CHHLTD | HCINT_FRMOVRUN | \
HCINT_DATATGLERR)
+#define HCINT_HCH_DONE_MASK \
+ (HCINT_ACK | HCINT_RETRY | HCINT_NYET | \
+ HCINT_ERRORS | HCINT_STALL | HCINT_SOFTWARE_ONLY)
#define HCINT_SOFTWARE_ONLY (1<<20) /* BSD only */
#define HCINT_DATATGLERR (1<<10)
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
index ad3a3fc55d2b1..7d0d5ab6d2099 100644
--- a/sys/dev/usb/controller/ehci.c
+++ b/sys/dev/usb/controller/ehci.c
@@ -98,19 +98,14 @@ static int ehciiaadbug = 0;
static int ehcilostintrbug = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci");
-SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RWTUN,
&ehcidebug, 0, "Debug level");
-TUNABLE_INT("hw.usb.ehci.debug", &ehcidebug);
-SYSCTL_INT(_hw_usb_ehci, OID_AUTO, no_hs, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_ehci, OID_AUTO, no_hs, CTLFLAG_RWTUN,
&ehcinohighspeed, 0, "Disable High Speed USB");
-TUNABLE_INT("hw.usb.ehci.no_hs", &ehcinohighspeed);
-SYSCTL_INT(_hw_usb_ehci, OID_AUTO, iaadbug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_ehci, OID_AUTO, iaadbug, CTLFLAG_RWTUN,
&ehciiaadbug, 0, "Enable doorbell bug workaround");
-TUNABLE_INT("hw.usb.ehci.iaadbug", &ehciiaadbug);
-SYSCTL_INT(_hw_usb_ehci, OID_AUTO, lostintrbug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_ehci, OID_AUTO, lostintrbug, CTLFLAG_RWTUN,
&ehcilostintrbug, 0, "Enable lost interrupt bug workaround");
-TUNABLE_INT("hw.usb.ehci.lostintrbug", &ehcilostintrbug);
-
static void ehci_dump_regs(ehci_softc_t *sc);
static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh);
diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c
index 39ff96844be7d..89de3d582b6c6 100644
--- a/sys/dev/usb/controller/musb_otg.c
+++ b/sys/dev/usb/controller/musb_otg.c
@@ -131,7 +131,7 @@ static void musbotg_do_poll(struct usb_bus *);
static void musbotg_standard_done(struct usb_xfer *);
static void musbotg_interrupt_poll(struct musbotg_softc *);
static void musbotg_root_intr(struct musbotg_softc *);
-static int musbotg_channel_alloc(struct musbotg_softc *, struct musbotg_td *td);
+static int musbotg_channel_alloc(struct musbotg_softc *, struct musbotg_td *td, uint8_t);
static void musbotg_channel_free(struct musbotg_softc *, struct musbotg_td *td);
static void musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on);
@@ -149,7 +149,7 @@ static const struct usb_hw_ep_profile musbotg_ep_profile[1] = {
};
static int
-musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td)
+musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td, uint8_t is_tx)
{
int ch;
int ep;
@@ -173,12 +173,23 @@ musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td)
return (0);
}
- for (ch = 1; ch < MUSB2_EP_MAX; ch++) {
- if (!(sc->sc_channel_mask & (1 << ch))) {
- sc->sc_channel_mask |= (1 << ch);
- musbotg_ep_int_set(sc, ch, 1);
- return (ch);
+ for (ch = sc->sc_ep_max; ch != 0; ch--) {
+ if (sc->sc_channel_mask & (1 << ch))
+ continue;
+
+ /* check FIFO size requirement */
+ if (is_tx) {
+ if (td->max_frame_size >
+ sc->sc_hw_ep_profile[ch].max_in_frame_size)
+ continue;
+ } else {
+ if (td->max_frame_size >
+ sc->sc_hw_ep_profile[ch].max_out_frame_size)
+ continue;
}
+ sc->sc_channel_mask |= (1 << ch);
+ musbotg_ep_int_set(sc, ch, 1);
+ return (ch);
}
DPRINTFN(-1, "No available channels. Mask: %04x\n", sc->sc_channel_mask);
@@ -377,7 +388,7 @@ musbotg_dev_ctrl_setup_rx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 0);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -498,7 +509,7 @@ musbotg_host_ctrl_setup_tx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 1);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -870,7 +881,7 @@ musbotg_host_ctrl_data_rx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 0);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -1049,7 +1060,7 @@ musbotg_host_ctrl_data_tx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 1);
/* No free EPs */
if (td->channel == -1)
@@ -1259,7 +1270,7 @@ musbotg_host_ctrl_status_rx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 0);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -1346,7 +1357,7 @@ musbotg_host_ctrl_status_tx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 1);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -1419,7 +1430,7 @@ musbotg_dev_data_rx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 0);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -1567,7 +1578,7 @@ musbotg_dev_data_tx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 1);
/* EP0 is busy, wait */
if (td->channel == -1)
@@ -1695,7 +1706,7 @@ musbotg_host_data_rx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 0);
/* No free EPs */
if (td->channel == -1)
@@ -1917,7 +1928,7 @@ musbotg_host_data_tx(struct musbotg_td *td)
sc = MUSBOTG_PC2SC(td->pc);
if (td->channel == -1)
- td->channel = musbotg_channel_alloc(sc, td);
+ td->channel = musbotg_channel_alloc(sc, td, 1);
/* No free EPs */
if (td->channel == -1)
@@ -2403,7 +2414,8 @@ musbotg_setup_standard_chain(struct usb_xfer *xfer)
temp.td = NULL;
temp.td_next = xfer->td_start[0];
temp.offset = 0;
- temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr;
temp.did_stall = !xfer->flags_int.control_stall;
temp.channel = -1;
temp.dev_addr = dev_addr;
@@ -2714,7 +2726,8 @@ musbotg_standard_done_sub(struct usb_xfer *xfer)
}
/* Check for short transfer */
if (len > 0) {
- if (xfer->flags_int.short_frames_ok) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
/* follow alt next */
if (td->alt_next) {
td = td->obj_next;
@@ -3224,6 +3237,7 @@ musbotg_init(struct musbotg_softc *sc)
pf->support_out = 1;
} else if (frx && (temp <= nrx)) {
pf->max_out_frame_size = 1 << frx;
+ pf->max_in_frame_size = 0;
pf->is_simplex = 1; /* simplex */
pf->support_multi_buffer = 1;
pf->support_bulk = 1;
@@ -3232,6 +3246,7 @@ musbotg_init(struct musbotg_softc *sc)
pf->support_out = 1;
} else if (ftx && (temp <= ntx)) {
pf->max_in_frame_size = 1 << ftx;
+ pf->max_out_frame_size = 0;
pf->is_simplex = 1; /* simplex */
pf->support_multi_buffer = 1;
pf->support_bulk = 1;
@@ -3285,18 +3300,6 @@ musbotg_uninit(struct musbotg_softc *sc)
}
static void
-musbotg_suspend(struct musbotg_softc *sc)
-{
- /* TODO */
-}
-
-static void
-musbotg_resume(struct musbotg_softc *sc)
-{
- /* TODO */
-}
-
-static void
musbotg_do_poll(struct usb_bus *bus)
{
struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus);
@@ -4212,13 +4215,13 @@ musbotg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
switch (state) {
case USB_HW_POWER_SUSPEND:
- musbotg_suspend(sc);
+ musbotg_uninit(sc);
break;
case USB_HW_POWER_SHUTDOWN:
musbotg_uninit(sc);
break;
case USB_HW_POWER_RESUME:
- musbotg_resume(sc);
+ musbotg_init(sc);
break;
default:
break;
diff --git a/sys/dev/usb/controller/musb_otg.h b/sys/dev/usb/controller/musb_otg.h
index f8f3470dd9dab..7fe19a7ce6541 100644
--- a/sys/dev/usb/controller/musb_otg.h
+++ b/sys/dev/usb/controller/musb_otg.h
@@ -388,7 +388,7 @@ struct musbotg_flags {
struct musbotg_softc {
struct usb_bus sc_bus;
union musbotg_hub_temp sc_hub_temp;
- struct usb_hw_ep_profile sc_hw_ep_profile[16];
+ struct usb_hw_ep_profile sc_hw_ep_profile[MUSB2_EP_MAX];
struct usb_device *sc_devices[MUSB2_MAX_DEVICES];
struct resource *sc_io_res;
diff --git a/sys/dev/usb/controller/ohci.c b/sys/dev/usb/controller/ohci.c
index 86fca1eeaa3b6..206c75314d0c5 100644
--- a/sys/dev/usb/controller/ohci.c
+++ b/sys/dev/usb/controller/ohci.c
@@ -84,9 +84,8 @@
static int ohcidebug = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci");
-SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RWTUN,
&ohcidebug, 0, "ohci debug level");
-TUNABLE_INT("hw.usb.ohci.debug", &ohcidebug);
static void ohci_dumpregs(ohci_softc_t *);
static void ohci_dump_tds(ohci_td_t *);
diff --git a/sys/dev/usb/controller/saf1761_otg.c b/sys/dev/usb/controller/saf1761_otg.c
new file mode 100644
index 0000000000000..0f5b4741d7531
--- /dev/null
+++ b/sys/dev/usb/controller/saf1761_otg.c
@@ -0,0 +1,3633 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the driver for the SAF1761 series USB OTG
+ * controller.
+ *
+ * Datasheet is available from:
+ * http://www.nxp.com/products/automotive/multimedia/usb/SAF1761BE.html
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define USB_DEBUG_VAR saf1761_otg_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#endif /* USB_GLOBAL_INCLUDE_FILE */
+
+#include <dev/usb/controller/saf1761_otg.h>
+#include <dev/usb/controller/saf1761_otg_reg.h>
+
+#define SAF1761_OTG_BUS2SC(bus) \
+ ((struct saf1761_otg_softc *)(((uint8_t *)(bus)) - \
+ ((uint8_t *)&(((struct saf1761_otg_softc *)0)->sc_bus))))
+
+#define SAF1761_OTG_PC2UDEV(pc) \
+ (USB_DMATAG_TO_XROOT((pc)->tag_parent)->udev)
+
+#define SAF1761_DCINTERRUPT_THREAD_IRQ \
+ (SOTG_DCINTERRUPT_IEVBUS | SOTG_DCINTERRUPT_IEBRST | \
+ SOTG_DCINTERRUPT_IERESM | SOTG_DCINTERRUPT_IESUSP)
+
+#ifdef USB_DEBUG
+static int saf1761_otg_debug = 0;
+static int saf1761_otg_forcefs = 0;
+
+static
+SYSCTL_NODE(_hw_usb, OID_AUTO, saf1761_otg, CTLFLAG_RW, 0,
+ "USB SAF1761 DCI");
+
+SYSCTL_INT(_hw_usb_saf1761_otg, OID_AUTO, debug, CTLFLAG_RW,
+ &saf1761_otg_debug, 0, "SAF1761 DCI debug level");
+SYSCTL_INT(_hw_usb_saf1761_otg, OID_AUTO, forcefs, CTLFLAG_RW,
+ &saf1761_otg_forcefs, 0, "SAF1761 DCI force FULL speed");
+#endif
+
+#define SAF1761_OTG_INTR_ENDPT 1
+
+/* prototypes */
+
+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;
+static saf1761_otg_cmd_t saf1761_host_bulk_data_tx;
+static saf1761_otg_cmd_t saf1761_host_intr_data_rx;
+static saf1761_otg_cmd_t saf1761_host_intr_data_tx;
+static saf1761_otg_cmd_t saf1761_host_isoc_data_rx;
+static saf1761_otg_cmd_t saf1761_host_isoc_data_tx;
+static saf1761_otg_cmd_t saf1761_device_setup_rx;
+static saf1761_otg_cmd_t saf1761_device_data_rx;
+static saf1761_otg_cmd_t saf1761_device_data_tx;
+static saf1761_otg_cmd_t saf1761_device_data_tx_sync;
+static void saf1761_otg_device_done(struct usb_xfer *, usb_error_t);
+static void saf1761_otg_do_poll(struct usb_bus *);
+static void saf1761_otg_standard_done(struct usb_xfer *);
+static void saf1761_otg_intr_set(struct usb_xfer *, uint8_t);
+static void saf1761_otg_root_intr(struct saf1761_otg_softc *);
+
+/*
+ * Here is a list of what the SAF1761 chip can support. The main
+ * limitation is that the sum of the buffer sizes must be less than
+ * 8192 bytes.
+ */
+static const struct usb_hw_ep_profile saf1761_otg_ep_profile[] = {
+
+ [0] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 0,
+ .support_control = 1,
+ },
+ [1] = {
+ .max_in_frame_size = SOTG_HS_MAX_PACKET_SIZE,
+ .max_out_frame_size = SOTG_HS_MAX_PACKET_SIZE,
+ .is_simplex = 0,
+ .support_interrupt = 1,
+ .support_bulk = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+};
+
+static void
+saf1761_otg_get_hw_ep_profile(struct usb_device *udev,
+ const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+ if (ep_addr == 0) {
+ *ppf = saf1761_otg_ep_profile + 0;
+ } else if (ep_addr < 8) {
+ *ppf = saf1761_otg_ep_profile + 1;
+ } else {
+ *ppf = NULL;
+ }
+}
+
+static void
+saf1761_otg_pull_up(struct saf1761_otg_softc *sc)
+{
+ /* activate pullup on D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) {
+ DPRINTF("\n");
+
+ sc->sc_flags.d_pulled_up = 1;
+ }
+}
+
+static void
+saf1761_otg_pull_down(struct saf1761_otg_softc *sc)
+{
+ /* release pullup on D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ DPRINTF("\n");
+
+ sc->sc_flags.d_pulled_up = 0;
+ }
+}
+
+static void
+saf1761_otg_wakeup_peer(struct saf1761_otg_softc *sc)
+{
+ uint16_t temp;
+
+ if (!(sc->sc_flags.status_suspend))
+ return;
+
+ DPRINTFN(5, "\n");
+
+ temp = SAF1761_READ_LE_4(sc, SOTG_MODE);
+ SAF1761_WRITE_LE_4(sc, SOTG_MODE, temp | SOTG_MODE_SNDRSU);
+ SAF1761_WRITE_LE_4(sc, SOTG_MODE, temp & ~SOTG_MODE_SNDRSU);
+
+ /* Wait 8ms for remote wakeup to complete. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+}
+
+static uint8_t
+saf1761_host_channel_alloc(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ uint32_t x;
+
+ if (td->channel < SOTG_HOST_CHANNEL_MAX)
+ return (0);
+
+ /* check if device is suspended */
+ if (SAF1761_OTG_PC2UDEV(td->pc)->flags.self_suspended != 0)
+ return (1); /* busy - cannot transfer data */
+
+ 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 = 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 = 64 + x;
+ return (0);
+ }
+ break;
+ }
+ return (1);
+}
+
+static void
+saf1761_host_channel_free(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ uint32_t x;
+
+ if (td->channel >= SOTG_HOST_CHANNEL_MAX)
+ return;
+
+ switch (td->ep_type) {
+ case UE_INTERRUPT:
+ x = td->channel - 32;
+ td->channel = SOTG_HOST_CHANNEL_MAX;
+ sc->sc_host_intr_map &= ~(1 << x);
+ sc->sc_host_intr_suspend_map &= ~(1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
+ (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+ break;
+ case UE_ISOCHRONOUS:
+ x = td->channel;
+ td->channel = SOTG_HOST_CHANNEL_MAX;
+ sc->sc_host_isoc_map &= ~(1 << x);
+ sc->sc_host_isoc_suspend_map &= ~(1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
+ (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
+ break;
+ default:
+ x = td->channel - 64;
+ td->channel = SOTG_HOST_CHANNEL_MAX;
+ sc->sc_host_async_map &= ~(1 << x);
+ sc->sc_host_async_suspend_map &= ~(1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+ (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+ break;
+ }
+}
+
+static uint32_t
+saf1761_peek_host_status_le_4(struct saf1761_otg_softc *sc, uint32_t offset)
+{
+ uint32_t x = 0;
+ while (1) {
+ uint32_t retval;
+
+ SAF1761_WRITE_LE_4(sc, SOTG_MEMORY_REG, offset);
+ SAF1761_90NS_DELAY(sc); /* read prefetch time is 90ns */
+ retval = SAF1761_READ_LE_4(sc, offset);
+ if (retval != 0)
+ return (retval);
+ if (++x == 8) {
+ DPRINTF("STAUS is zero at offset 0x%x\n", offset);
+ break;
+ }
+ }
+ return (0);
+}
+
+static void
+saf1761_read_host_memory(struct saf1761_otg_softc *sc,
+ struct saf1761_otg_td *td, uint32_t len)
+{
+ struct usb_page_search buf_res;
+ uint32_t offset;
+ uint32_t count;
+
+ if (len == 0)
+ return;
+
+ offset = SOTG_DATA_ADDR(td->channel);
+ SAF1761_WRITE_LE_4(sc, SOTG_MEMORY_REG, offset);
+ SAF1761_90NS_DELAY(sc); /* read prefetch time is 90ns */
+
+ /* optimised read first */
+ while (len > 0) {
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > len)
+ buf_res.length = len;
+
+ /* check buffer alignment */
+ if (((uintptr_t)buf_res.buffer) & 3)
+ break;
+
+ count = buf_res.length & ~3;
+ if (count == 0)
+ break;
+
+ bus_space_read_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ offset, buf_res.buffer, count / 4);
+
+ len -= count;
+ offset += count;
+
+ /* update remainder and offset */
+ td->remainder -= count;
+ td->offset += count;
+ }
+
+ if (len > 0) {
+ /* use bounce buffer */
+ bus_space_read_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ offset, sc->sc_bounce_buffer, (len + 3) / 4);
+ usbd_copy_in(td->pc, td->offset,
+ sc->sc_bounce_buffer, len);
+
+ /* update remainder and offset */
+ td->remainder -= len;
+ td->offset += len;
+ }
+}
+
+static void
+saf1761_write_host_memory(struct saf1761_otg_softc *sc,
+ struct saf1761_otg_td *td, uint32_t len)
+{
+ struct usb_page_search buf_res;
+ uint32_t offset;
+ uint32_t count;
+
+ if (len == 0)
+ return;
+
+ offset = SOTG_DATA_ADDR(td->channel);
+
+ /* optimised write first */
+ while (len > 0) {
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > len)
+ buf_res.length = len;
+
+ /* check buffer alignment */
+ if (((uintptr_t)buf_res.buffer) & 3)
+ break;
+
+ count = buf_res.length & ~3;
+ if (count == 0)
+ break;
+
+ bus_space_write_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ offset, buf_res.buffer, count / 4);
+
+ len -= count;
+ offset += count;
+
+ /* update remainder and offset */
+ td->remainder -= count;
+ td->offset += count;
+ }
+ if (len > 0) {
+ /* use bounce buffer */
+ usbd_copy_out(td->pc, td->offset, sc->sc_bounce_buffer, len);
+ bus_space_write_region_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ offset, sc->sc_bounce_buffer, (len + 3) / 4);
+
+ /* update remainder and offset */
+ td->remainder -= len;
+ td->offset += len;
+ }
+}
+
+static uint8_t
+saf1761_host_setup_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ uint32_t pdt_addr;
+ uint32_t status;
+ uint32_t count;
+ uint32_t temp;
+
+ if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+ 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) {
+ td->error_any = 1;
+ }
+ goto complete;
+ }
+ if (saf1761_host_channel_alloc(sc, td))
+ goto busy;
+
+ count = 8;
+
+ if (count != td->remainder) {
+ td->error_any = 1;
+ goto complete;
+ }
+
+ saf1761_write_host_memory(sc, td, count);
+
+ 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);
+ SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, 0);
+
+ 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;
+ SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+ temp = td->dw1_value | (2 << 10) /* SETUP 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 */ |
+ (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_ATL_PTD_SKIP_PTD,
+ (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+
+ td->toggle = 1;
+busy:
+ return (1); /* busy */
+complete:
+ saf1761_host_channel_free(sc, td);
+ return (0); /* complete */
+}
+
+static uint8_t
+saf1761_host_bulk_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;
+ uint8_t got_short;
+
+ 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) {
+ if (!(status & SOTG_PTD_DW3_ERRORS))
+ td->error_stall = 1;
+ td->error_any = 1;
+ goto complete;
+ }
+ count = (status & SOTG_PTD_DW3_XFER_COUNT);
+ 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_memory(sc, td, count);
+
+ /* 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 */
+ }
+ saf1761_host_channel_free(sc, td);
+ }
+ 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 */
+
+ 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);
+ SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, 0);
+
+ temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) |
+ SOTG_PTD_DW3_CERR_2;
+ 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_ATL_PTD_SKIP_PTD,
+ (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+busy:
+ return (1); /* busy */
+complete:
+ saf1761_host_channel_free(sc, td);
+ return (0); /* complete */
+}
+
+static uint8_t
+saf1761_host_bulk_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) {
+ if (!(status & SOTG_PTD_DW3_ERRORS))
+ td->error_stall = 1;
+ td->error_any = 1;
+ goto complete;
+ }
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt)
+ goto complete;
+ /* else we need to transmit a short packet */
+ }
+ saf1761_host_channel_free(sc, td);
+ }
+ 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);
+
+ /* set toggle, if any */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+
+ /* 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);
+ SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, 0);
+
+ temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) |
+ SOTG_PTD_DW3_CERR_2;
+ 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 */ |
+ (td->max_packet_size << 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_ATL_PTD_SKIP_PTD,
+ (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+
+ td->toggle ^= 1;
+busy:
+ return (1); /* busy */
+complete:
+ saf1761_host_channel_free(sc, td);
+ return (0); /* complete */
+}
+
+static uint8_t
+saf1761_host_intr_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;
+ uint8_t got_short;
+
+ 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) {
+ if (!(status & SOTG_PTD_DW3_ERRORS))
+ td->error_stall = 1;
+ td->error_any = 1;
+ goto complete;
+ }
+ count = (status & SOTG_PTD_DW3_XFER_COUNT);
+ 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_memory(sc, td, count);
+
+ /* 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 */
+ }
+ saf1761_host_channel_free(sc, td);
+ }
+ 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 */
+
+ 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 | (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);
+ 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_INT_PTD_SKIP_PTD,
+ (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+busy:
+ return (1); /* busy */
+complete:
+ saf1761_host_channel_free(sc, td);
+ return (0); /* complete */
+}
+
+static uint8_t
+saf1761_host_intr_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) {
+ if (!(status & SOTG_PTD_DW3_ERRORS))
+ td->error_stall = 1;
+ td->error_any = 1;
+ goto complete;
+ }
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt)
+ goto complete;
+ /* else we need to transmit a short packet */
+ }
+ saf1761_host_channel_free(sc, td);
+ }
+ 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);
+
+ /* set toggle, if any */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+
+ /* 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);
+
+ 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 | (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);
+ 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 */ |
+ (td->max_packet_size << 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_INT_PTD_SKIP_PTD,
+ (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+
+ td->toggle ^= 1;
+busy:
+ return (1); /* busy */
+complete:
+ saf1761_host_channel_free(sc, td);
+ return (0); /* 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
+saf1761_otg_set_address(struct saf1761_otg_softc *sc, uint8_t addr)
+{
+ DPRINTFN(5, "addr=%d\n", addr);
+
+ SAF1761_WRITE_LE_4(sc, SOTG_ADDRESS, addr | SOTG_ADDRESS_ENABLE);
+}
+
+
+static void
+saf1761_read_device_fifo(struct saf1761_otg_softc *sc,
+ struct saf1761_otg_td *td, uint32_t len)
+{
+ struct usb_page_search buf_res;
+ uint32_t count;
+
+ /* optimised read first */
+ while (len > 0) {
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > len)
+ buf_res.length = len;
+
+ /* check buffer alignment */
+ if (((uintptr_t)buf_res.buffer) & 3)
+ break;
+
+ count = buf_res.length & ~3;
+ if (count == 0)
+ break;
+
+ bus_space_read_multi_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ SOTG_DATA_PORT, buf_res.buffer, count / 4);
+
+ len -= count;
+
+ /* update remainder and offset */
+ td->remainder -= count;
+ td->offset += count;
+ }
+
+ if (len > 0) {
+ /* use bounce buffer */
+ bus_space_read_multi_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ SOTG_DATA_PORT, sc->sc_bounce_buffer, (len + 3) / 4);
+ usbd_copy_in(td->pc, td->offset,
+ sc->sc_bounce_buffer, len);
+
+ /* update remainder and offset */
+ td->remainder -= len;
+ td->offset += len;
+ }
+}
+
+static void
+saf1761_write_device_fifo(struct saf1761_otg_softc *sc,
+ struct saf1761_otg_td *td, uint32_t len)
+{
+ struct usb_page_search buf_res;
+ uint32_t count;
+
+ /* optimised write first */
+ while (len > 0) {
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > len)
+ buf_res.length = len;
+
+ /* check buffer alignment */
+ if (((uintptr_t)buf_res.buffer) & 3)
+ break;
+
+ count = buf_res.length & ~3;
+ if (count == 0)
+ break;
+
+ bus_space_write_multi_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ SOTG_DATA_PORT, buf_res.buffer, count / 4);
+
+ len -= count;
+
+ /* update remainder and offset */
+ td->remainder -= count;
+ td->offset += count;
+ }
+ if (len > 0) {
+ /* use bounce buffer */
+ usbd_copy_out(td->pc, td->offset, sc->sc_bounce_buffer, len);
+ bus_space_write_multi_4((sc)->sc_io_tag, (sc)->sc_io_hdl,
+ SOTG_DATA_PORT, sc->sc_bounce_buffer, (len + 3) / 4);
+
+ /* update remainder and offset */
+ td->remainder -= len;
+ td->offset += len;
+ }
+}
+
+static uint8_t
+saf1761_device_setup_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ struct usb_device_request req;
+ uint32_t count;
+
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) == 0)
+ goto busy;
+
+ /* get buffer length */
+ count &= SOTG_BUF_LENGTH_BUFLEN_MASK;
+
+ DPRINTFN(5, "count=%u rem=%u\n", count, td->remainder);
+
+ /* clear did stall */
+ td->did_stall = 0;
+
+ /* clear stall */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, 0);
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(0, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ goto busy;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(0, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ goto busy;
+ }
+ /* receive data */
+ saf1761_read_device_fifo(sc, td, sizeof(req));
+
+ /* extract SETUP packet again */
+ usbd_copy_out(td->pc, 0, &req, sizeof(req));
+
+ /* sneak peek the set address request */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ DPRINTF("Set address %d\n", sc->sc_dv_addr);
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+ return (0); /* complete */
+
+busy:
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(5, "stalling\n");
+
+ /* set stall */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_STALL);
+
+ td->did_stall = 1;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+saf1761_device_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ uint32_t count;
+ uint8_t got_short = 0;
+
+ if (td->ep_index == 0) {
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) != 0) {
+
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP:
+ */
+ DPRINTFN(5, "faking complete\n");
+ return (0); /* complete */
+ }
+ DPRINTFN(5, "SETUP packet while receiving data\n");
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+ }
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (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_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_DSEN);
+ }
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) == 0)
+ return (1); /* not complete */
+
+ /* get buffer length */
+ count &= SOTG_BUF_LENGTH_BUFLEN_MASK;
+
+ DPRINTFN(5, "rem=%u count=0x%04x\n", td->remainder, 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;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error_any = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error_any = 1;
+ return (0); /* we are complete */
+ }
+ /* receive data */
+ saf1761_read_device_fifo(sc, td, 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 */
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+saf1761_device_data_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ uint32_t count;
+
+ if (td->ep_index == 0) {
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) != 0) {
+ DPRINTFN(5, "SETUP abort\n");
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+ }
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (td->ep_index << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+ SOTG_EP_INDEX_DIR_IN);
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) != 0)
+ return (1); /* not complete */
+
+ /* enable data stage */
+ if (td->set_toggle) {
+ td->set_toggle = 0;
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_DSEN);
+ }
+
+ DPRINTFN(5, "rem=%u\n", td->remainder);
+
+ count = td->max_packet_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ /* transmit data */
+ saf1761_write_device_fifo(sc, td, count);
+
+ if (td->ep_index == 0) {
+ if (count < SOTG_FS_MAX_PACKET_SIZE) {
+ /* set end of packet */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_VENDP);
+ }
+ } else {
+ if (count < SOTG_HS_MAX_PACKET_SIZE) {
+ /* set end of packet */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_VENDP);
+ }
+ }
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+saf1761_device_data_tx_sync(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
+{
+ uint32_t count;
+
+ if (td->ep_index == 0) {
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) != 0) {
+ DPRINTFN(5, "Faking complete\n");
+ return (0); /* complete */
+ }
+ }
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (td->ep_index << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+ SOTG_EP_INDEX_DIR_IN);
+
+ count = SAF1761_READ_LE_4(sc, SOTG_BUF_LENGTH);
+
+ /* check buffer status */
+ if ((count & SOTG_BUF_LENGTH_FILLED_MASK) != 0)
+ return (1); /* busy */
+
+ if (sc->sc_dv_addr != 0xFF) {
+ /* write function address */
+ saf1761_otg_set_address(sc, sc->sc_dv_addr);
+ }
+ return (0); /* complete */
+}
+
+static void
+saf1761_otg_xfer_do_fifo(struct saf1761_otg_softc *sc, struct usb_xfer *xfer)
+{
+ struct saf1761_otg_td *td;
+ uint8_t toggle;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+ if (td == NULL)
+ return;
+
+ while (1) {
+ if ((td->func) (sc, td)) {
+ /* operation in progress */
+ break;
+ }
+ if (((void *)td) == xfer->td_transfer_last) {
+ goto done;
+ }
+ if (td->error_any) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ goto done;
+ }
+ }
+ /*
+ * Fetch the next transfer descriptor.
+ */
+ toggle = td->toggle;
+ td = td->obj_next;
+ td->toggle = toggle;
+ xfer->td_transfer_cache = td;
+ }
+ return;
+
+done:
+ /* compute all actual lengths */
+ xfer->td_transfer_cache = NULL;
+ sc->sc_xfer_complete = 1;
+}
+
+static uint8_t
+saf1761_otg_xfer_do_complete(struct saf1761_otg_softc *sc, struct usb_xfer *xfer)
+{
+ struct saf1761_otg_td *td;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+ if (td == NULL) {
+ /* compute all actual lengths */
+ saf1761_otg_standard_done(xfer);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+saf1761_otg_interrupt_poll_locked(struct saf1761_otg_softc *sc)
+{
+ struct usb_xfer *xfer;
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry)
+ saf1761_otg_xfer_do_fifo(sc, xfer);
+}
+
+static void
+saf1761_otg_wait_suspend(struct saf1761_otg_softc *sc, uint8_t on)
+{
+ if (on) {
+ sc->sc_intr_enable |= SOTG_DCINTERRUPT_IESUSP;
+ sc->sc_intr_enable &= ~SOTG_DCINTERRUPT_IERESM;
+ } else {
+ sc->sc_intr_enable &= ~SOTG_DCINTERRUPT_IESUSP;
+ sc->sc_intr_enable |= SOTG_DCINTERRUPT_IERESM;
+ }
+ SAF1761_WRITE_LE_4(sc, SOTG_DCINTERRUPT_EN, sc->sc_intr_enable);
+}
+
+static void
+saf1761_otg_update_vbus(struct saf1761_otg_softc *sc)
+{
+ uint16_t status;
+
+ /* read fresh status */
+ status = SAF1761_READ_LE_4(sc, SOTG_STATUS);
+
+ DPRINTFN(4, "STATUS=0x%04x\n", status);
+
+ if ((status & SOTG_STATUS_VBUS_VLD) &&
+ (status & SOTG_STATUS_ID)) {
+ /* VBUS present and device mode */
+ if (!sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 1;
+
+ /* complete root HUB interrupt endpoint */
+ saf1761_otg_root_intr(sc);
+ }
+ } else {
+ /* VBUS not-present or host mode */
+ if (sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+ saf1761_otg_root_intr(sc);
+ }
+ }
+}
+
+static void
+saf1761_otg_interrupt_complete_locked(struct saf1761_otg_softc *sc)
+{
+ struct usb_xfer *xfer;
+repeat:
+ /* scan for completion events */
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (saf1761_otg_xfer_do_complete(sc, xfer))
+ goto repeat;
+ }
+}
+
+int
+saf1761_otg_filter_interrupt(void *arg)
+{
+ struct saf1761_otg_softc *sc = arg;
+ int retval = FILTER_HANDLED;
+ uint32_t hcstat;
+ uint32_t status;
+
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ hcstat = SAF1761_READ_LE_4(sc, SOTG_HCINTERRUPT);
+ /* acknowledge all host controller interrupts */
+ SAF1761_WRITE_LE_4(sc, SOTG_HCINTERRUPT, hcstat);
+
+ status = SAF1761_READ_LE_4(sc, SOTG_DCINTERRUPT);
+ /* acknowledge all device controller interrupts */
+ SAF1761_WRITE_LE_4(sc, SOTG_DCINTERRUPT,
+ status & ~SAF1761_DCINTERRUPT_THREAD_IRQ);
+
+ (void) SAF1761_READ_LE_4(sc, SOTG_ATL_PTD_DONE_PTD);
+ (void) SAF1761_READ_LE_4(sc, SOTG_INT_PTD_DONE_PTD);
+ (void) SAF1761_READ_LE_4(sc, SOTG_ISO_PTD_DONE_PTD);
+
+ if (status & SAF1761_DCINTERRUPT_THREAD_IRQ)
+ retval = FILTER_SCHEDULE_THREAD;
+
+ /* poll FIFOs, if any */
+ saf1761_otg_interrupt_poll_locked(sc);
+
+ if (sc->sc_xfer_complete != 0)
+ retval = FILTER_SCHEDULE_THREAD;
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+
+ return (retval);
+}
+
+void
+saf1761_otg_interrupt(void *arg)
+{
+ struct saf1761_otg_softc *sc = arg;
+ uint32_t status;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ status = SAF1761_READ_LE_4(sc, SOTG_DCINTERRUPT) &
+ SAF1761_DCINTERRUPT_THREAD_IRQ;
+
+ /* acknowledge all device controller interrupts */
+ SAF1761_WRITE_LE_4(sc, SOTG_DCINTERRUPT, status);
+
+ DPRINTF("DCINTERRUPT=0x%08x SOF=0x%08x "
+ "FRINDEX=0x%08x\n", status,
+ SAF1761_READ_LE_4(sc, SOTG_FRAME_NUM),
+ SAF1761_READ_LE_4(sc, SOTG_FRINDEX));
+
+ /* update VBUS and ID bits, if any */
+ if (status & SOTG_DCINTERRUPT_IEVBUS)
+ saf1761_otg_update_vbus(sc);
+
+ if (status & SOTG_DCINTERRUPT_IEBRST) {
+ /* unlock device */
+ SAF1761_WRITE_LE_4(sc, SOTG_UNLOCK_DEVICE,
+ SOTG_UNLOCK_DEVICE_CODE);
+
+ /* Enable device address */
+ SAF1761_WRITE_LE_4(sc, SOTG_ADDRESS,
+ SOTG_ADDRESS_ENABLE);
+
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* disable resume interrupt */
+ saf1761_otg_wait_suspend(sc, 1);
+ /* complete root HUB interrupt endpoint */
+ saf1761_otg_root_intr(sc);
+ }
+ /*
+ * If "RESUME" and "SUSPEND" is set at the same time we
+ * interpret that like "RESUME". Resume is set when there is
+ * at least 3 milliseconds of inactivity on the USB BUS:
+ */
+ if (status & SOTG_DCINTERRUPT_IERESM) {
+ /* unlock device */
+ SAF1761_WRITE_LE_4(sc, SOTG_UNLOCK_DEVICE,
+ SOTG_UNLOCK_DEVICE_CODE);
+
+ if (sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+ /* disable resume interrupt */
+ saf1761_otg_wait_suspend(sc, 1);
+ /* complete root HUB interrupt endpoint */
+ saf1761_otg_root_intr(sc);
+ }
+ } else if (status & SOTG_DCINTERRUPT_IESUSP) {
+ if (!sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+ /* enable resume interrupt */
+ saf1761_otg_wait_suspend(sc, 0);
+ /* complete root HUB interrupt endpoint */
+ saf1761_otg_root_intr(sc);
+ }
+ }
+
+ if (sc->sc_xfer_complete != 0) {
+ sc->sc_xfer_complete = 0;
+
+ /* complete FIFOs, if any */
+ saf1761_otg_interrupt_complete_locked(sc);
+ }
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+saf1761_otg_setup_standard_chain_sub(struct saf1761_otg_std_temp *temp)
+{
+ struct saf1761_otg_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ 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
+saf1761_otg_setup_standard_chain(struct usb_xfer *xfer)
+{
+ struct saf1761_otg_std_temp temp;
+ struct saf1761_otg_softc *sc;
+ struct saf1761_otg_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),
+ xfer->sumlen, usbd_get_speed(xfer->xroot->udev));
+
+ temp.max_frame_size = xfer->max_frame_size;
+
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ /* setup temp */
+
+ temp.pc = NULL;
+ temp.td = NULL;
+ temp.td_next = xfer->td_start[0];
+ temp.offset = 0;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr;
+ temp.did_stall = !xfer->flags_int.control_stall;
+
+ is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST);
+
+ sc = SAF1761_OTG_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) {
+
+ 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;
+ /* check for last frame */
+ if (xfer->nframes == 1) {
+ /* no STATUS stage yet, SETUP is last */
+ if (xfer->flags_int.control_act)
+ temp.setup_alt_next = 0;
+ }
+ saf1761_otg_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpointno & UE_DIR_IN) {
+ 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 {
+ 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 */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_act) {
+ temp.setup_alt_next = 0;
+ }
+ } else {
+ temp.setup_alt_next = 0;
+ }
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ saf1761_otg_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* check for control transfer */
+ if (xfer->flags_int.control_xfr) {
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+ temp.len = 0;
+ temp.short_pkt = 0;
+ temp.setup_alt_next = 0;
+
+ /* check if we should append a status stage */
+ if (!xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ if (xfer->endpointno & UE_DIR_IN) {
+ if (is_host) {
+ temp.func = &saf1761_host_bulk_data_tx;
+ need_sync = 0;
+ } else {
+ temp.func = &saf1761_device_data_rx;
+ need_sync = 0;
+ }
+ } else {
+ 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_otg_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_device_data_tx_sync;
+ saf1761_otg_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_otg_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
+saf1761_otg_timeout(void *arg)
+{
+ struct usb_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ saf1761_otg_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+saf1761_otg_intr_set(struct usb_xfer *xfer, uint8_t set)
+{
+ struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(xfer->xroot->bus);
+ uint8_t ep_no = (xfer->endpointno & UE_ADDR);
+ uint32_t mask;
+
+ DPRINTFN(15, "endpoint=%d set=%d\n", xfer->endpointno, set);
+
+ if (ep_no == 0) {
+ mask = SOTG_DCINTERRUPT_IEPRX(0) |
+ SOTG_DCINTERRUPT_IEPTX(0) |
+ SOTG_DCINTERRUPT_IEP0SETUP;
+ } else if (xfer->endpointno & UE_DIR_IN) {
+ mask = SOTG_DCINTERRUPT_IEPTX(ep_no);
+ } else {
+ mask = SOTG_DCINTERRUPT_IEPRX(ep_no);
+ }
+
+ if (set)
+ sc->sc_intr_enable |= mask;
+ else
+ sc->sc_intr_enable &= ~mask;
+
+ SAF1761_WRITE_LE_4(sc, SOTG_DCINTERRUPT_EN, sc->sc_intr_enable);
+}
+
+static void
+saf1761_otg_start_standard_chain(struct usb_xfer *xfer)
+{
+ struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(xfer->xroot->bus);
+
+ DPRINTFN(9, "\n");
+
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ /* poll one time */
+ saf1761_otg_xfer_do_fifo(sc, xfer);
+
+ if (xfer->td_transfer_cache != NULL) {
+ /*
+ * Only enable the endpoint interrupt when we are
+ * actually waiting for data, hence we are dealing
+ * with level triggered interrupts !
+ */
+ saf1761_otg_intr_set(xfer, 1);
+
+ /* put transfer on interrupt queue */
+ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usbd_transfer_timeout_ms(xfer,
+ &saf1761_otg_timeout, xfer->timeout);
+ }
+ } else {
+ /* catch completion, if any */
+ saf1761_otg_interrupt_complete_locked(sc);
+ }
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+static void
+saf1761_otg_root_intr(struct saf1761_otg_softc *sc)
+{
+ DPRINTFN(9, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ /* set port bit - we only have one port */
+ sc->sc_hub_idata[0] = 0x02;
+
+ uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
+ sizeof(sc->sc_hub_idata));
+}
+
+static usb_error_t
+saf1761_otg_standard_done_sub(struct usb_xfer *xfer)
+{
+ struct saf1761_otg_td *td;
+ uint32_t len;
+ usb_error_t error;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ 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_any = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error_any) {
+ /* the transfer is finished */
+ error = (td->error_stall ?
+ USB_ERR_STALLED : USB_ERR_IOERROR);
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error);
+}
+
+static void
+saf1761_otg_standard_done(struct usb_xfer *xfer)
+{
+ usb_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
+ xfer, xfer->endpoint);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = saf1761_otg_standard_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = saf1761_otg_standard_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = saf1761_otg_standard_done_sub(xfer);
+ }
+done:
+ saf1761_otg_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * saf1761_otg_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+saf1761_otg_device_done(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct saf1761_otg_softc *sc = SAF1761_OTG_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);
+
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
+ saf1761_otg_intr_set(xfer, 0);
+ } else {
+ struct saf1761_otg_td *td;
+
+ td = xfer->td_transfer_cache;
+
+ if (td != NULL)
+ saf1761_host_channel_free(sc, td);
+ }
+
+ /* dequeue transfer and start next transfer */
+ usbd_transfer_done(xfer, error);
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+static void
+saf1761_otg_xfer_stall(struct usb_xfer *xfer)
+{
+ saf1761_otg_device_done(xfer, USB_ERR_STALLED);
+}
+
+static void
+saf1761_otg_set_stall(struct usb_device *udev,
+ struct usb_endpoint *ep, uint8_t *did_stall)
+{
+ struct saf1761_otg_softc *sc;
+ uint8_t ep_no;
+ uint8_t ep_type;
+ uint8_t ep_dir;
+
+ 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 STALL bit */
+ sc = SAF1761_OTG_BUS2SC(udev->bus);
+
+ ep_no = (ep->edesc->bEndpointAddress & UE_ADDR);
+ ep_dir = (ep->edesc->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT));
+ ep_type = (ep->edesc->bmAttributes & UE_XFERTYPE);
+
+ if (ep_type == UE_CONTROL) {
+ /* should not happen */
+ return;
+ }
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (ep_no << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+ ((ep_dir == UE_DIR_IN) ? SOTG_EP_INDEX_DIR_IN :
+ SOTG_EP_INDEX_DIR_OUT));
+
+ /* set stall */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_STALL);
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+static void
+saf1761_otg_clear_stall_sub_locked(struct saf1761_otg_softc *sc,
+ uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
+{
+ if (ep_type == UE_CONTROL) {
+ /* clearing stall is not needed */
+ return;
+ }
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (ep_no << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+ ((ep_dir == UE_DIR_IN) ? SOTG_EP_INDEX_DIR_IN :
+ SOTG_EP_INDEX_DIR_OUT));
+
+ /* disable endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_TYPE, 0);
+ /* enable endpoint again - will clear data toggle */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_TYPE, ep_type | SOTG_EP_TYPE_ENABLE);
+
+ /* clear buffer */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_CLBUF);
+ /* clear stall */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_FUNC, 0);
+}
+
+static void
+saf1761_otg_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
+{
+ struct saf1761_otg_softc *sc;
+ struct usb_endpoint_descriptor *ed;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(5, "endpoint=%p\n", ep);
+
+ /* check mode */
+ if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ /* get softc */
+ sc = SAF1761_OTG_BUS2SC(udev->bus);
+
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ /* get endpoint descriptor */
+ ed = ep->edesc;
+
+ /* reset endpoint */
+ saf1761_otg_clear_stall_sub_locked(sc,
+ (ed->bEndpointAddress & UE_ADDR),
+ (ed->bmAttributes & UE_XFERTYPE),
+ (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+}
+
+usb_error_t
+saf1761_otg_init(struct saf1761_otg_softc *sc)
+{
+ const struct usb_hw_ep_profile *pf;
+ uint32_t x;
+
+ DPRINTF("\n");
+
+ /* set up the bus structure */
+ sc->sc_bus.usbrev = USB_REV_2_0;
+ sc->sc_bus.methods = &saf1761_otg_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* Reset Host controller, including HW mode */
+ SAF1761_WRITE_LE_4(sc, SOTG_SW_RESET, SOTG_SW_RESET_ALL);
+
+ DELAY(1000);
+
+ /* Reset Host controller, including HW mode */
+ SAF1761_WRITE_LE_4(sc, SOTG_SW_RESET, SOTG_SW_RESET_HC);
+
+ /* wait a bit */
+ DELAY(1000);
+
+ SAF1761_WRITE_LE_4(sc, SOTG_SW_RESET, 0);
+
+ /* wait a bit */
+ DELAY(1000);
+
+ /* Enable interrupts */
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_GLOBAL_INTR_EN |
+ SOTG_HW_MODE_CTRL_COMN_INT;
+
+ /* unlock device */
+ SAF1761_WRITE_LE_4(sc, SOTG_UNLOCK_DEVICE, SOTG_UNLOCK_DEVICE_CODE);
+
+ /*
+ * Set correct hardware mode, must be written twice if bus
+ * width is changed:
+ */
+ SAF1761_WRITE_LE_4(sc, SOTG_HW_MODE_CTRL, sc->sc_hw_mode);
+ SAF1761_WRITE_LE_4(sc, SOTG_HW_MODE_CTRL, sc->sc_hw_mode);
+
+ SAF1761_WRITE_LE_4(sc, SOTG_DCSCRATCH, 0xdeadbeef);
+ SAF1761_WRITE_LE_4(sc, SOTG_HCSCRATCH, 0xdeadbeef);
+
+ DPRINTF("DCID=0x%08x VEND_PROD=0x%08x HWMODE=0x%08x SCRATCH=0x%08x,0x%08x\n",
+ SAF1761_READ_LE_4(sc, SOTG_DCCHIP_ID),
+ SAF1761_READ_LE_4(sc, SOTG_VEND_PROD_ID),
+ SAF1761_READ_LE_4(sc, SOTG_HW_MODE_CTRL),
+ SAF1761_READ_LE_4(sc, SOTG_DCSCRATCH),
+ SAF1761_READ_LE_4(sc, SOTG_HCSCRATCH));
+
+ /* reset device controller */
+ SAF1761_WRITE_LE_4(sc, SOTG_MODE, SOTG_MODE_SFRESET);
+ SAF1761_WRITE_LE_4(sc, SOTG_MODE, 0);
+
+ /* wait a bit */
+ DELAY(1000);
+
+ /* reset host controller */
+ SAF1761_WRITE_LE_4(sc, SOTG_USBCMD, SOTG_USBCMD_HCRESET);
+
+ /* wait for reset to clear */
+ for (x = 0; x != 10; x++) {
+ if ((SAF1761_READ_LE_4(sc, SOTG_USBCMD) & SOTG_USBCMD_HCRESET) == 0)
+ break;
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10);
+ }
+
+ SAF1761_WRITE_LE_4(sc, SOTG_HW_MODE_CTRL, sc->sc_hw_mode |
+ SOTG_HW_MODE_CTRL_ALL_ATX_RESET);
+
+ /* wait a bit */
+ DELAY(1000);
+
+ SAF1761_WRITE_LE_4(sc, SOTG_HW_MODE_CTRL, sc->sc_hw_mode);
+
+ /* wait a bit */
+ DELAY(1000);
+
+ /* do a pulldown */
+ saf1761_otg_pull_down(sc);
+
+ /* wait 10ms for pulldown to stabilise */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+ for (x = 1;; x++) {
+
+ saf1761_otg_get_hw_ep_profile(NULL, &pf, x);
+ if (pf == NULL)
+ break;
+
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (x << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+ SOTG_EP_INDEX_DIR_IN);
+
+ /* select the maximum packet size */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_MAXPACKET, pf->max_in_frame_size);
+
+ /* select the correct endpoint */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_INDEX,
+ (x << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+ SOTG_EP_INDEX_DIR_OUT);
+
+ /* select the maximum packet size */
+ SAF1761_WRITE_LE_4(sc, SOTG_EP_MAXPACKET, pf->max_out_frame_size);
+ }
+
+ /* enable interrupts */
+ SAF1761_WRITE_LE_4(sc, SOTG_MODE, SOTG_MODE_GLINTENA |
+ SOTG_MODE_CLKAON | SOTG_MODE_WKUPCS);
+
+ sc->sc_interrupt_cfg |=
+ SOTG_INTERRUPT_CFG_CDBGMOD |
+ SOTG_INTERRUPT_CFG_DDBGMODIN |
+ SOTG_INTERRUPT_CFG_DDBGMODOUT;
+
+ /* set default values */
+ SAF1761_WRITE_LE_4(sc, SOTG_INTERRUPT_CFG, sc->sc_interrupt_cfg);
+
+ /* enable VBUS and ID interrupt */
+ SAF1761_WRITE_LE_4(sc, SOTG_IRQ_ENABLE_SET_CLR,
+ SOTG_IRQ_ENABLE_CLR(0xFFFF));
+ SAF1761_WRITE_LE_4(sc, SOTG_IRQ_ENABLE_SET_CLR,
+ SOTG_IRQ_ENABLE_SET(SOTG_IRQ_ID | SOTG_IRQ_VBUS_VLD));
+
+ /* enable interrupts */
+ sc->sc_intr_enable = SOTG_DCINTERRUPT_IEVBUS |
+ SOTG_DCINTERRUPT_IEBRST | SOTG_DCINTERRUPT_IESUSP;
+ SAF1761_WRITE_LE_4(sc, SOTG_DCINTERRUPT_EN, sc->sc_intr_enable);
+
+ /*
+ * Connect ATX port 1 to device controller, select external
+ * charge pump and driver VBUS to +5V:
+ */
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_SET_CLR,
+ SOTG_CTRL_CLR(0xFFFF));
+ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_SET_CLR,
+ SOTG_CTRL_SET(SOTG_CTRL_SW_SEL_HC_DC |
+ SOTG_CTRL_BDIS_ACON_EN | SOTG_CTRL_SEL_CP_EXT |
+ SOTG_CTRL_VBUS_DRV));
+
+ /* disable device address */
+ SAF1761_WRITE_LE_4(sc, SOTG_ADDRESS, 0);
+
+ /* enable host controller clock and preserve reserved bits */
+ x = SAF1761_READ_LE_4(sc, SOTG_POWER_DOWN);
+ SAF1761_WRITE_LE_4(sc, SOTG_POWER_DOWN, x | 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_LE_4(sc, SOTG_CONFIGFLAG, SOTG_CONFIGFLAG_ENABLE);
+
+ /* clear RAM block */
+ for (x = 0x400; x != 0x10000; x += 4)
+ SAF1761_WRITE_LE_4(sc, x, 0);
+
+ /* start the HC */
+ SAF1761_WRITE_LE_4(sc, SOTG_USBCMD, SOTG_USBCMD_RS);
+
+ DPRINTF("USBCMD=0x%08x\n", SAF1761_READ_LE_4(sc, SOTG_USBCMD));
+
+ /* make HC scan all PTDs */
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_LAST_PTD, (1 << 31));
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_LAST_PTD, (1 << 31));
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_LAST_PTD, (1 << 31));
+
+ /* skip all PTDs by default */
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD, -1U);
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD, -1U);
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD, -1U);
+
+ /* activate all PTD types */
+ SAF1761_WRITE_LE_4(sc, SOTG_HCBUFFERSTATUS,
+ SOTG_HCBUFFERSTATUS_ISO_BUF_FILL |
+ SOTG_HCBUFFERSTATUS_INT_BUF_FILL |
+ SOTG_HCBUFFERSTATUS_ATL_BUF_FILL);
+
+ /* we don't use the AND mask */
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_IRQ_MASK_AND, 0);
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_IRQ_MASK_AND, 0);
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_IRQ_MASK_AND, 0);
+
+ /* enable all PTD OR interrupts by default */
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_IRQ_MASK_OR, -1U);
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_IRQ_MASK_OR, -1U);
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_IRQ_MASK_OR, -1U);
+
+ /* enable HC interrupts */
+ SAF1761_WRITE_LE_4(sc, SOTG_HCINTERRUPT_ENABLE,
+ SOTG_HCINTERRUPT_OTG_IRQ |
+ SOTG_HCINTERRUPT_ISO_IRQ |
+ SOTG_HCINTERRUPT_ALT_IRQ |
+ SOTG_HCINTERRUPT_INT_IRQ);
+
+ /* poll initial VBUS status */
+ saf1761_otg_update_vbus(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+
+ saf1761_otg_do_poll(&sc->sc_bus);
+
+ return (0); /* success */
+}
+
+void
+saf1761_otg_uninit(struct saf1761_otg_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* disable all interrupts */
+ SAF1761_WRITE_LE_4(sc, SOTG_MODE, 0);
+
+ sc->sc_flags.port_powered = 0;
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ saf1761_otg_pull_down(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+saf1761_otg_suspend(struct saf1761_otg_softc *sc)
+{
+ /* TODO */
+}
+
+static void
+saf1761_otg_resume(struct saf1761_otg_softc *sc)
+{
+ /* TODO */
+}
+
+static void
+saf1761_otg_do_poll(struct usb_bus *bus)
+{
+ struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+ saf1761_otg_interrupt_poll_locked(sc);
+ saf1761_otg_interrupt_complete_locked(sc);
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * saf1761_otg control support
+ * saf1761_otg interrupt support
+ * saf1761_otg bulk support
+ *------------------------------------------------------------------------*/
+static void
+saf1761_otg_device_non_isoc_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+saf1761_otg_device_non_isoc_close(struct usb_xfer *xfer)
+{
+ saf1761_otg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+saf1761_otg_device_non_isoc_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+saf1761_otg_device_non_isoc_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ saf1761_otg_setup_standard_chain(xfer);
+ saf1761_otg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods saf1761_otg_non_isoc_methods =
+{
+ .open = saf1761_otg_device_non_isoc_open,
+ .close = saf1761_otg_device_non_isoc_close,
+ .enter = saf1761_otg_device_non_isoc_enter,
+ .start = saf1761_otg_device_non_isoc_start,
+};
+
+/*------------------------------------------------------------------------*
+ * saf1761_otg device side isochronous support
+ *------------------------------------------------------------------------*/
+static void
+saf1761_otg_device_isoc_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+saf1761_otg_device_isoc_close(struct usb_xfer *xfer)
+{
+ saf1761_otg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+saf1761_otg_device_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_FRAME_NUM);
+
+ /*
+ * check if the frame index is within the window where the
+ * frames will be inserted
+ */
+ temp = (nframes - xfer->endpoint->isoc_next) & SOTG_FRAME_NUM_SOFR_MASK;
+
+ 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_FRAME_NUM_SOFR_MASK;
+ 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_FRAME_NUM_SOFR_MASK;
+
+ /*
+ * 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_device_isoc_start(struct usb_xfer *xfer)
+{
+ /* start TD chain */
+ saf1761_otg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods saf1761_otg_device_isoc_methods =
+{
+ .open = saf1761_otg_device_isoc_open,
+ .close = saf1761_otg_device_isoc_close,
+ .enter = saf1761_otg_device_isoc_enter,
+ .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
+ *------------------------------------------------------------------------*
+ * Simulate a hardware HUB by handling all the necessary requests.
+ *------------------------------------------------------------------------*/
+
+#define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
+
+static const struct usb_device_descriptor saf1761_otg_devd = {
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = UDESC_DEVICE,
+ HSETW(.idVendor, 0x04cc),
+ HSETW(.idProduct, 0x1761),
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize = 64,
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb_device_qualifier saf1761_otg_odevd = {
+ .bLength = sizeof(struct usb_device_qualifier),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+};
+
+static const struct saf1761_otg_config_desc saf1761_otg_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(saf1761_otg_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = 0,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = (UE_DIR_IN | SAF1761_OTG_INTR_ENDPT),
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 255,
+ },
+};
+
+static const struct usb_hub_descriptor_min saf1761_otg_hubd = {
+ .bDescLength = sizeof(saf1761_otg_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = SOTG_NUM_PORTS,
+ HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)),
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0}, /* port is removable */
+};
+
+#define STRING_VENDOR \
+ "N\0X\0P"
+
+#define STRING_PRODUCT \
+ "D\0C\0I\0 \0R\0o\0o\0t\0 \0H\0U\0B"
+
+USB_MAKE_STRING_DESC(STRING_VENDOR, saf1761_otg_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, saf1761_otg_product);
+
+static usb_error_t
+saf1761_otg_roothub_exec(struct usb_device *udev,
+ struct usb_device_request *req, const void **pptr, uint16_t *plength)
+{
+ struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(udev->bus);
+ const void *ptr;
+ 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);
+
+ /* buffer reset */
+ ptr = (const void *)&sc->sc_hub_temp;
+ len = 0;
+ err = 0;
+
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ /* demultiplex the control request */
+
+ switch (req->bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (req->bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_descriptor;
+ case UR_GET_CONFIG:
+ goto tr_handle_get_config;
+ case UR_GET_STATUS:
+ goto tr_handle_get_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_DEVICE:
+ switch (req->bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_DESCRIPTOR:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (req->bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (UGETW(req->wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (UGETW(req->wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SYNCH_FRAME:
+ goto tr_valid; /* nop */
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (req->bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_INTERFACE:
+ switch (req->bRequest) {
+ case UR_SET_INTERFACE:
+ goto tr_handle_set_interface;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (req->bRequest) {
+ case UR_GET_INTERFACE:
+ goto tr_handle_get_interface;
+ case UR_GET_STATUS:
+ goto tr_handle_get_iface_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_INTERFACE:
+ case UT_WRITE_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_READ_CLASS_INTERFACE:
+ case UT_READ_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_WRITE_CLASS_DEVICE:
+ switch (req->bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_valid;
+ case UR_SET_DESCRIPTOR:
+ case UR_SET_FEATURE:
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_OTHER:
+ switch (req->bRequest) {
+ case UR_CLEAR_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:
+ 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:
+ goto tr_valid;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_OTHER:
+ switch (req->bRequest) {
+ case UR_GET_TT_STATE:
+ goto tr_handle_get_tt_state;
+ case UR_GET_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;
+ }
+ break;
+
+ case UT_READ_CLASS_DEVICE:
+ switch (req->bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ case UR_GET_STATUS:
+ goto tr_handle_get_class_status;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if (value & 0xff)
+ goto tr_stalled;
+ len = sizeof(saf1761_otg_devd);
+ ptr = (const void *)&saf1761_otg_devd;
+ goto tr_valid;
+ case UDESC_DEVICE_QUALIFIER:
+ if (value & 0xff)
+ goto tr_stalled;
+ len = sizeof(saf1761_otg_odevd);
+ ptr = (const void *)&saf1761_otg_odevd;
+ goto tr_valid;
+ case UDESC_CONFIG:
+ if (value & 0xff)
+ goto tr_stalled;
+ len = sizeof(saf1761_otg_confd);
+ ptr = (const void *)&saf1761_otg_confd;
+ goto tr_valid;
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ len = sizeof(usb_string_lang_en);
+ ptr = (const void *)&usb_string_lang_en;
+ goto tr_valid;
+
+ case 1: /* Vendor */
+ len = sizeof(saf1761_otg_vendor);
+ ptr = (const void *)&saf1761_otg_vendor;
+ goto tr_valid;
+
+ case 2: /* Product */
+ len = sizeof(saf1761_otg_product);
+ ptr = (const void *)&saf1761_otg_product;
+ goto tr_valid;
+ default:
+ break;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_config:
+ len = 1;
+ sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+ goto tr_valid;
+
+tr_handle_get_status:
+ len = 2;
+ USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (value & 0xFF00)
+ goto tr_stalled;
+
+ sc->sc_rt_addr = value;
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (value >= 2)
+ goto tr_stalled;
+ sc->sc_conf = value;
+ goto tr_valid;
+
+tr_handle_get_interface:
+ len = 1;
+ sc->sc_hub_temp.wValue[0] = 0;
+ goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+ len = 2;
+ USETW(sc->sc_hub_temp.wValue, 0);
+ goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+ goto tr_valid;
+
+tr_handle_clear_port_feature_device:
+ DPRINTFN(9, "UR_CLEAR_FEATURE on port %d\n", index);
+
+ switch (value) {
+ case UHF_PORT_SUSPEND:
+ saf1761_otg_wakeup_peer(sc);
+ break;
+
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 0;
+ break;
+
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 0;
+ saf1761_otg_pull_down(sc);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_flags.change_connect = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_flags.change_suspend = 0;
+ break;
+ default:
+ err = USB_ERR_IOERROR;
+ goto tr_valid;
+ }
+ goto tr_valid;
+
+tr_handle_clear_port_feature_host:
+ DPRINTFN(9, "UR_CLEAR_FEATURE on port %d\n", index);
+
+ temp = SAF1761_READ_LE_4(sc, SOTG_PORTSC1);
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp & ~SOTG_PORTSC1_PED);
+ break;
+ case UHF_PORT_SUSPEND:
+ if ((temp & SOTG_PORTSC1_SUSP) && (!(temp & SOTG_PORTSC1_FPR)))
+ SAF1761_WRITE_LE_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_LE_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_LE_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_LE_4(sc, SOTG_PORTSC1, temp & ~SOTG_PORTSC1_PP);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ SAF1761_WRITE_LE_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:
+ sc->sc_flags.port_enabled = 1;
+ break;
+ case UHF_PORT_SUSPEND:
+ case UHF_PORT_RESET:
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 1;
+ break;
+ default:
+ err = USB_ERR_IOERROR;
+ goto tr_valid;
+ }
+ goto tr_valid;
+
+tr_handle_set_port_feature_host:
+ DPRINTFN(9, "UR_SET_FEATURE on port %d\n", index);
+
+ temp = SAF1761_READ_LE_4(sc, SOTG_PORTSC1);
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_PED);
+ break;
+ case UHF_PORT_SUSPEND:
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(6, "reset port %d\n", index);
+
+ /* Start reset sequence. */
+ temp &= ~(SOTG_PORTSC1_PED | SOTG_PORTSC1_PR);
+
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_PR);
+
+ /* Wait for reset to complete. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(usb_port_root_reset_delay));
+
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp);
+
+ /* Wait for HC to complete reset. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(2));
+
+ temp = SAF1761_READ_LE_4(sc, SOTG_PORTSC1);
+
+ DPRINTF("After reset, status=0x%08x\n", temp);
+ if (temp & SOTG_PORTSC1_PR) {
+ device_printf(sc->sc_bus.bdev, "port reset timeout\n");
+ err = USB_ERR_TIMEOUT;
+ goto tr_valid;
+ }
+ if (!(temp & SOTG_PORTSC1_PED)) {
+ /* Not a high speed device, give up ownership.*/
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_PO);
+ break;
+ }
+ sc->sc_isreset = 1;
+ DPRINTF("port %d reset, status = 0x%08x\n", index, temp);
+ break;
+ case UHF_PORT_POWER:
+ DPRINTFN(3, "set port power %d\n", index);
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_PP);
+ break;
+
+ case UHF_PORT_TEST:
+ DPRINTFN(3, "set port test %d\n", index);
+ break;
+
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(3, "set port ind %d\n", index);
+ SAF1761_WRITE_LE_4(sc, SOTG_PORTSC1, temp | SOTG_PORTSC1_PIC);
+ break;
+ default:
+ err = USB_ERR_IOERROR;
+ goto tr_valid;
+ }
+ goto tr_valid;
+
+tr_handle_get_port_status_device:
+
+ DPRINTFN(9, "UR_GET_PORT_STATUS on port %d\n", index);
+
+ if (sc->sc_flags.status_vbus) {
+ saf1761_otg_pull_up(sc);
+ } else {
+ saf1761_otg_pull_down(sc);
+ }
+
+ /* Select FULL-speed and Device Side Mode */
+
+ value = UPS_PORT_MODE_DEVICE;
+
+ if (sc->sc_flags.port_powered)
+ value |= UPS_PORT_POWER;
+
+ if (sc->sc_flags.port_enabled)
+ value |= UPS_PORT_ENABLED;
+
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset)
+ value |= UPS_CURRENT_CONNECT_STATUS;
+
+ if (sc->sc_flags.status_suspend)
+ value |= UPS_SUSPEND;
+
+ USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+ value = 0;
+
+ if (sc->sc_flags.change_connect)
+ value |= UPS_C_CONNECT_STATUS;
+
+ if (sc->sc_flags.change_suspend)
+ value |= UPS_C_SUSPEND;
+
+ USETW(sc->sc_hub_temp.ps.wPortChange, value);
+ len = sizeof(sc->sc_hub_temp.ps);
+ goto tr_valid;
+
+tr_handle_get_port_status_host:
+
+ temp = SAF1761_READ_LE_4(sc, SOTG_PORTSC1);
+
+ DPRINTFN(9, "UR_GET_PORT_STATUS on port %d = 0x%08x\n", index, temp);
+
+ i = UPS_HIGH_SPEED;
+
+ if (temp & SOTG_PORTSC1_ECCS)
+ i |= UPS_CURRENT_CONNECT_STATUS;
+ if (temp & SOTG_PORTSC1_PED)
+ i |= UPS_PORT_ENABLED;
+ if ((temp & SOTG_PORTSC1_SUSP) && !(temp & SOTG_PORTSC1_FPR))
+ i |= UPS_SUSPEND;
+ if (temp & SOTG_PORTSC1_PR)
+ i |= UPS_RESET;
+ if (temp & SOTG_PORTSC1_PP)
+ i |= UPS_PORT_POWER;
+
+ USETW(sc->sc_hub_temp.ps.wPortStatus, i);
+ i = 0;
+
+ if (temp & SOTG_PORTSC1_ECSC)
+ i |= UPS_C_CONNECT_STATUS;
+ if (temp & SOTG_PORTSC1_FPR)
+ i |= UPS_C_SUSPEND;
+ if (sc->sc_isreset)
+ i |= UPS_C_PORT_RESET;
+ USETW(sc->sc_hub_temp.ps.wPortChange, i);
+ len = sizeof(sc->sc_hub_temp.ps);
+ goto tr_valid;
+
+tr_handle_get_class_descriptor:
+ if (value & 0xFF)
+ goto tr_stalled;
+ ptr = (const void *)&saf1761_otg_hubd;
+ len = sizeof(saf1761_otg_hubd);
+ goto tr_valid;
+
+tr_stalled:
+ err = USB_ERR_STALLED;
+tr_valid:
+ *plength = len;
+ *pptr = ptr;
+ return (err);
+}
+
+static void
+saf1761_otg_xfer_setup(struct usb_setup_params *parm)
+{
+ struct saf1761_otg_softc *sc;
+ struct usb_xfer *xfer;
+ void *last_obj;
+ uint32_t dw1;
+ uint32_t ntd;
+ uint32_t n;
+ uint8_t ep_no;
+ uint8_t ep_type;
+
+ sc = SAF1761_OTG_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+ parm->hc_max_packet_size = 0x500;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x500;
+
+ usbd_transfer_setup_sub(parm);
+
+ /*
+ * Compute maximum number of TDs:
+ */
+ ep_type = (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE);
+
+ if (ep_type == UE_CONTROL) {
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ;
+
+ } else {
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+ }
+
+ /*
+ * check if "usbd_transfer_setup_sub" set an error
+ */
+ if (parm->err)
+ return;
+
+ /*
+ * allocate transfer descriptors
+ */
+ last_obj = NULL;
+
+ ep_no = xfer->endpointno & UE_ADDR;
+
+ /*
+ * Check profile stuff
+ */
+ if (parm->udev->flags.usb_mode == USB_MODE_DEVICE) {
+ const struct usb_hw_ep_profile *pf;
+
+ saf1761_otg_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+ if (pf == NULL) {
+ /* should not happen */
+ parm->err = USB_ERR_INVAL;
+ return;
+ }
+ }
+
+ dw1 = (xfer->address << 3) | (ep_type << 12);
+
+ switch (parm->udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ /* check if root HUB port is running High Speed */
+ if (parm->udev->parent_hs_hub != NULL) {
+ dw1 |= SOTG_PTD_DW1_ENABLE_SPLIT;
+ dw1 |= (parm->udev->hs_port_no << 18);
+ dw1 |= (parm->udev->hs_hub_addr << 25);
+ if (parm->udev->speed == USB_SPEED_LOW)
+ dw1 |= (1 << 17);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ for (n = 0; n != ntd; n++) {
+
+ struct saf1761_otg_td *td;
+
+ if (parm->buf) {
+
+ td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ /* init TD */
+ td->max_packet_size = xfer->max_packet_size;
+ td->ep_index = ep_no;
+ td->ep_type = ep_type;
+ td->dw1_value = dw1;
+ 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;
+ else
+ td->interval = (xfer->interval / 2) << 3;
+ } else {
+ td->interval = 0;
+ }
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ parm->size[0] += sizeof(*td);
+ }
+
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+saf1761_otg_xfer_unsetup(struct usb_xfer *xfer)
+{
+}
+
+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);
+
+ if (udev->parent_hub == NULL) {
+ /* root HUB has special endpoint handling */
+ 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) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_ISOCHRONOUS:
+ ep->methods = &saf1761_otg_device_isoc_methods;
+ break;
+ default:
+ ep->methods = &saf1761_otg_non_isoc_methods;
+ break;
+ }
+ } else {
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_ISOCHRONOUS:
+ ep->methods = &saf1761_otg_host_isoc_methods;
+ break;
+ default:
+ ep->methods = &saf1761_otg_non_isoc_methods;
+ break;
+ }
+ }
+}
+
+static void
+saf1761_otg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
+{
+ struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(bus);
+
+ switch (state) {
+ case USB_HW_POWER_SUSPEND:
+ saf1761_otg_suspend(sc);
+ break;
+ case USB_HW_POWER_SHUTDOWN:
+ saf1761_otg_uninit(sc);
+ break;
+ case USB_HW_POWER_RESUME:
+ saf1761_otg_resume(sc);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+saf1761_otg_device_resume(struct usb_device *udev)
+{
+ struct saf1761_otg_softc *sc;
+ struct saf1761_otg_td *td;
+ struct usb_xfer *xfer;
+ uint8_t x;
+
+ DPRINTF("\n");
+
+ if (udev->flags.usb_mode != USB_MODE_HOST)
+ return;
+
+ sc = SAF1761_OTG_BUS2SC(udev->bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev != udev)
+ continue;
+
+ td = xfer->td_transfer_cache;
+ if (td == NULL || td->channel >= SOTG_HOST_CHANNEL_MAX)
+ continue;
+
+ switch (td->ep_type) {
+ case UE_INTERRUPT:
+ x = td->channel - 32;
+ sc->sc_host_intr_suspend_map &= ~(1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
+ (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+ break;
+ case UE_ISOCHRONOUS:
+ x = td->channel;
+ sc->sc_host_isoc_suspend_map &= ~(1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
+ (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
+ break;
+ default:
+ x = td->channel - 64;
+ sc->sc_host_async_suspend_map &= ~(1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+ (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+ break;
+ }
+ }
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* poll all transfers again to restart resumed ones */
+ saf1761_otg_do_poll(&sc->sc_bus);
+}
+
+static void
+saf1761_otg_device_suspend(struct usb_device *udev)
+{
+ struct saf1761_otg_softc *sc;
+ struct saf1761_otg_td *td;
+ struct usb_xfer *xfer;
+ uint8_t x;
+
+ DPRINTF("\n");
+
+ if (udev->flags.usb_mode != USB_MODE_HOST)
+ return;
+
+ sc = SAF1761_OTG_BUS2SC(udev->bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ USB_BUS_SPIN_LOCK(&sc->sc_bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev != udev)
+ continue;
+
+ td = xfer->td_transfer_cache;
+ if (td == NULL || td->channel >= SOTG_HOST_CHANNEL_MAX)
+ continue;
+
+ switch (td->ep_type) {
+ case UE_INTERRUPT:
+ x = td->channel - 32;
+ sc->sc_host_intr_suspend_map |= (1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
+ (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
+ break;
+ case UE_ISOCHRONOUS:
+ x = td->channel;
+ sc->sc_host_isoc_suspend_map |= (1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
+ (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
+ break;
+ default:
+ x = td->channel - 64;
+ sc->sc_host_async_suspend_map |= (1 << x);
+ SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
+ (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
+ break;
+ }
+ }
+
+ USB_BUS_SPIN_UNLOCK(&sc->sc_bus);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static const struct usb_bus_methods saf1761_otg_bus_methods =
+{
+ .endpoint_init = &saf1761_otg_ep_init,
+ .xfer_setup = &saf1761_otg_xfer_setup,
+ .xfer_unsetup = &saf1761_otg_xfer_unsetup,
+ .get_hw_ep_profile = &saf1761_otg_get_hw_ep_profile,
+ .xfer_stall = &saf1761_otg_xfer_stall,
+ .set_stall = &saf1761_otg_set_stall,
+ .clear_stall = &saf1761_otg_clear_stall,
+ .roothub_exec = &saf1761_otg_roothub_exec,
+ .xfer_poll = &saf1761_otg_do_poll,
+ .set_hw_power_sleep = saf1761_otg_set_hw_power_sleep,
+ .device_resume = &saf1761_otg_device_resume,
+ .device_suspend = &saf1761_otg_device_suspend,
+};
diff --git a/sys/dev/usb/controller/saf1761_otg.h b/sys/dev/usb/controller/saf1761_otg.h
new file mode 100644
index 0000000000000..6d68e3e1bb233
--- /dev/null
+++ b/sys/dev/usb/controller/saf1761_otg.h
@@ -0,0 +1,172 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SAF1761_OTG_H_
+#define _SAF1761_OTG_H_
+
+#define SOTG_MAX_DEVICES MIN(USB_MAX_DEVICES, 32)
+#define SOTG_FS_MAX_PACKET_SIZE 64
+#define SOTG_HS_MAX_PACKET_SIZE 512
+#define SOTG_NUM_PORTS 2 /* one Device and one Host port */
+#define SOTG_HOST_PORT_NUM 1
+#define SOTG_DEVICE_PORT_NUM 2
+#define SOTG_HOST_CHANNEL_MAX (3 * 32)
+
+/* Macros used for reading and writing little endian registers */
+
+#define SAF1761_READ_LE_4(sc, reg) ({ uint32_t _temp; \
+ _temp = bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (reg)); \
+ le32toh(_temp); })
+
+#define SAF1761_WRITE_LE_4(sc, reg, data) do { \
+ uint32_t _temp = (data); \
+ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (reg), htole32(_temp)); \
+} while (0)
+
+/* 90ns delay macro */
+
+#define SAF1761_90NS_DELAY(sc) do { \
+ (void) SAF1761_READ_LE_4(sc, SOTG_VEND_PROD_ID); \
+ (void) SAF1761_READ_LE_4(sc, SOTG_VEND_PROD_ID); \
+ (void) SAF1761_READ_LE_4(sc, SOTG_VEND_PROD_ID); \
+ (void) SAF1761_READ_LE_4(sc, SOTG_VEND_PROD_ID); \
+} while (0)
+
+struct saf1761_otg_softc;
+struct saf1761_otg_td;
+
+typedef uint8_t (saf1761_otg_cmd_t)(struct saf1761_otg_softc *, struct saf1761_otg_td *td);
+
+struct saf1761_otg_td {
+ struct saf1761_otg_td *obj_next;
+ saf1761_otg_cmd_t *func;
+ struct usb_page_cache *pc;
+ uint32_t offset;
+ uint32_t remainder;
+ uint32_t dw1_value;
+ uint16_t max_packet_size;
+ uint8_t ep_index;
+ uint8_t ep_type;
+ uint8_t channel;
+ uint8_t uframe;
+ uint8_t interval;
+ uint8_t error_any:1;
+ uint8_t error_stall:1;
+ uint8_t alt_next:1;
+ uint8_t short_pkt:1;
+ uint8_t did_stall:1;
+ uint8_t toggle:1;
+ uint8_t set_toggle:1;
+};
+
+struct saf1761_otg_std_temp {
+ saf1761_otg_cmd_t *func;
+ struct usb_page_cache *pc;
+ struct saf1761_otg_td *td;
+ struct saf1761_otg_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t max_frame_size;
+ uint8_t short_pkt;
+ /*
+ * short_pkt = 0: transfer should be short terminated
+ * short_pkt = 1: transfer should not be short terminated
+ */
+ uint8_t setup_alt_next;
+ uint8_t did_stall;
+};
+
+struct saf1761_otg_config_desc {
+ struct usb_config_descriptor confd;
+ struct usb_interface_descriptor ifcd;
+ struct usb_endpoint_descriptor endpd;
+} __packed;
+
+union saf1761_otg_hub_temp {
+ uWord wValue;
+ struct usb_port_status ps;
+};
+
+struct saf1761_otg_flags {
+ uint8_t change_connect:1;
+ uint8_t change_suspend:1;
+ uint8_t status_suspend:1; /* set if suspended */
+ uint8_t status_vbus:1; /* set if present */
+ uint8_t status_bus_reset:1; /* set if reset complete */
+ uint8_t clocks_off:1;
+ uint8_t port_powered:1;
+ uint8_t port_enabled:1;
+ uint8_t d_pulled_up:1;
+};
+
+struct saf1761_otg_softc {
+ struct usb_bus sc_bus;
+ union saf1761_otg_hub_temp sc_hub_temp;
+
+ struct usb_device *sc_devices[SOTG_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ uint32_t sc_host_async_map;
+ uint32_t sc_host_async_suspend_map;
+ uint32_t sc_host_intr_map;
+ uint32_t sc_host_intr_suspend_map;
+ uint32_t sc_host_isoc_map;
+ uint32_t sc_host_isoc_suspend_map;
+ uint32_t sc_intr_enable; /* enabled interrupts */
+ uint32_t sc_hw_mode; /* hardware mode */
+ uint32_t sc_interrupt_cfg; /* interrupt configuration */
+ uint32_t sc_xfer_complete;
+
+ uint32_t sc_bounce_buffer[1024 / 4];
+
+ uint8_t sc_rt_addr; /* root HUB address */
+ uint8_t sc_dv_addr; /* device address */
+ uint8_t sc_conf; /* root HUB config */
+ uint8_t sc_isreset; /* host mode */
+
+ uint8_t sc_hub_idata[1];
+
+ struct saf1761_otg_flags sc_flags;
+};
+
+/* prototypes */
+
+usb_error_t saf1761_otg_init(struct saf1761_otg_softc *sc);
+void saf1761_otg_uninit(struct saf1761_otg_softc *sc);
+driver_filter_t saf1761_otg_filter_interrupt;
+driver_intr_t saf1761_otg_interrupt;
+
+#endif /* _SAF1761_OTG_H_ */
diff --git a/sys/dev/usb/controller/saf1761_otg_boot.c b/sys/dev/usb/controller/saf1761_otg_boot.c
new file mode 100644
index 0000000000000..ead8de30e5543
--- /dev/null
+++ b/sys/dev/usb/controller/saf1761_otg_boot.c
@@ -0,0 +1,146 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include USB_GLOBAL_INCLUDE_FILE
+
+#include <dev/usb/controller/saf1761_otg.h>
+#include <dev/usb/controller/saf1761_otg_reg.h>
+
+static device_probe_t saf1761_otg_fdt_probe;
+static device_attach_t saf1761_otg_fdt_attach;
+static device_detach_t saf1761_otg_fdt_detach;
+
+static device_method_t saf1761_otg_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, saf1761_otg_fdt_probe),
+ DEVMETHOD(device_attach, saf1761_otg_fdt_attach),
+ DEVMETHOD(device_detach, saf1761_otg_fdt_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t saf1761_otg_driver = {
+ .name = "saf1761otg",
+ .methods = saf1761_otg_methods,
+ .size = sizeof(struct saf1761_otg_softc),
+};
+
+static devclass_t saf1761_otg_devclass;
+
+DRIVER_MODULE(saf1761otg, pci, saf1761_otg_driver, saf1761_otg_devclass, 0, 0);
+MODULE_DEPEND(saf1761otg, usb, 1, 1, 1);
+
+static int
+saf1761_otg_fdt_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "ISP1761/SAF1761 DCI USB 2.0 Device Controller");
+
+ return (0);
+}
+
+static int
+saf1761_otg_fdt_attach(device_t dev)
+{
+ struct saf1761_otg_softc *sc = device_get_softc(dev);
+ int err;
+
+ /* 32-bit data bus */
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = SOTG_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), NULL))
+ return (ENOMEM);
+
+ sc->sc_io_res = (void *)1;
+ sc->sc_io_tag = (void *)1;
+ sc->sc_io_hdl = (void *)USB_PCI_MEMORY_ADDRESS;
+ sc->sc_io_size = USB_PCI_MEMORY_SIZE;
+
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (sc->sc_bus.bdev == NULL)
+ goto error;
+
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_interrupt(dev, &saf1761_otg_filter_interrupt, &saf1761_otg_interrupt, sc);
+
+ err = saf1761_otg_init(sc);
+ if (err) {
+ device_printf(dev, "Init failed\n");
+ goto error;
+ }
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err) {
+ device_printf(dev, "USB probe and attach failed\n");
+ goto error;
+ }
+ return (0);
+
+error:
+ saf1761_otg_fdt_detach(dev);
+ return (ENXIO);
+}
+
+static int
+saf1761_otg_fdt_detach(device_t dev)
+{
+ struct saf1761_otg_softc *sc = device_get_softc(dev);
+ device_t bdev;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (sc->sc_irq_res) {
+ /*
+ * Only call uninit() after init()
+ */
+ saf1761_otg_uninit(sc);
+ }
+ usb_bus_mem_free_all(&sc->sc_bus, NULL);
+
+ return (0);
+}
diff --git a/sys/dev/usb/controller/saf1761_otg_fdt.c b/sys/dev/usb/controller/saf1761_otg_fdt.c
new file mode 100644
index 0000000000000..1b18aea33c4db
--- /dev/null
+++ b/sys/dev/usb/controller/saf1761_otg_fdt.c
@@ -0,0 +1,274 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/rman.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#endif /* USB_GLOBAL_INCLUDE_FILE */
+
+#include <dev/usb/controller/saf1761_otg.h>
+#include <dev/usb/controller/saf1761_otg_reg.h>
+
+static device_probe_t saf1761_otg_fdt_probe;
+static device_attach_t saf1761_otg_fdt_attach;
+static device_detach_t saf1761_otg_fdt_detach;
+
+static device_method_t saf1761_otg_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, saf1761_otg_fdt_probe),
+ DEVMETHOD(device_attach, saf1761_otg_fdt_attach),
+ DEVMETHOD(device_detach, saf1761_otg_fdt_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t saf1761_otg_driver = {
+ .name = "saf1761otg",
+ .methods = saf1761_otg_methods,
+ .size = sizeof(struct saf1761_otg_softc),
+};
+
+static devclass_t saf1761_otg_devclass;
+
+DRIVER_MODULE(saf1761otg, simplebus, saf1761_otg_driver, saf1761_otg_devclass, 0, 0);
+MODULE_DEPEND(saf1761otg, usb, 1, 1, 1);
+
+static int
+saf1761_otg_fdt_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "nxp,usb-isp1761"))
+ return (ENXIO);
+
+ device_set_desc(dev, "ISP1761/SAF1761 DCI USB 2.0 Device Controller");
+
+ return (0);
+}
+
+static int
+saf1761_otg_fdt_attach(device_t dev)
+{
+ struct saf1761_otg_softc *sc = device_get_softc(dev);
+ char param[24];
+ int err;
+ int rid;
+
+ /* get configuration from FDT */
+
+ /* get bus-width, if any */
+ if (OF_getprop(ofw_bus_get_node(dev), "bus-width",
+ &param, sizeof(param)) > 0) {
+ param[sizeof(param) - 1] = 0;
+ if (strcmp(param, "32") == 0)
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH;
+ } else {
+ /* assume 32-bit data bus */
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH;
+ }
+
+ /* get analog over-current setting */
+ if (OF_getprop(ofw_bus_get_node(dev), "analog-oc",
+ &param, sizeof(param)) > 0) {
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_ANA_DIGI_OC;
+ }
+
+ /* get DACK polarity */
+ if (OF_getprop(ofw_bus_get_node(dev), "dack-polarity",
+ &param, sizeof(param)) > 0) {
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DACK_POL;
+ }
+
+ /* get DREQ polarity */
+ if (OF_getprop(ofw_bus_get_node(dev), "dreq-polarity",
+ &param, sizeof(param)) > 0) {
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DREQ_POL;
+ }
+
+ /* get IRQ polarity */
+ if (OF_getprop(ofw_bus_get_node(dev), "int-polarity",
+ &param, sizeof(param)) > 0) {
+ sc->sc_interrupt_cfg |= SOTG_INTERRUPT_CFG_INTPOL;
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_INTR_POL;
+ }
+
+ /* get IRQ level triggering */
+ if (OF_getprop(ofw_bus_get_node(dev), "int-level",
+ &param, sizeof(param)) > 0) {
+ sc->sc_interrupt_cfg |= SOTG_INTERRUPT_CFG_INTLVL;
+ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_INTR_LEVEL;
+ }
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = SOTG_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ return (ENOMEM);
+ }
+ rid = 0;
+ sc->sc_io_res =
+ bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+
+ if (sc->sc_io_res == NULL)
+ goto error;
+
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ /* try to allocate the HC interrupt first */
+ rid = 1;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ /* try to allocate a common IRQ second */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL)
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (sc->sc_bus.bdev == NULL)
+ goto error;
+
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE,
+ &saf1761_otg_filter_interrupt, &saf1761_otg_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ err = saf1761_otg_init(sc);
+ if (err) {
+ device_printf(dev, "Init failed\n");
+ goto error;
+ }
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err) {
+ device_printf(dev, "USB probe and attach failed\n");
+ goto error;
+ }
+ return (0);
+
+error:
+ saf1761_otg_fdt_detach(dev);
+ return (ENXIO);
+}
+
+static int
+saf1761_otg_fdt_detach(device_t dev)
+{
+ struct saf1761_otg_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * Only call uninit() after init()
+ */
+ saf1761_otg_uninit(sc);
+
+ err = bus_teardown_intr(dev, sc->sc_irq_res,
+ sc->sc_intr_hdl);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb_bus_mem_free_all(&sc->sc_bus, NULL);
+
+ return (0);
+}
diff --git a/sys/dev/usb/controller/saf1761_otg_reg.h b/sys/dev/usb/controller/saf1761_otg_reg.h
new file mode 100644
index 0000000000000..d73718f20ddcf
--- /dev/null
+++ b/sys/dev/usb/controller/saf1761_otg_reg.h
@@ -0,0 +1,273 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SAF1761_OTG_REG_H_
+#define _SAF1761_OTG_REG_H_
+
+/* Global registers */
+
+#define SOTG_VEND_PROD_ID 0x370
+#define SOTG_VEND_ID(x) ((x) & 0xFFFF)
+#define SOTG_PROD_ID(x) (((x) >> 16) & 0xFFFF)
+#define SOTG_CTRL_SET_CLR 0x374
+#define SOTG_CTRL_SET(x) ((x) & 0xFFFF)
+#define SOTG_CTRL_CLR(x) (((x) << 16) & 0xFFFF0000)
+#define SOTG_CTRL_OTG_DISABLE (1 << 10)
+#define SOTG_CTRL_OTG_SE0_EN (1 << 9)
+#define SOTG_CTRL_BDIS_ACON_EN (1 << 8)
+#define SOTG_CTRL_SW_SEL_HC_DC (1 << 7)
+#define SOTG_CTRL_VBUS_CHRG (1 << 6)
+#define SOTG_CTRL_VBUS_DISCHRG (1 << 5)
+#define SOTG_CTRL_VBUS_DRV (1 << 4)
+#define SOTG_CTRL_SEL_CP_EXT (1 << 3)
+#define SOTG_CTRL_DM_PULL_DOWN (1 << 2)
+#define SOTG_CTRL_DP_PULL_DOWN (1 << 1)
+#define SOTG_CTRL_DP_PULL_UP (1 << 0)
+#define SOTG_STATUS 0x378
+#define SOTG_STATUS_B_SE0_SRP (1 << 8)
+#define SOTG_STATUS_B_SESS_END (1 << 7)
+#define SOTG_STATUS_RMT_CONN (1 << 4)
+#define SOTG_STATUS_ID (1 << 3)
+#define SOTG_STATUS_DP_SRP (1 << 2)
+#define SOTG_STATUS_A_B_SESS_VLD (1 << 1)
+#define SOTG_STATUS_VBUS_VLD (1 << 0)
+#define SOTG_IRQ_LATCH_SET_CLR 0x37C
+#define SOTG_IRQ_LATCH_SET(x) ((x) & 0xFFFF)
+#define SOTG_IRQ_LATCH_CLR(x) (((x) << 16) & 0xFFFF0000)
+#define SOTG_IRQ_ENABLE_SET_CLR 0x380
+#define SOTG_IRQ_ENABLE_SET(x) ((x) & 0xFFFF)
+#define SOTG_IRQ_ENABLE_CLR(x) (((x) << 16) & 0xFFFF0000)
+#define SOTG_IRQ_RISE_SET_CLR 0x384
+#define SOTG_IRQ_RISE_SET(x) ((x) & 0xFFFF)
+#define SOTG_IRQ_RISE_CLR(x) (((x) << 16) & 0xFFFF0000)
+#define SOTG_IRQ_OTG_TMR_TIMEOUT (1 << 9)
+#define SOTG_IRQ_B_SE0_SRP (1 << 8)
+#define SOTG_IRQ_B_SESS_END (1 << 7)
+#define SOTG_IRQ_BDIS_ACON (1 << 6)
+#define SOTG_IRQ_OTG_RESUME (1 << 5)
+#define SOTG_IRQ_RMT_CONN (1 << 4)
+#define SOTG_IRQ_ID (1 << 3)
+#define SOTG_IRQ_DP_SRP (1 << 2)
+#define SOTG_IRQ_A_B_SESS_VLD (1 << 1)
+#define SOTG_IRQ_VBUS_VLD (1 << 0)
+#define SOTG_TIMER_LOW_SET_CLR 0x388
+#define SOTG_TIMER_LOW_SET(x) ((x) & 0xFFFF)
+#define SOTG_TIMER_LOW_CLR(x) (((x) << 16) & 0xFFFF0000)
+#define SOTG_TIMER_HIGH_SET_CLR 0x38C
+#define SOTG_TIMER_HIGH_SET(x) ((x) & 0xFFFF)
+#define SOTG_TIMER_HIGH_CLR(x) (((x) << 16) & 0xFFFF0000)
+#define SOTG_TIMER_HIGH_START (1U << 15)
+#define SOTG_MEMORY_REG 0x33c
+
+/* Peripheral controller specific registers */
+
+#define SOTG_ADDRESS 0x200
+#define SOTG_ADDRESS_ENABLE (1 << 7)
+#define SOTG_MODE 0x20C
+#define SOTG_MODE_DMACLK_ON (1 << 9)
+#define SOTG_MODE_VBUSSTAT (1 << 8)
+#define SOTG_MODE_CLKAON (1 << 7)
+#define SOTG_MODE_SNDRSU (1 << 6)
+#define SOTG_MODE_GOSUSP (1 << 5)
+#define SOTG_MODE_SFRESET (1 << 4)
+#define SOTG_MODE_GLINTENA (1 << 3)
+#define SOTG_MODE_WKUPCS (1 << 2)
+#define SOTG_INTERRUPT_CFG 0x210
+#define SOTG_INTERRUPT_CFG_DEBUG_SET (1 << 16)
+#define SOTG_INTERRUPT_CFG_CDBGMOD (1 << 6) /* ACK only */
+#define SOTG_INTERRUPT_CFG_DDBGMODIN (1 << 4) /* ACK only */
+#define SOTG_INTERRUPT_CFG_DDBGMODOUT (1 << 2) /* ACK and NYET only */
+#define SOTG_INTERRUPT_CFG_INTLVL (1 << 1)
+#define SOTG_INTERRUPT_CFG_INTPOL (1 << 0)
+#define SOTG_DCINTERRUPT_EN 0x214
+#define SOTG_HW_MODE_CTRL 0x300
+#define SOTG_HW_MODE_CTRL_ALL_ATX_RESET (1 << 31)
+#define SOTG_HW_MODE_CTRL_ANA_DIGI_OC (1 << 15)
+#define SOTG_HW_MODE_CTRL_DEV_DMA (1 << 11)
+#define SOTG_HW_MODE_CTRL_COMN_INT (1 << 10)
+#define SOTG_HW_MODE_CTRL_COMN_DMA (1 << 9)
+#define SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH (1 << 8)
+#define SOTG_HW_MODE_CTRL_DACK_POL (1 << 6)
+#define SOTG_HW_MODE_CTRL_DREQ_POL (1 << 5)
+#define SOTG_HW_MODE_CTRL_INTR_POL (1 << 2)
+#define SOTG_HW_MODE_CTRL_INTR_LEVEL (1 << 1)
+#define SOTG_HW_MODE_CTRL_GLOBAL_INTR_EN (1 << 0)
+#define SOTG_OTG_CTRL 0x374
+#define SOTG_EP_INDEX 0x22c
+#define SOTG_EP_INDEX_EP0SETUP (1 << 5)
+#define SOTG_EP_INDEX_ENDP_INDEX_MASK (15 << 1)
+#define SOTG_EP_INDEX_ENDP_INDEX_SHIFT 1
+#define SOTG_EP_INDEX_DIR_IN (1 << 0)
+#define SOTG_EP_INDEX_DIR_OUT 0
+#define SOTG_CTRL_FUNC 0x228
+#define SOTG_CTRL_FUNC_CLBUF (1 << 4)
+#define SOTG_CTRL_FUNC_VENDP (1 << 3)
+#define SOTG_CTRL_FUNC_DSEN (1 << 2)
+#define SOTG_CTRL_FUNC_STATUS (1 << 1)
+#define SOTG_CTRL_FUNC_STALL (1 << 0)
+#define SOTG_DATA_PORT 0x220
+#define SOTG_BUF_LENGTH 0x21C
+#define SOTG_BUF_LENGTH_BUFLEN_MASK 0xFFFF
+#define SOTG_BUF_LENGTH_FILLED_MASK (3 << 16)
+#define SOTG_EP_MAXPACKET 0x204
+#define SOTG_EP_TYPE 0x208
+#define SOTG_EP_TYPE_NOEMPPKT (1 << 4)
+#define SOTG_EP_TYPE_ENABLE (1 << 3)
+#define SOTG_EP_TYPE_DBLBUF (1 << 2)
+#define SOTG_EP_TYPE_EP_TYPE (3 << 0)
+#define SOTG_DMA_CMD 0x230
+#define SOTG_DMA_XFER_COUNT 0x234
+#define SOTG_DCDMA_CFG 0x238
+#define SOTG_DMA_HW 0x23C
+#define SOTG_DMA_IRQ_REASON 0x250
+#define SOTG_DMA_IRQ_ENABLE 0x254
+#define SOTG_DMA_EP 0x258
+#define SOTG_BURST_COUNTER 0x264
+#define SOTG_DCINTERRUPT 0x218
+#define SOTG_DCINTERRUPT_IEPRX(n) (1 << (10 + (2*(n))))
+#define SOTG_DCINTERRUPT_IEPTX(n) (1 << (11 + (2*(n))))
+#define SOTG_DCINTERRUPT_IEP0SETUP (1 << 8)
+#define SOTG_DCINTERRUPT_IEVBUS (1 << 7)
+#define SOTG_DCINTERRUPT_IEDMA (1 << 6)
+#define SOTG_DCINTERRUPT_IEHS_STA (1 << 5)
+#define SOTG_DCINTERRUPT_IERESM (1 << 4)
+#define SOTG_DCINTERRUPT_IESUSP (1 << 3)
+#define SOTG_DCINTERRUPT_IEPSOF (1 << 2)
+#define SOTG_DCINTERRUPT_IESOF (1 << 1)
+#define SOTG_DCINTERRUPT_IEBRST (1 << 0)
+#define SOTG_DCCHIP_ID 0x270
+#define SOTG_FRAME_NUM 0x274
+#define SOTG_FRAME_NUM_MICROSOFR_MASK 0x3800
+#define SOTG_FRAME_NUM_MICROSOFR_SHIFT 11
+#define SOTG_FRAME_NUM_SOFR_MASK 0x7FF
+#define SOTG_DCSCRATCH 0x278
+#define SOTG_UNLOCK_DEVICE 0x27C
+#define SOTG_UNLOCK_DEVICE_CODE 0xAA37
+#define SOTG_IRQ_PULSE_WIDTH 0x280
+#define SOTG_TEST_MODE 0x284
+#define SOTG_TEST_MODE_FORCEHS (1 << 7)
+#define SOTG_TEST_MODE_FORCEFS (1 << 4)
+#define SOTG_TEST_MODE_PRBS (1 << 3)
+#define SOTG_TEST_MODE_KSTATE (1 << 2)
+#define SOTG_TEST_MODE_JSTATE (1 << 1)
+#define SOTG_TEST_MODE_SE0_NAK (1 << 0)
+
+/* 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
+#define SOTG_PORTSC1_PIC (3 << 14)
+#define SOTG_PORTSC1_PO (1 << 13)
+#define SOTG_PORTSC1_PP (1 << 12)
+#define SOTG_PORTSC1_LS (3 << 10)
+#define SOTG_PORTSC1_PR (1 << 8)
+#define SOTG_PORTSC1_SUSP (1 << 7)
+#define SOTG_PORTSC1_FPR (1 << 6)
+#define SOTG_PORTSC1_PED (1 << 2)
+#define SOTG_PORTSC1_ECSC (1 << 1)
+#define SOTG_PORTSC1_ECCS (1 << 0)
+#define SOTG_PTD_DW0 0
+#define SOTG_PTD_DW0_VALID 1U
+#define SOTG_PTD_DW1 4
+#define SOTG_PTD_DW1_ENABLE_SPLIT (1 << 14)
+#define SOTG_PTD_DW2 8
+#define SOTG_PTD_DW2_RL (0xf << 25)
+#define SOTG_PTD_DW3 12
+#define SOTG_PTD_DW3_NRL (0xf << 19)
+#define SOTG_PTD_DW3_ACTIVE (1U << 31)
+#define SOTG_PTD_DW3_HALTED (1U << 30)
+#define SOTG_PTD_DW3_ERRORS (3U << 28)
+#define SOTG_PTD_DW3_CERR_3 (3U << 23)
+#define SOTG_PTD_DW3_CERR_2 (2U << 23) /* infinite NAKs */
+#define SOTG_PTD_DW3_CERR_1 (1U << 23)
+#define SOTG_PTD_DW3_XFER_COUNT 0x7FFF
+#define SOTG_PTD_DW4 16
+#define SOTG_PTD_DW5 20
+#define SOTG_PTD_DW6 24
+#define SOTG_PTD_DW7 28
+#define SOTG_DATA_ADDR(x) (0x1000 + (512 * (x)))
+#define SOTG_ASYNC_PTD(x) (0xC00 + ((x) * 32))
+#define SOTG_INTR_PTD(x) (0x800 + ((x) * 32))
+#define SOTG_ISOC_PTD(x) (0x400 + ((x) * 32))
+#define SOTG_PTD(x) (0x400 + ((x) * 32))
+#define SOTG_HC_MEMORY_ADDR(x) (((x) - 0x400) >> 3)
+#define SOTG_SW_RESET 0x30C
+#define SOTG_SW_RESET_HC (1 << 1)
+#define SOTG_SW_RESET_ALL (1 << 0)
+#define SOTG_POWER_DOWN 0x354
+#define SOTG_POWER_DOWN_PORT3_PD (1 << 12)
+#define SOTG_POWER_DOWN_PORT2_PD (1 << 11)
+#define SOTG_POWER_DOWN_VBATDET_PWR (1 << 10)
+#define SOTG_POWER_DOWN_BIAS_EN (1 << 5)
+#define SOTG_POWER_DOWN_VREG_ON (1 << 4)
+#define SOTG_POWER_DOWN_OC3_PWR (1 << 3)
+#define SOTG_POWER_DOWN_OC2_PWR (1 << 2)
+#define SOTG_POWER_DOWN_OC1_PWR (1 << 1)
+#define SOTG_POWER_DOWN_HC_CLK_EN (1 << 0)
+#define SOTG_USBCMD 0x20
+#define SOTG_USBCMD_LHCR (1 << 7)
+#define SOTG_USBCMD_HCRESET (1 << 1)
+#define SOTG_USBCMD_RS (1 << 0)
+#define SOTG_HCSCRATCH 0x308
+#define SOTG_HCINTERRUPT 0x310
+#define SOTG_HCINTERRUPT_OTG_IRQ (1 << 10)
+#define SOTG_HCINTERRUPT_ISO_IRQ (1 << 9)
+#define SOTG_HCINTERRUPT_ALT_IRQ (1 << 8)
+#define SOTG_HCINTERRUPT_INT_IRQ (1 << 7)
+#define SOTG_HCINTERRUPT_CLKREADY (1 << 6)
+#define SOTG_HCINTERRUPT_HCSUSP (1 << 5)
+#define SOTG_HCINTERRUPT_DMAEOTINT (1 << 3)
+#define SOTG_HCINTERRUPT_SOFITLINT (1 << 1)
+#define SOTG_HCINTERRUPT_ENABLE 0x314
+#define SOTG_ATL_PTD_DONE_PTD 0x150
+#define SOTG_ATL_PTD_SKIP_PTD 0x154
+#define SOTG_ATL_PTD_LAST_PTD 0x158
+#define SOTG_INT_PTD_DONE_PTD 0x140
+#define SOTG_INT_PTD_SKIP_PTD 0x144
+#define SOTG_INT_PTD_LAST_PTD 0x148
+#define SOTG_ISO_PTD_DONE_PTD 0x130
+#define SOTG_ISO_PTD_SKIP_PTD 0x134
+#define SOTG_ISO_PTD_LAST_PTD 0x138
+#define SOTG_HCBUFFERSTATUS 0x334
+#define SOTG_HCBUFFERSTATUS_ISO_BUF_FILL (1 << 2)
+#define SOTG_HCBUFFERSTATUS_INT_BUF_FILL (1 << 1)
+#define SOTG_HCBUFFERSTATUS_ATL_BUF_FILL (1 << 0)
+#define SOTG_ISO_IRQ_MASK_OR 0x318
+#define SOTG_INT_IRQ_MASK_OR 0x31C
+#define SOTG_ATL_IRQ_MASK_OR 0x320
+#define SOTG_ISO_IRQ_MASK_AND 0x324
+#define SOTG_INT_IRQ_MASK_AND 0x328
+#define SOTG_ATL_IRQ_MASK_AND 0x32C
+
+#endif /* _SAF1761_OTG_REG_H_ */
diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c
index 134a4472363ac..00776154447c0 100644
--- a/sys/dev/usb/controller/uhci.c
+++ b/sys/dev/usb/controller/uhci.c
@@ -89,12 +89,10 @@ static int uhcidebug = 0;
static int uhcinoloop = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci");
-SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RWTUN,
&uhcidebug, 0, "uhci debug level");
-TUNABLE_INT("hw.usb.uhci.debug", &uhcidebug);
-SYSCTL_INT(_hw_usb_uhci, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_uhci, OID_AUTO, loop, CTLFLAG_RWTUN,
&uhcinoloop, 0, "uhci noloop");
-TUNABLE_INT("hw.usb.uhci.loop", &uhcinoloop);
static void uhci_dumpregs(uhci_softc_t *sc);
static void uhci_dump_tds(uhci_td_t *td);
diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c
index bbb9e85df954e..dd75ba7a5aa24 100644
--- a/sys/dev/usb/controller/usb_controller.c
+++ b/sys/dev/usb/controller/usb_controller.c
@@ -90,19 +90,16 @@ SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0,
#if USB_HAVE_ROOT_MOUNT_HOLD
static int usb_no_boot_wait = 0;
-TUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait);
-SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RD|CTLFLAG_TUN, &usb_no_boot_wait, 0,
+SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0,
"No USB device enumerate waiting at boot.");
#endif
static int usb_no_suspend_wait = 0;
-TUNABLE_INT("hw.usb.no_suspend_wait", &usb_no_suspend_wait);
-SYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RW|CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RWTUN,
&usb_no_suspend_wait, 0, "No USB device waiting at system suspend.");
static int usb_no_shutdown_wait = 0;
-TUNABLE_INT("hw.usb.no_shutdown_wait", &usb_no_shutdown_wait);
-SYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RW|CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RWTUN,
&usb_no_shutdown_wait, 0, "No USB device waiting at system shutdown.");
static devclass_t usb_devclass;
@@ -138,6 +135,7 @@ DRIVER_MODULE(usbus, octusb, usb_driver, usb_devclass, 0, 0);
/* Dual Mode Drivers */
DRIVER_MODULE(usbus, dwcotg, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usbus, saf1761otg, usb_driver, usb_devclass, 0, 0);
/*------------------------------------------------------------------------*
* usb_probe
@@ -332,7 +330,7 @@ usb_shutdown(device_t dev)
return (0);
}
- device_printf(bus->bdev, "Controller shutdown\n");
+ DPRINTF("%s: Controller shutdown\n", device_get_nameunit(bus->bdev));
USB_BUS_LOCK(bus);
usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus),
@@ -344,7 +342,8 @@ usb_shutdown(device_t dev)
}
USB_BUS_UNLOCK(bus);
- device_printf(bus->bdev, "Controller shutdown complete\n");
+ DPRINTF("%s: Controller shutdown complete\n",
+ device_get_nameunit(bus->bdev));
return (0);
}
@@ -366,7 +365,13 @@ usb_bus_explore(struct usb_proc_msg *pm)
if (bus->no_explore != 0)
return;
- if (udev && udev->hub) {
+ if (udev != NULL) {
+ USB_BUS_UNLOCK(bus);
+ uhub_explore_handle_re_enumerate(udev);
+ USB_BUS_LOCK(bus);
+ }
+
+ if (udev != NULL && udev->hub != NULL) {
if (bus->do_probe) {
bus->do_probe = 0;
@@ -898,7 +903,10 @@ usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat,
bus->alloc_failed = 0;
mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent),
- NULL, MTX_DEF | MTX_RECURSE);
+ "usb_def_mtx", MTX_DEF | MTX_RECURSE);
+
+ mtx_init(&bus->bus_spin_lock, device_get_nameunit(bus->parent),
+ "usb_spin_mtx", MTX_SPIN | MTX_RECURSE);
usb_callout_init_mtx(&bus->power_wdog,
&bus->bus_mtx, 0);
@@ -953,6 +961,7 @@ usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb)
#endif
mtx_destroy(&bus->bus_mtx);
+ mtx_destroy(&bus->bus_spin_lock);
}
/* convenience wrappers */
diff --git a/sys/dev/usb/controller/uss820dci.c b/sys/dev/usb/controller/uss820dci.c
index ba777115cc36a..9e10000030387 100644
--- a/sys/dev/usb/controller/uss820dci.c
+++ b/sys/dev/usb/controller/uss820dci.c
@@ -875,7 +875,8 @@ uss820dci_setup_standard_chain(struct usb_xfer *xfer)
temp.td = NULL;
temp.td_next = xfer->td_start[0];
temp.offset = 0;
- temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr;
temp.did_stall = !xfer->flags_int.control_stall;
sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
@@ -1120,7 +1121,8 @@ uss820dci_standard_done_sub(struct usb_xfer *xfer)
}
/* Check for short transfer */
if (len > 0) {
- if (xfer->flags_int.short_frames_ok) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
/* follow alt next */
if (td->alt_next) {
td = td->obj_next;
@@ -1543,7 +1545,7 @@ uss820dci_do_poll(struct usb_bus *bus)
}
/*------------------------------------------------------------------------*
- * at91dci bulk support
+ * uss820dci bulk support
*------------------------------------------------------------------------*/
static void
uss820dci_device_bulk_open(struct usb_xfer *xfer)
@@ -1580,7 +1582,7 @@ static const struct usb_pipe_methods uss820dci_device_bulk_methods =
};
/*------------------------------------------------------------------------*
- * at91dci control support
+ * uss820dci control support
*------------------------------------------------------------------------*/
static void
uss820dci_device_ctrl_open(struct usb_xfer *xfer)
@@ -1617,7 +1619,7 @@ static const struct usb_pipe_methods uss820dci_device_ctrl_methods =
};
/*------------------------------------------------------------------------*
- * at91dci interrupt support
+ * uss820dci interrupt support
*------------------------------------------------------------------------*/
static void
uss820dci_device_intr_open(struct usb_xfer *xfer)
@@ -1654,7 +1656,7 @@ static const struct usb_pipe_methods uss820dci_device_intr_methods =
};
/*------------------------------------------------------------------------*
- * at91dci full speed isochronous support
+ * uss820dci full speed isochronous support
*------------------------------------------------------------------------*/
static void
uss820dci_device_isoc_fs_open(struct usb_xfer *xfer)
@@ -1736,7 +1738,7 @@ static const struct usb_pipe_methods uss820dci_device_isoc_fs_methods =
};
/*------------------------------------------------------------------------*
- * at91dci root control support
+ * uss820dci root control support
*------------------------------------------------------------------------*
* Simulate a hardware HUB by handling all the necessary requests.
*------------------------------------------------------------------------*/
diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c
index 5092cc962727e..5e9029c5abe9e 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -90,24 +90,20 @@
static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI");
static int xhcistreams;
-SYSCTL_INT(_hw_usb_xhci, OID_AUTO, streams, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, streams, CTLFLAG_RWTUN,
&xhcistreams, 0, "Set to enable streams mode support");
-TUNABLE_INT("hw.usb.xhci.streams", &xhcistreams);
#ifdef USB_DEBUG
static int xhcidebug;
static int xhciroute;
static int xhcipolling;
-SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RWTUN,
&xhcidebug, 0, "Debug level");
-TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug);
-SYSCTL_INT(_hw_usb_xhci, OID_AUTO, xhci_port_route, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, xhci_port_route, CTLFLAG_RWTUN,
&xhciroute, 0, "Routing bitmap for switching EHCI ports to XHCI controller");
-TUNABLE_INT("hw.usb.xhci.xhci_port_route", &xhciroute);
-SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RWTUN,
&xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller");
-TUNABLE_INT("hw.usb.xhci.use_polling", &xhcipolling);
#else
#define xhciroute 0
#endif
@@ -1849,31 +1845,25 @@ restart:
XHCI_TRB_3_ISO_SIA_BIT;
}
if (temp->direction == UE_DIR_IN)
- dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT;
+ dword |= XHCI_TRB_3_ISP_BIT;
break;
case XHCI_TRB_TYPE_DATA_STAGE:
dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
- XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE) |
- XHCI_TRB_3_TBC_SET(temp->tbc) |
- XHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE);
if (temp->direction == UE_DIR_IN)
dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT;
break;
case XHCI_TRB_TYPE_STATUS_STAGE:
dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
- XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE) |
- XHCI_TRB_3_TBC_SET(temp->tbc) |
- XHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE);
if (temp->direction == UE_DIR_IN)
dword |= XHCI_TRB_3_DIR_IN;
break;
default: /* XHCI_TRB_TYPE_NORMAL */
dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
- XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL) |
- XHCI_TRB_3_TBC_SET(temp->tbc) |
- XHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL);
if (temp->direction == UE_DIR_IN)
- dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT;
+ dword |= XHCI_TRB_3_ISP_BIT;
break;
}
td->td_trb[x].dwTrb3 = htole32(dword);
@@ -2688,24 +2678,23 @@ xhci_alloc_device_ext(struct usb_device *udev)
goto error;
}
- pc = &sc->sc_hw.devs[index].endpoint_pc;
- pg = &sc->sc_hw.devs[index].endpoint_pg;
+ /* initialise all endpoint LINK TRBs */
- /* need to initialize the page cache */
- pc->tag_parent = sc->sc_bus.dma_parent_tag;
+ for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) {
- if (usb_pc_alloc_mem(pc, pg,
- sizeof(struct xhci_dev_endpoint_trbs), XHCI_PAGE_SIZE)) {
- goto error;
- }
+ pc = &sc->sc_hw.devs[index].endpoint_pc[i];
+ pg = &sc->sc_hw.devs[index].endpoint_pg[i];
- /* initialise all endpoint LINK TRBs */
+ /* need to initialize the page cache */
+ pc->tag_parent = sc->sc_bus.dma_parent_tag;
- for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) {
+ if (usb_pc_alloc_mem(pc, pg,
+ sizeof(struct xhci_dev_endpoint_trbs), XHCI_TRB_ALIGN)) {
+ goto error;
+ }
/* lookup endpoint TRB ring */
- usbd_get_page(pc, (uintptr_t)&
- ((struct xhci_dev_endpoint_trbs *)0)->trb[i][0], &buf_ep);
+ usbd_get_page(pc, 0, &buf_ep);
/* get TRB pointer */
trb = buf_ep.buffer;
@@ -2719,9 +2708,9 @@ xhci_alloc_device_ext(struct usb_device *udev)
trb->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0));
trb->dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT |
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
- }
- usb_pc_cpu_flush(pc);
+ usb_pc_cpu_flush(pc);
+ }
xhci_set_slot_pointer(sc, index, buf_dev.physaddr);
@@ -2738,13 +2727,15 @@ xhci_free_device_ext(struct usb_device *udev)
{
struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
uint8_t index;
+ uint8_t i;
index = udev->controller_slot_id;
xhci_set_slot_pointer(sc, index, 0);
usb_pc_free_mem(&sc->sc_hw.devs[index].device_pc);
usb_pc_free_mem(&sc->sc_hw.devs[index].input_pc);
- usb_pc_free_mem(&sc->sc_hw.devs[index].endpoint_pc);
+ for (i = 0; i != XHCI_MAX_ENDPOINTS; i++)
+ usb_pc_free_mem(&sc->sc_hw.devs[index].endpoint_pc[i]);
}
static struct xhci_endpoint_ext *
@@ -2765,10 +2756,9 @@ xhci_get_endpoint_ext(struct usb_device *udev, struct usb_endpoint_descriptor *e
index = udev->controller_slot_id;
- pc = &sc->sc_hw.devs[index].endpoint_pc;
+ pc = &sc->sc_hw.devs[index].endpoint_pc[epno];
- usbd_get_page(pc, (uintptr_t)&((struct xhci_dev_endpoint_trbs *)0)->
- trb[epno][0], &buf_ep);
+ usbd_get_page(pc, 0, &buf_ep);
pepext = &sc->sc_hw.devs[index].endp[epno];
pepext->page_cache = pc;
diff --git a/sys/dev/usb/controller/xhci.h b/sys/dev/usb/controller/xhci.h
index b000e4f11dd6c..408b429dff0ab 100644
--- a/sys/dev/usb/controller/xhci.h
+++ b/sys/dev/usb/controller/xhci.h
@@ -316,8 +316,8 @@ struct xhci_trb {
} __aligned(4);
struct xhci_dev_endpoint_trbs {
- struct xhci_trb trb[XHCI_MAX_ENDPOINTS]
- [(XHCI_MAX_STREAMS * XHCI_MAX_TRANSFERS) + XHCI_MAX_STREAMS];
+ struct xhci_trb trb[(XHCI_MAX_STREAMS *
+ XHCI_MAX_TRANSFERS) + XHCI_MAX_STREAMS];
};
#define XHCI_TD_PAGE_NBUF 17 /* units, room enough for 64Kbytes */
@@ -385,11 +385,11 @@ enum {
struct xhci_hw_dev {
struct usb_page_cache device_pc;
struct usb_page_cache input_pc;
- struct usb_page_cache endpoint_pc;
+ struct usb_page_cache endpoint_pc[XHCI_MAX_ENDPOINTS];
struct usb_page device_pg;
struct usb_page input_pg;
- struct usb_page endpoint_pg;
+ struct usb_page endpoint_pg[XHCI_MAX_ENDPOINTS];
struct xhci_endpoint_ext endp[XHCI_MAX_ENDPOINTS];
diff --git a/sys/dev/usb/controller/xhci_pci.c b/sys/dev/usb/controller/xhci_pci.c
index dc5a6e9dd445a..f0b789a3d66b2 100644
--- a/sys/dev/usb/controller/xhci_pci.c
+++ b/sys/dev/usb/controller/xhci_pci.c
@@ -150,6 +150,8 @@ static int
xhci_pci_port_route(device_t self, uint32_t set, uint32_t clear)
{
uint32_t temp;
+ uint32_t usb3_mask;
+ uint32_t usb2_mask;
temp = pci_read_config(self, PCI_XHCI_INTEL_USB3_PSSEN, 4) |
pci_read_config(self, PCI_XHCI_INTEL_XUSB2PR, 4);
@@ -157,8 +159,12 @@ xhci_pci_port_route(device_t self, uint32_t set, uint32_t clear)
temp |= set;
temp &= ~clear;
- pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp, 4);
- pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp, 4);
+ /* Don't set bits which the hardware doesn't support */
+ usb3_mask = pci_read_config(self, PCI_XHCI_INTEL_USB3PRM, 4);
+ usb2_mask = pci_read_config(self, PCI_XHCI_INTEL_USB2PRM, 4);
+
+ pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp & usb3_mask, 4);
+ pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp & usb2_mask, 4);
device_printf(self, "Port routing mask set to 0x%08x\n", temp);
diff --git a/sys/dev/usb/controller/xhcireg.h b/sys/dev/usb/controller/xhcireg.h
index bd1d635cff778..a0b7397110811 100644
--- a/sys/dev/usb/controller/xhcireg.h
+++ b/sys/dev/usb/controller/xhcireg.h
@@ -35,7 +35,9 @@
#define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */
#define PCI_XHCI_INTEL_XUSB2PR 0xD0 /* Intel USB2 Port Routing */
+#define PCI_XHCI_INTEL_USB2PRM 0xD4 /* Intel USB2 Port Routing Mask */
#define PCI_XHCI_INTEL_USB3_PSSEN 0xD8 /* Intel USB3 Port SuperSpeed Enable */
+#define PCI_XHCI_INTEL_USB3PRM 0xDC /* Intel USB3 Port Routing Mask */
/* XHCI capability registers */
#define XHCI_CAPLENGTH 0x00 /* RO capability */