[Zrouter-src-freebsd] ZRouter.org: push to FreeBSD HEAD tree
zrouter-src-freebsd at zrouter.org
zrouter-src-freebsd at zrouter.org
Tue Oct 16 10:36:42 UTC 2012
details: http://zrouter.org/hg/FreeBSD/head//rev/cdfc72b65a60
changeset: 537:cdfc72b65a60
user: Aleksandr Rybalko <ray at ddteam.net>
date: Tue Sep 25 11:48:54 2012 +0300
description:
Update to FreeBSD-HEAD r240914.
diffstat:
head/sys/dev/usb/controller/dwc_otg.c | 1919 +++++++++++++++++++++++++++++---
1 files changed, 1696 insertions(+), 223 deletions(-)
diffs (2596 lines):
diff -r 5fa13a913a6d -r cdfc72b65a60 head/sys/dev/usb/controller/dwc_otg.c
--- a/head/sys/dev/usb/controller/dwc_otg.c Sun Sep 16 23:02:39 2012 +0300
+++ b/head/sys/dev/usb/controller/dwc_otg.c Tue Sep 25 11:48:54 2012 +0300
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,8 +26,7 @@
/*
* This file contains the driver for the DesignWare series USB 2.0 OTG
- * Controller. This driver currently only supports the device mode of
- * the USB hardware.
+ * Controller.
*/
/*
@@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/usb/controller/dwc_otg.c 239909 2012-08-30 16:19:05Z hselasky $");
+__FBSDID("$FreeBSD: head/sys/dev/usb/controller/dwc_otg.c 240890 2012-09-24 16:34:13Z hselasky $");
#include <sys/stdint.h>
#include <sys/stddef.h>
@@ -91,19 +91,28 @@
DWC_OTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus)
#define DWC_OTG_MSK_GINT_ENABLED \
- (GINTSTS_ENUMDONE | \
- GINTSTS_USBRST | \
- GINTSTS_USBSUSP | \
- GINTSTS_IEPINT | \
- GINTSTS_RXFLVL | \
- GINTSTS_SESSREQINT)
-
-#define DWC_OTG_USE_HSIC 0
+ (GINTSTS_ENUMDONE | \
+ GINTSTS_USBRST | \
+ GINTSTS_USBSUSP | \
+ GINTSTS_IEPINT | \
+ GINTSTS_RXFLVL | \
+ GINTSTS_SESSREQINT | \
+ GINTMSK_OTGINTMSK | \
+ GINTMSK_HCHINTMSK | \
+ GINTSTS_PRTINT)
+
+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,
+ &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 = 0;
-
-static SYSCTL_NODE(_hw_usb, OID_AUTO, dwc_otg, CTLFLAG_RW, 0, "USB DWC OTG");
+static int dwc_otg_debug;
+
SYSCTL_INT(_hw_usb_dwc_otg, OID_AUTO, debug, CTLFLAG_RW,
&dwc_otg_debug, 0, "DWC OTG debug level");
#endif
@@ -114,16 +123,22 @@
struct usb_bus_methods dwc_otg_bus_methods;
struct usb_pipe_methods dwc_otg_device_non_isoc_methods;
-struct usb_pipe_methods dwc_otg_device_isoc_fs_methods;
+struct usb_pipe_methods dwc_otg_device_isoc_methods;
static dwc_otg_cmd_t dwc_otg_setup_rx;
static dwc_otg_cmd_t dwc_otg_data_rx;
static dwc_otg_cmd_t dwc_otg_data_tx;
static dwc_otg_cmd_t dwc_otg_data_tx_sync;
+
+static dwc_otg_cmd_t dwc_otg_host_setup_tx;
+static dwc_otg_cmd_t dwc_otg_host_data_tx;
+static dwc_otg_cmd_t dwc_otg_host_data_rx;
+
static 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);
/*
* Here is a configuration that the chip supports.
@@ -153,7 +168,7 @@
}
static int
-dwc_otg_init_fifo(struct dwc_otg_softc *sc)
+dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
{
struct dwc_otg_profile *pf;
uint32_t fifo_size;
@@ -186,14 +201,52 @@
return (EINVAL);
}
- DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ, (0x10 << 16) | (tx_start / 4));
- fifo_size -= 0x40;
- tx_start += 0x40;
-
- /* setup control endpoint profile */
- sc->sc_hw_ep_profile[0].usb = dwc_otg_ep_profile[0];
-
- for (x = 1; x != sc->sc_dev_ep_max; x++) {
+ if (mode == DWC_MODE_HOST) {
+
+ /* reset active endpoints */
+ sc->sc_active_rx_ep = 0;
+
+ fifo_size /= 2;
+
+ DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
+ ((fifo_size / 4) << 16) |
+ (tx_start / 4));
+
+ tx_start += fifo_size;
+
+ DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ,
+ ((fifo_size / 4) << 16) |
+ (tx_start / 4));
+
+ for (x = 0; x != sc->sc_host_ch_max; x++) {
+ /* enable interrupts */
+ DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
+ HCINT_STALL | HCINT_BBLERR |
+ HCINT_XACTERR | HCINT_XFERCOMPL |
+ HCINT_NAK | HCINT_ACK | HCINT_NYET |
+ HCINT_CHHLTD | HCINT_FRMOVRUN |
+ HCINT_DATATGLERR);
+ }
+
+ /* enable host channel interrupts */
+ DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK,
+ (1U << sc->sc_host_ch_max) - 1U);
+ }
+
+ if (mode == DWC_MODE_DEVICE) {
+
+ DWC_OTG_WRITE_4(sc, DOTG_GNPTXFSIZ,
+ (0x10 << 16) | (tx_start / 4));
+ fifo_size -= 0x40;
+ tx_start += 0x40;
+
+ /* setup control endpoint profile */
+ sc->sc_hw_ep_profile[0].usb = dwc_otg_ep_profile[0];
+
+ /* reset active endpoints */
+ sc->sc_active_rx_ep = 1;
+
+ for (x = 1; x != sc->sc_dev_ep_max; x++) {
pf = sc->sc_hw_ep_profile + x;
@@ -240,17 +293,22 @@
DPRINTF("FIFO%d = IN:%d / OUT:%d\n", x,
pf->usb.max_in_frame_size,
pf->usb.max_out_frame_size);
+ }
}
/* reset RX FIFO */
DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
GRSTCTL_RXFFLSH);
- /* reset all TX FIFOs */
- DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
- GRSTCTL_TXFIFO(0x10) |
- GRSTCTL_TXFFLSH);
-
+ if (mode != DWC_MODE_OTG) {
+ /* reset all TX FIFOs */
+ DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+ GRSTCTL_TXFIFO(0x10) |
+ GRSTCTL_TXFFLSH);
+ } else {
+ /* reset active endpoints */
+ sc->sc_active_rx_ep = 0;
+ }
return (0);
}
@@ -322,13 +380,15 @@
sc->sc_flags.status_suspend = 0;
sc->sc_flags.change_suspend = 1;
- /*
- * Disable resume interrupt and enable suspend
- * interrupt:
- */
- sc->sc_irq_mask &= ~GINTSTS_WKUPINT;
- sc->sc_irq_mask |= GINTSTS_USBSUSP;
- DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+ if (sc->sc_flags.status_device_mode) {
+ /*
+ * Disable resume interrupt and enable suspend
+ * interrupt:
+ */
+ sc->sc_irq_mask &= ~GINTSTS_WKUPINT;
+ sc->sc_irq_mask |= GINTSTS_USBSUSP;
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+ }
/* complete root HUB interrupt endpoint */
dwc_otg_root_intr(sc);
@@ -336,25 +396,70 @@
}
static void
+dwc_otg_suspend_irq(struct dwc_otg_softc *sc)
+{
+ if (!sc->sc_flags.status_suspend) {
+ /* update status bits */
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+
+ if (sc->sc_flags.status_device_mode) {
+ /*
+ * Disable suspend interrupt and enable resume
+ * interrupt:
+ */
+ sc->sc_irq_mask &= ~GINTSTS_USBSUSP;
+ sc->sc_irq_mask |= GINTSTS_WKUPINT;
+ DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
+ }
+
+ /* complete root HUB interrupt endpoint */
+ dwc_otg_root_intr(sc);
+ }
+}
+
+static void
dwc_otg_wakeup_peer(struct dwc_otg_softc *sc)
{
- uint32_t temp;
-
if (!sc->sc_flags.status_suspend)
return;
DPRINTFN(5, "Remote wakeup\n");
- /* enable remote wakeup signalling */
- temp = DWC_OTG_READ_4(sc, DOTG_DCTL);
- temp |= DCTL_RMTWKUPSIG;
- DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
-
- /* Wait 8ms for remote wakeup to complete. */
- usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
-
- temp &= ~DCTL_RMTWKUPSIG;
- DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+ if (sc->sc_flags.status_device_mode) {
+ uint32_t temp;
+
+ /* enable remote wakeup signalling */
+ temp = DWC_OTG_READ_4(sc, DOTG_DCTL);
+ temp |= DCTL_RMTWKUPSIG;
+ DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+
+ /* Wait 8ms for remote wakeup to complete. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+
+ temp &= ~DCTL_RMTWKUPSIG;
+ DWC_OTG_WRITE_4(sc, DOTG_DCTL, temp);
+ } else {
+ /* enable USB port */
+ DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0);
+
+ /* wait 10ms */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+ /* resume port */
+ sc->sc_hprt_val |= HPRT_PRTRES;
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+ /* Wait 100ms for resume signalling to complete. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10);
+
+ /* clear suspend and resume */
+ sc->sc_hprt_val &= ~(HPRT_PRTSUSP | HPRT_PRTRES);
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+ /* Wait 4ms */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250);
+ }
/* need to fake resume IRQ */
dwc_otg_resume_irq(sc);
@@ -386,6 +491,362 @@
sc->sc_last_rx_status = 0;
}
+static void
+dwc_otg_clear_hcint(struct dwc_otg_softc *sc, uint8_t x)
+{
+ uint32_t hcint;
+
+ hcint = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+ DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), hcint);
+
+ /* clear buffered interrupts */
+ sc->sc_chan_state[x].hcint = 0;
+}
+
+static uint8_t
+dwc_otg_host_channel_wait(struct dwc_otg_td *td)
+{
+ struct dwc_otg_softc *sc;
+ 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].hcint & HCINT_HALTED_ONLY) {
+ dwc_otg_clear_hcint(sc, x);
+ return (1);
+ }
+
+ if (x == 0)
+ return (0); /* wait */
+
+ /* find new disabled channel */
+ for (x = 1; x != sc->sc_host_ch_max; x++) {
+
+ uint32_t hcchar;
+
+ if (sc->sc_chan_state[x].allocated)
+ continue;
+
+ /* check if channel is enabled */
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
+ if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) {
+ DPRINTF("CH=%d is BUSY\n", x);
+ continue;
+ }
+
+ sc->sc_chan_state[td->channel].allocated = 0;
+ sc->sc_chan_state[x].allocated = 1;
+
+ 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(0x%08x) "
+ "HCSPLT=0x%08x\n", x, td->hcchar,
+ 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;
+ 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);
+
+ if ((td->hcchar & HCCHAR_EPNUM_MASK) == 0) {
+ max_channel = 1;
+ x = 0;
+ } else {
+ max_channel = sc->sc_host_ch_max;
+ x = 1;
+ }
+
+ for (; x != max_channel; x++) {
+
+ uint32_t hcchar;
+
+ if (sc->sc_chan_state[x].allocated)
+ continue;
+
+ /* check if channel is enabled */
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
+ if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS)) {
+ DPRINTF("CH=%d is BUSY\n", x);
+ continue;
+ }
+
+ sc->sc_chan_state[x].allocated = 1;
+
+ /* clear interrupts */
+ dwc_otg_clear_hcint(sc, x);
+
+ DPRINTF("CH=%d HCCHAR=0x%08x(0x%08x) "
+ "HCSPLT=0x%08x\n", x, td->hcchar,
+ hcchar, td->hcsplt);
+
+ /* set active channel */
+ sc->sc_active_rx_ep |= (1 << x);
+
+ /* set channel */
+ td->channel = x;
+
+ return (0); /* allocated */
+ }
+ return (1); /* busy */
+}
+
+static void
+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));
+ if (hcchar & (HCCHAR_CHENA | HCCHAR_CHDIS))
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x), HCCHAR_CHENA | HCCHAR_CHDIS);
+}
+
+static void
+dwc_otg_host_channel_free(struct dwc_otg_td *td)
+{
+ struct dwc_otg_softc *sc;
+ uint8_t x;
+
+ if (td->channel >= DWC_OTG_MAX_CHANNELS)
+ return; /* already freed */
+
+ /* free channel */
+ x = td->channel;
+ td->channel = DWC_OTG_MAX_CHANNELS;
+
+ DPRINTF("CH=%d\n", x);
+
+ /* get pointer to softc */
+ sc = DWC_OTG_PC2SC(td->pc);
+
+ dwc_otg_host_channel_disable(sc, x);
+
+ sc->sc_chan_state[x].allocated = 0;
+ sc->sc_chan_state[x].suspended = 0;
+
+ /* ack any pending messages */
+ if (sc->sc_last_rx_status != 0 &&
+ GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) == x) {
+ dwc_otg_common_rx_ack(sc);
+ }
+
+ /* clear active channel */
+ sc->sc_active_rx_ep &= ~(1 << x);
+}
+
+static uint8_t
+dwc_otg_host_setup_tx(struct dwc_otg_td *td)
+{
+ struct usb_device_request req __aligned(4);
+ struct dwc_otg_softc *sc;
+ uint32_t hcint;
+ uint32_t hcchar;
+
+ if (dwc_otg_host_channel_alloc(td))
+ return (1); /* busy */
+
+ /* get pointer to softc */
+ sc = DWC_OTG_PC2SC(td->pc);
+
+ 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)));
+
+ if (hcint & HCINT_STALL) {
+ DPRINTF("CH=%d STALL\n", td->channel);
+ td->error_stall = 1;
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+
+ if (hcint & HCINT_ERRORS) {
+ DPRINTF("CH=%d ERROR\n", td->channel);
+ td->errcnt++;
+ if (td->hcsplt != 0 || td->errcnt >= 3) {
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+ }
+
+ /* treat NYET like NAK, if SPLIT transactions are used */
+ if (hcint & HCINT_NYET) {
+ if (td->hcsplt != 0) {
+ DPRINTF("CH=%d NYET+SPLIT\n", td->channel);
+ hcint &= ~HCINT_NYET;
+ hcint |= HCINT_NAK;
+ }
+ }
+
+ /* channel must be disabled before we can complete the transfer */
+
+ if (hcint & (HCINT_ERRORS | HCINT_RETRY |
+ HCINT_ACK | HCINT_NYET)) {
+ uint32_t hcchar;
+
+ dwc_otg_host_channel_disable(sc, td->channel);
+
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+
+ if (!(hcchar & HCCHAR_CHENA)) {
+ hcint |= HCINT_HALTED_ONLY;
+ sc->sc_chan_state[td->channel].hcint = hcint;
+ }
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+
+ switch (td->state) {
+ case DWC_CHAN_ST_START:
+ 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;
+ goto send_pkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ td->offset += td->tx_bytes;
+ td->remainder -= td->tx_bytes;
+ td->toggle = 1;
+ return (0); /* complete */
+ }
+ break;
+ 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;
+ goto send_pkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ goto send_cpkt;
+ }
+ break;
+ case DWC_CHAN_ST_WAIT_C_ANE:
+ if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ td->did_nak = 1;
+ goto send_cpkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ td->offset += td->tx_bytes;
+ td->remainder -= td->tx_bytes;
+ td->toggle = 1;
+ return (0); /* complete */
+ }
+ break;
+ default:
+ break;
+ }
+ return (1); /* busy */
+
+send_pkt:
+ if (sizeof(req) != td->remainder) {
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+
+ if (td->hcsplt != 0) {
+ td->hcsplt &= ~HCSPLT_COMPSPLT;
+ td->state = DWC_CHAN_ST_WAIT_S_ANE;
+ } else {
+ td->state = DWC_CHAN_ST_WAIT_ANE;
+ }
+
+ usbd_copy_out(td->pc, 0, &req, sizeof(req));
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
+ (1 << HCTSIZ_PKTCNT_SHIFT) |
+ (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->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);
+#define _DMA_ 0
+ if (_DMA_) {
+ } else {
+ /* transfer data into FIFO */
+ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ DOTG_DFIFO(td->channel), (uint32_t *)&req,
+ sizeof(req) / 4);
+ }
+
+ /* store number of bytes transmitted */
+ td->tx_bytes = sizeof(req);
+
+ return (1); /* busy */
+
+send_cpkt:
+ td->hcsplt |= HCSPLT_COMPSPLT;
+ td->state = DWC_CHAN_ST_WAIT_C_ANE;
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+ (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->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);
+
+ return (1); /* busy */
+}
+
static uint8_t
dwc_otg_setup_rx(struct dwc_otg_td *td)
{
@@ -518,6 +979,293 @@
}
static uint8_t
+dwc_otg_host_rate_check(struct dwc_otg_td *td,
+ uint8_t do_inc)
+{
+ struct dwc_otg_softc *sc;
+ uint8_t ep_type;
+
+ /* get pointer to softc */
+ sc = DWC_OTG_PC2SC(td->pc);
+
+ ep_type = ((td->hcchar &
+ HCCHAR_EPTYPE_MASK) >> HCCHAR_EPTYPE_SHIFT);
+
+ if (sc->sc_chan_state[td->channel].suspended)
+ goto busy;
+
+ if (ep_type == UE_ISOCHRONOUS) {
+ if (td->tmr_val & 1)
+ td->hcchar |= HCCHAR_ODDFRM;
+ else
+ td->hcchar &= ~HCCHAR_ODDFRM;
+ if (do_inc)
+ td->tmr_val += td->tmr_res;
+ } else if (ep_type == UE_INTERRUPT) {
+ if ((sc->sc_tmr_val & 0xFF) != td->tmr_val)
+ goto busy;
+ if (do_inc)
+ td->tmr_val += td->tmr_res;
+ } else if (td->did_nak != 0) {
+ goto busy;
+ }
+
+ if (ep_type == UE_ISOCHRONOUS) {
+ td->toggle = 0;
+ } else if (td->set_toggle) {
+ td->set_toggle = 0;
+ td->toggle = 1;
+ }
+ return (0);
+busy:
+ return (1);
+}
+
+static uint8_t
+dwc_otg_host_data_rx(struct dwc_otg_td *td)
+{
+ struct dwc_otg_softc *sc;
+ uint32_t hcint;
+ uint32_t hcchar;
+ uint32_t count;
+
+ if (dwc_otg_host_channel_alloc(td))
+ return (1); /* busy */
+
+ /* get pointer to softc */
+ sc = DWC_OTG_PC2SC(td->pc);
+
+ 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_STALL) {
+ DPRINTF("CH=%d STALL\n", td->channel);
+ td->error_stall = 1;
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+
+ if (hcint & HCINT_ERRORS) {
+ DPRINTF("CH=%d ERROR\n", td->channel);
+ td->errcnt++;
+ if (td->hcsplt != 0 || td->errcnt >= 3) {
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+ }
+
+ /* treat NYET like NAK, if SPLIT transactions are used */
+ if (hcint & HCINT_NYET) {
+ if (td->hcsplt != 0) {
+ DPRINTF("CH=%d NYET+SPLIT\n", td->channel);
+ hcint &= ~HCINT_NYET;
+ hcint |= HCINT_NAK;
+ }
+ }
+
+ /* 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);
+
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+
+ if (!(hcchar & HCCHAR_CHENA)) {
+ hcint |= HCINT_HALTED_ONLY;
+ sc->sc_chan_state[td->channel].hcint = hcint;
+ }
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+
+ /* check endpoint status */
+ if (sc->sc_last_rx_status == 0)
+ goto check_state;
+
+ if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != td->channel)
+ goto check_state;
+
+ switch (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) {
+ case GRXSTSRH_IN_DATA:
+
+ DPRINTF("DATA\n");
+
+ td->toggle ^= 1;
+
+ /* get the packet byte count */
+ count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
+
+ /* verify the packet byte count */
+ if (count != td->max_packet_size) {
+ if (count < td->max_packet_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ td->got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error_any = 1;
+
+ /* release FIFO */
+ dwc_otg_common_rx_ack(sc);
+ return (0); /* we are complete */
+ }
+ }
+
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error_any = 1;
+
+ /* release FIFO */
+ dwc_otg_common_rx_ack(sc);
+ return (0); /* we are complete */
+ }
+
+ usbd_copy_in(td->pc, td->offset,
+ sc->sc_rx_bounce_buffer, count);
+
+ td->remainder -= count;
+ td->offset += count;
+ hcint |= HCINT_SOFTWARE_ONLY | HCINT_ACK;
+ sc->sc_chan_state[td->channel].hcint = hcint;
+ break;
+
+ default:
+ DPRINTF("OTHER\n");
+ break;
+ }
+ /* release FIFO */
+ dwc_otg_common_rx_ack(sc);
+
+check_state:
+ switch (td->state) {
+ case DWC_CHAN_ST_START:
+ if (td->hcsplt != 0)
+ goto receive_spkt;
+ else
+ goto receive_pkt;
+
+ case DWC_CHAN_ST_WAIT_ANE:
+ if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+
+ if (td->hcsplt != 0)
+ goto receive_spkt;
+ else
+ goto receive_pkt;
+ }
+ if (!(hcint & HCINT_SOFTWARE_ONLY))
+ break;
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || (td->got_short != 0)) {
+ if (td->short_pkt)
+ return (0); /* complete */
+
+ /*
+ * Else need to receive a zero length
+ * packet.
+ */
+ }
+ if (td->hcsplt != 0)
+ goto receive_spkt;
+ else
+ goto receive_pkt;
+ }
+ break;
+
+ case DWC_CHAN_ST_WAIT_S_ANE:
+ if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ goto receive_spkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ goto receive_pkt;
+ }
+ break;
+
+ case DWC_CHAN_ST_RX_PKT:
+ goto receive_pkt;
+
+ case DWC_CHAN_ST_RX_SPKT:
+ goto receive_spkt;
+
+ default:
+ break;
+ }
+ goto busy;
+
+receive_pkt:
+ if (dwc_otg_host_rate_check(td, 1)) {
+ td->state = DWC_CHAN_ST_RX_PKT;
+ dwc_otg_host_channel_free(td);
+ goto busy;
+ }
+
+ if (td->hcsplt != 0)
+ td->hcsplt |= HCSPLT_COMPSPLT;
+ td->state = DWC_CHAN_ST_WAIT_ANE;
+
+ /* receive one packet */
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->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);
+
+ 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;
+
+receive_spkt:
+ if (dwc_otg_host_rate_check(td, 0)) {
+ td->state = DWC_CHAN_ST_RX_SPKT;
+ dwc_otg_host_channel_free(td);
+ goto busy;
+ }
+
+ 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_HCSPLT(td->channel), td->hcsplt);
+
+ 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);
+
+busy:
+ return (1); /* busy */
+}
+
+static uint8_t
dwc_otg_data_rx(struct dwc_otg_td *td)
{
struct dwc_otg_softc *sc;
@@ -551,7 +1299,7 @@
/*
* USB Host Aborted the transfer.
*/
- td->error = 1;
+ td->error_any = 1;
return (0); /* complete */
}
@@ -573,7 +1321,7 @@
got_short = 1;
} else {
/* invalid USB packet */
- td->error = 1;
+ td->error_any = 1;
/* release FIFO */
dwc_otg_common_rx_ack(sc);
@@ -583,7 +1331,7 @@
/* verify the packet byte count */
if (count > td->remainder) {
/* invalid USB packet */
- td->error = 1;
+ td->error_any = 1;
/* release FIFO */
dwc_otg_common_rx_ack(sc);
@@ -610,8 +1358,7 @@
temp = sc->sc_out_ctl[td->ep_no];
- temp |= DOEPCTL_EPENA |
- DOEPCTL_CNAK;
+ temp |= DOEPCTL_EPENA | DOEPCTL_CNAK;
DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp);
@@ -632,6 +1379,235 @@
}
static uint8_t
+dwc_otg_host_data_tx(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))
+ return (1); /* 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)));
+
+ if (hcint & HCINT_STALL) {
+ DPRINTF("CH=%d STALL\n", td->channel);
+ td->error_stall = 1;
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+
+ if (hcint & HCINT_ERRORS) {
+ DPRINTF("CH=%d ERROR\n", td->channel);
+ td->errcnt++;
+ if (td->hcsplt != 0 || td->errcnt >= 3) {
+ td->error_any = 1;
+ return (0); /* complete */
+ }
+ }
+
+ /* treat NYET like NAK, if SPLIT transactions are used */
+ if (hcint & HCINT_NYET) {
+ if (td->hcsplt != 0) {
+ DPRINTF("CH=%d NYET+SPLIT\n", td->channel);
+ hcint &= ~HCINT_NYET;
+ hcint |= HCINT_NAK;
+ }
+ }
+
+ /* 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);
+
+ hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel));
+
+ if (!(hcchar & HCCHAR_CHENA)) {
+ hcint |= HCINT_HALTED_ONLY;
+ sc->sc_chan_state[td->channel].hcint = hcint;
+ }
+ if (!(hcint & HCINT_ERRORS))
+ td->errcnt = 0;
+ }
+
+ switch (td->state) {
+ case DWC_CHAN_ST_START:
+ 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;
+ goto send_pkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+
+ td->offset += td->tx_bytes;
+ td->remainder -= td->tx_bytes;
+ td->toggle ^= 1;
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt)
+ return (0); /* complete */
+
+ /*
+ * Else we need to transmit a short
+ * packet:
+ */
+ }
+ goto send_pkt;
+ }
+ break;
+ 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;
+ goto send_pkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ goto send_cpkt;
+ }
+ break;
+ case DWC_CHAN_ST_WAIT_C_ANE:
+ if (hcint & (HCINT_RETRY | HCINT_ERRORS)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ td->did_nak = 1;
+ goto send_cpkt;
+ }
+ if (hcint & (HCINT_ACK | HCINT_NYET)) {
+ if (!dwc_otg_host_channel_wait(td))
+ break;
+ td->offset += td->tx_bytes;
+ td->remainder -= td->tx_bytes;
+ td->toggle ^= 1;
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt)
+ return (0); /* complete */
+
+ /* else we need to transmit a short packet */
+ }
+ goto send_pkt;
+ }
+ break;
+
+ case DWC_CHAN_ST_TX_PKT:
+ goto send_pkt;
+
+ case DWC_CHAN_ST_TX_CPKT:
+ goto send_cpkt;
+
+ default:
+ break;
+ }
+ goto busy;
+
+send_pkt:
+ if (dwc_otg_host_rate_check(td, 1)) {
+ td->state = DWC_CHAN_ST_TX_PKT;
+ dwc_otg_host_channel_free(td);
+ goto busy;
+ }
+
+ if (td->hcsplt != 0) {
+ td->hcsplt &= ~HCSPLT_COMPSPLT;
+ td->state = DWC_CHAN_ST_WAIT_S_ANE;
+ } else {
+ td->state = DWC_CHAN_ST_WAIT_ANE;
+ }
+
+ /* send one packet at a time */
+ count = td->max_packet_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+
+ /* TODO: HCTSIZ_DOPNG */
+
+ DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->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);
+
+ hcchar = td->hcchar;
+ hcchar &= ~HCCHAR_EPDIR_IN;
+
+ /* must enable before writing data to FIFO */
+ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+
+ if (count != 0) {
+
+ /* clear topmost word before copy */
+ sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0;
+
+ /* copy out data */
+ usbd_copy_out(td->pc, td->offset,
+ sc->sc_tx_bounce_buffer, count);
+
+ if (_DMA_) {
+ } else {
+ /* transfer data into FIFO */
+ bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+ DOTG_DFIFO(td->channel),
+ sc->sc_tx_bounce_buffer, (count + 3) / 4);
+ }
+ }
+
+ /* store number of bytes transmitted */
+ td->tx_bytes = count;
+
+ goto busy;
+
+send_cpkt:
+ 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_HCSPLT(td->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);
+
+busy:
+ return (1); /* busy */
+}
+
+static uint8_t
dwc_otg_data_tx(struct dwc_otg_td *td)
{
struct dwc_otg_softc *sc;
@@ -667,7 +1643,7 @@
* The current transfer was cancelled
* by the USB Host:
*/
- td->error = 1;
+ td->error_any = 1;
return (0); /* complete */
}
}
@@ -710,10 +1686,14 @@
usbd_copy_out(td->pc, td->offset,
sc->sc_tx_bounce_buffer, count);
- /* transfer data into FIFO */
- bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
- DOTG_DFIFO(td->ep_no),
- sc->sc_tx_bounce_buffer, (count + 3) / 4);
+ if (_DMA_) {
+ } else {
+ /* transfer data into FIFO */
+ bus_space_write_region_4(sc->sc_io_tag,
+ sc->sc_io_hdl,
+ DOTG_DFIFO(td->ep_no),
+ sc->sc_tx_bounce_buffer, (count + 3) / 4);
+ }
td->tx_bytes -= count;
td->remainder -= count;
@@ -872,10 +1852,24 @@
dwc_otg_xfer_do_fifo(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 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)) {
/* operation in progress */
@@ -884,7 +1878,7 @@
if (((void *)td) == xfer->td_transfer_last) {
goto done;
}
- if (td->error) {
+ if (td->error_any) {
goto done;
} else if (td->remainder > 0) {
/*
@@ -899,8 +1893,16 @@
* Fetch the next transfer descriptor and transfer
* some flags to the next transfer descriptor
*/
+ 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 */
@@ -912,13 +1914,82 @@
}
static void
+dwc_otg_timer(void *_sc)
+{
+ struct dwc_otg_softc *sc = _sc;
+ struct usb_xfer *xfer;
+ struct dwc_otg_td *td;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTF("\n");
+
+ /* 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)
+ td->did_nak = 0;
+ }
+
+ /* poll jobs */
+ dwc_otg_interrupt_poll(sc);
+
+ if (sc->sc_timer_active) {
+ /* restart timer */
+ usb_callout_reset(&sc->sc_timer,
+ hz / (1000 / DWC_OTG_HOST_TIMER_RATE),
+ &dwc_otg_timer, sc);
+ }
+}
+
+static void
+dwc_otg_timer_start(struct dwc_otg_softc *sc)
+{
+ if (sc->sc_timer_active != 0)
+ return;
+
+ sc->sc_timer_active = 1;
+
+ /* restart timer */
+ usb_callout_reset(&sc->sc_timer,
+ hz / (1000 / DWC_OTG_HOST_TIMER_RATE),
+ &dwc_otg_timer, sc);
+}
+
+static void
+dwc_otg_timer_stop(struct dwc_otg_softc *sc)
+{
+ if (sc->sc_timer_active == 0)
+ return;
+
+ sc->sc_timer_active = 0;
+
+ /* stop timer */
+ usb_callout_stop(&sc->sc_timer);
+}
+
+static void
dwc_otg_interrupt_poll(struct dwc_otg_softc *sc)
{
struct usb_xfer *xfer;
uint32_t temp;
uint8_t got_rx_status;
+ uint8_t x;
repeat:
+ /* get all channel interrupts */
+ for (x = 0; x != sc->sc_host_ch_max; x++) {
+ temp = DWC_OTG_READ_4(sc, DOTG_HCINT(x));
+ if (temp != 0) {
+ DWC_OTG_WRITE_4(sc, DOTG_HCINT(x), temp);
+ temp &= ~(HCINT_SOFTWARE_ONLY |
+ HCINT_HALTED_ONLY);
+ sc->sc_chan_state[x].hcint |= temp;
+ }
+ }
+
if (sc->sc_last_rx_status == 0) {
temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
@@ -932,19 +2003,6 @@
uint8_t ep_no;
- temp = GRXSTSRD_BCNT_GET(
- sc->sc_last_rx_status);
- ep_no = GRXSTSRD_CHNUM_GET(
- sc->sc_last_rx_status);
-
- /* receive data, if any */
- if (temp != 0) {
- DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no);
- bus_space_read_region_4(sc->sc_io_tag, sc->sc_io_hdl,
- DOTG_DFIFO(ep_no),
- sc->sc_rx_bounce_buffer, (temp + 3) / 4);
- }
-
temp = sc->sc_last_rx_status &
GRXSTSRD_PKTSTS_MASK;
@@ -955,8 +2013,26 @@
goto repeat;
}
+ temp = GRXSTSRD_BCNT_GET(
+ sc->sc_last_rx_status);
+ ep_no = GRXSTSRD_CHNUM_GET(
+ sc->sc_last_rx_status);
+
+ /* receive data, if any */
+ if (temp != 0) {
+ DPRINTF("Reading %d bytes from ep %d\n", temp, ep_no);
+ if (_DMA_) {
+ } else {
+ bus_space_read_region_4(sc->sc_io_tag,
+ sc->sc_io_hdl,
+ DOTG_DFIFO(ep_no),
+ sc->sc_rx_bounce_buffer,
+ (temp + 3) / 4);
+ }
+ }
+
/* check if we should dump the data */
- if (!(sc->sc_active_out_ep & (1U << ep_no))) {
+ if (!(sc->sc_active_rx_ep & (1U << ep_no))) {
dwc_otg_common_rx_ack(sc);
goto repeat;
}
@@ -978,7 +2054,7 @@
sc->sc_last_rx_status);
/* check if we should dump the data */
- if (!(sc->sc_active_out_ep & (1U << ep_no))) {
+ if (!(sc->sc_active_rx_ep & (1U << ep_no))) {
dwc_otg_common_rx_ack(sc);
goto repeat;
}
@@ -994,6 +2070,7 @@
}
if (got_rx_status) {
+ /* check if data was consumed */
if (sc->sc_last_rx_status == 0)
goto repeat;
@@ -1042,11 +2119,14 @@
status = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
DWC_OTG_WRITE_4(sc, DOTG_GINTSTS, status);
- DPRINTFN(14, "GINTSTS=0x%08x\n", status);
+ DPRINTFN(14, "GINTSTS=0x%08x HAINT=0x%08x HFNUM=0x%08x\n",
+ status, DWC_OTG_READ_4(sc, DOTG_HAINT),
+ DWC_OTG_READ_4(sc, DOTG_HFNUM));
if (status & GINTSTS_USBRST) {
/* set correct state */
+ sc->sc_flags.status_device_mode = 1;
sc->sc_flags.status_bus_reset = 0;
sc->sc_flags.status_suspend = 0;
sc->sc_flags.change_suspend = 0;
@@ -1064,24 +2144,23 @@
DPRINTFN(5, "end of reset\n");
/* set correct state */
+ sc->sc_flags.status_device_mode = 1;
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;
+ sc->sc_flags.status_low_speed = 0;
+ sc->sc_flags.port_enabled = 1;
/* reset FIFOs */
- dwc_otg_init_fifo(sc);
+ dwc_otg_init_fifo(sc, DWC_MODE_DEVICE);
/* reset function address */
dwc_otg_set_address(sc, 0);
- /* reset active endpoints */
- sc->sc_active_out_ep = 1;
-
/* figure out enumeration speed */
temp = DWC_OTG_READ_4(sc, DOTG_DSTS);
- if (DSTS_ENUMSPD_GET(temp) ==
- DSTS_ENUMSPD_HI)
+ if (DSTS_ENUMSPD_GET(temp) == DSTS_ENUMSPD_HI)
sc->sc_flags.status_high_speed = 1;
else
sc->sc_flags.status_high_speed = 0;
@@ -1095,6 +2174,72 @@
/* complete root HUB interrupt endpoint */
dwc_otg_root_intr(sc);
}
+
+ if (status & GINTSTS_PRTINT) {
+ uint32_t hprt;
+
+ hprt = DWC_OTG_READ_4(sc, DOTG_HPRT);
+
+ /* clear change bits */
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, (hprt & (
+ HPRT_PRTPWR | HPRT_PRTENCHNG |
+ HPRT_PRTCONNDET | HPRT_PRTOVRCURRCHNG)) |
+ sc->sc_hprt_val);
+
+ DPRINTFN(12, "GINTSTS=0x%08x, HPRT=0x%08x\n", status, hprt);
+
+ sc->sc_flags.status_device_mode = 0;
+
+ if (hprt & HPRT_PRTCONNSTS)
+ sc->sc_flags.status_bus_reset = 1;
+ else
+ sc->sc_flags.status_bus_reset = 0;
+
+ if (hprt & HPRT_PRTENCHNG)
+ sc->sc_flags.change_enabled = 1;
+
+ if (hprt & HPRT_PRTENA)
+ sc->sc_flags.port_enabled = 1;
+ else
+ sc->sc_flags.port_enabled = 0;
+
+ if (hprt & HPRT_PRTOVRCURRCHNG)
+ sc->sc_flags.change_over_current = 1;
+
+ if (hprt & HPRT_PRTOVRCURRACT)
+ sc->sc_flags.port_over_current = 1;
+ else
+ sc->sc_flags.port_over_current = 0;
+
+ if (hprt & HPRT_PRTPWR)
+ sc->sc_flags.port_powered = 1;
+ else
+ sc->sc_flags.port_powered = 0;
+
+ if (((hprt & HPRT_PRTSPD_MASK)
+ >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_LOW)
+ sc->sc_flags.status_low_speed = 1;
+ else
+ sc->sc_flags.status_low_speed = 0;
+
+ if (((hprt & HPRT_PRTSPD_MASK)
+ >> HPRT_PRTSPD_SHIFT) == HPRT_PRTSPD_HIGH)
+ sc->sc_flags.status_high_speed = 1;
+ else
+ sc->sc_flags.status_high_speed = 0;
+
+ if (hprt & HPRT_PRTCONNDET)
+ sc->sc_flags.change_connect = 1;
+
+ if (hprt & HPRT_PRTSUSP)
+ dwc_otg_suspend_irq(sc);
+ else
+ dwc_otg_resume_irq(sc);
+
+ /* complete root HUB interrupt endpoint */
+ dwc_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
@@ -1110,26 +2255,12 @@
DPRINTFN(5, "suspend interrupt\n");
- if (!sc->sc_flags.status_suspend) {
- /* update status bits */
- sc->sc_flags.status_suspend = 1;
- sc->sc_flags.change_suspend = 1;
-
- /*
- * Disable suspend interrupt and enable resume
- * interrupt:
- */
- sc->sc_irq_mask &= ~GINTSTS_USBSUSP;
- sc->sc_irq_mask |= GINTSTS_WKUPINT;
- DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
-
- /* complete root HUB interrupt endpoint */
- dwc_otg_root_intr(sc);
- }
+ dwc_otg_suspend_irq(sc);
}
/* check VBUS */
if (status & (GINTSTS_USBSUSP |
GINTSTS_USBRST |
+ GINTMSK_OTGINTMSK |
GINTSTS_SESSREQINT)) {
uint32_t temp;
@@ -1138,7 +2269,7 @@
DPRINTFN(5, "GOTGCTL=0x%08x\n", temp);
dwc_otg_vbus_interrupt(sc,
- (temp & GOTGCTL_BSESVLD) ? 1 : 0);
+ (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0);
}
/* clear all IN endpoint interrupts */
@@ -1155,12 +2286,8 @@
}
}
-#if 0
- /* check if we should poll the FIFOs */
- if (status & (GINTSTS_RXFLVL | GINTSTS_IEPINT))
-#endif
- /* poll FIFO(s) */
- dwc_otg_interrupt_poll(sc);
+ /* poll FIFO(s) */
+ dwc_otg_interrupt_poll(sc);
USB_BUS_UNLOCK(&sc->sc_bus);
}
@@ -1183,11 +2310,17 @@
td->offset = temp->offset;
td->remainder = temp->len;
td->tx_bytes = 0;
- td->error = 0;
- td->npkt = 1;
+ td->error_any = 0;
+ td->npkt = 0;
td->did_stall = temp->did_stall;
td->short_pkt = temp->short_pkt;
td->alt_next = temp->setup_alt_next;
+ td->set_toggle = 0;
+ td->got_short = 0;
+ td->did_nak = 0;
+ td->channel = DWC_OTG_MAX_CHANNELS;
+ td->state = 0;
+ td->errcnt = 0;
}
static void
@@ -1197,6 +2330,7 @@
struct dwc_otg_td *td;
uint32_t x;
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),
@@ -1217,12 +2351,18 @@
temp.setup_alt_next = xfer->flags_int.short_frames_ok;
temp.did_stall = !xfer->flags_int.control_stall;
+ is_host = (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST);
+
/* check if we should prepend a setup message */
if (xfer->flags_int.control_xfr) {
if (xfer->flags_int.control_hdr) {
- temp.func = &dwc_otg_setup_rx;
+ if (is_host)
+ temp.func = &dwc_otg_host_setup_tx;
+ else
+ temp.func = &dwc_otg_setup_rx;
+
temp.len = xfer->frlengths[0];
temp.pc = xfer->frbuffers + 0;
temp.short_pkt = temp.len ? 1 : 0;
@@ -1243,11 +2383,21 @@
if (x != xfer->nframes) {
if (xfer->endpointno & UE_DIR_IN) {
- temp.func = &dwc_otg_data_tx;
- need_sync = 1;
+ if (is_host) {
+ temp.func = &dwc_otg_host_data_rx;
+ need_sync = 0;
+ } else {
+ temp.func = &dwc_otg_data_tx;
+ need_sync = 1;
+ }
} else {
- temp.func = &dwc_otg_data_rx;
- need_sync = 0;
+ if (is_host) {
+ temp.func = &dwc_otg_host_data_tx;
+ need_sync = 0;
+ } else {
+ temp.func = &dwc_otg_data_rx;
+ need_sync = 0;
+ }
}
/* setup "pc" pointer */
@@ -1318,14 +2468,29 @@
* endpoint direction.
*/
if (xfer->endpointno & UE_DIR_IN) {
- temp.func = &dwc_otg_data_rx;
- need_sync = 0;
+ if (is_host) {
+ temp.func = &dwc_otg_host_data_tx;
+ need_sync = 0;
+ } else {
+ temp.func = &dwc_otg_data_rx;
+ need_sync = 0;
+ }
} else {
- temp.func = &dwc_otg_data_tx;
- need_sync = 1;
+ if (is_host) {
+ temp.func = &dwc_otg_host_data_rx;
+ need_sync = 0;
+ } else {
+ temp.func = &dwc_otg_data_tx;
+ need_sync = 1;
+ }
}
dwc_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 = &dwc_otg_data_tx_sync;
@@ -1350,6 +2515,101 @@
/* must have at least one frame! */
td = temp.td;
xfer->td_transfer_last = td;
+
+ if (is_host) {
+
+ 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;
+ td->toggle = (xfer->endpoint->toggle_next ? 1 : 0);
+
+ 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;
+
+ if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_LOW)
+ hcchar |= HCCHAR_LSPDDEV;
+ if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN)
+ hcchar |= HCCHAR_EPDIR_IN;
+
+ switch (xfer->xroot->udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ /* check if root HUB port is running High Speed */
+ if (sc->sc_flags.status_high_speed != 0) {
+ hcsplt = HCSPLT_SPLTENA |
+ (xfer->xroot->udev->hs_port_no <<
+ HCSPLT_PRTADDR_SHIFT) |
+ (xfer->xroot->udev->hs_hub_addr <<
+ HCSPLT_HUBADDR_SHIFT);
+ if (xfer_type == UE_ISOCHRONOUS) /* XXX */
+ hcsplt |= (3 << HCSPLT_XACTPOS_SHIFT);
+ } else {
+ hcsplt = 0;
+ }
+ if (xfer_type == UE_INTERRUPT) {
+ uint32_t ival;
+ ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
+ if (ival == 0)
+ ival = 1;
+ else if (ival > 255)
+ ival = 255;
+ td->tmr_val = sc->sc_tmr_val + ival;
+ td->tmr_res = ival;
+ }
+ break;
+ case USB_SPEED_HIGH:
+ hcsplt = 0;
+ if (xfer_type == UE_ISOCHRONOUS ||
+ xfer_type == UE_INTERRUPT) {
+ hcchar |= ((xfer->max_packet_count & 3)
+ << HCCHAR_MC_SHIFT);
+ }
+ if (xfer_type == UE_INTERRUPT) {
+ uint32_t ival;
+ ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
+ if (ival == 0)
+ ival = 1;
+ else if (ival > 255)
+ ival = 255;
+ td->tmr_val = sc->sc_tmr_val + ival;
+ td->tmr_res = ival;
+ }
+ break;
+ default:
+ hcsplt = 0;
+ break;
+ }
+
+ if (xfer_type == UE_ISOCHRONOUS) {
+ td->tmr_val = xfer->endpoint->isoc_next & 0xFF;
+ td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer);
+ } else if (xfer_type != UE_INTERRUPT) {
+ td->tmr_val = 0;
+ td->tmr_res = 0;
+ }
+
+ /* store configuration in all TD's */
+ while (1) {
+ td->hcchar = hcchar;
+ td->hcsplt = hcsplt;
+
+ if (((void *)td) == xfer->td_transfer_last)
+ break;
+
+ td = td->obj_next;
+ }
+ }
}
static void
@@ -1403,7 +2663,7 @@
{
struct dwc_otg_td *td;
uint32_t len;
- uint8_t error;
+ usb_error_t error;
DPRINTFN(9, "\n");
@@ -1412,21 +2672,25 @@
do {
len = td->remainder;
+ /* store last data toggle */
+ xfer->endpoint->toggle_next = td->toggle;
+
if (xfer->aframes != xfer->nframes) {
/*
* Verify the length and subtract
* the remainder from "frlengths[]":
*/
if (len > xfer->frlengths[xfer->aframes]) {
- td->error = 1;
+ td->error_any = 1;
} else {
xfer->frlengths[xfer->aframes] -= len;
}
}
/* Check for transfer error */
- if (td->error) {
+ if (td->error_any) {
/* the transfer is finished */
- error = 1;
+ error = (td->error_stall ?
+ USB_ERR_STALLED : USB_ERR_IOERROR);
td = NULL;
break;
}
@@ -1458,8 +2722,7 @@
xfer->td_transfer_cache = td;
- return (error ?
- USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+ return (error);
}
static void
@@ -1519,6 +2782,13 @@
if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
DPRINTFN(15, "disabled interrupts!\n");
+ } else {
+ struct dwc_otg_td *td;
+
+ td = xfer->td_transfer_first;
+
+ if (td != NULL)
+ dwc_otg_host_channel_free(td);
}
/* dequeue transfer and start next transfer */
usbd_transfer_done(xfer, error);
@@ -1541,6 +2811,12 @@
USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+ /* check mode */
+ if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+
sc = DWC_OTG_BUS2SC(udev->bus);
/* get endpoint address */
@@ -1563,7 +2839,7 @@
/* clear active OUT ep */
if (!(ep_no & UE_DIR_IN)) {
- sc->sc_active_out_ep &= ~(1U << (ep_no & UE_ADDR));
+ sc->sc_active_rx_ep &= ~(1U << (ep_no & UE_ADDR));
if (sc->sc_last_rx_status != 0 &&
(ep_no & UE_ADDR) == GRXSTSRD_CHNUM_GET(
@@ -1592,7 +2868,7 @@
reg = DOTG_DIEPCTL(ep_no);
} else {
reg = DOTG_DOEPCTL(ep_no);
- sc->sc_active_out_ep |= (1U << ep_no);
+ sc->sc_active_rx_ep |= (1U << ep_no);
}
/* round up and mask away the multiplier count */
@@ -1673,6 +2949,12 @@
struct dwc_otg_softc *sc;
uint8_t x;
+ /* check mode */
+ if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+
/* get softc */
sc = DWC_OTG_BUS2SC(udev->bus);
@@ -1709,8 +2991,8 @@
sc->sc_bus.usbrev = USB_REV_2_0;
sc->sc_bus.methods = &dwc_otg_bus_methods;
- /* reset active endpoints */
- sc->sc_active_out_ep = 1;
+ usb_callout_init_mtx(&sc->sc_timer,
+ &sc->sc_bus.bus_mtx, 0);
USB_BUS_LOCK(&sc->sc_bus);
@@ -1733,11 +3015,23 @@
/* wait a little bit for block to reset */
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 128);
+ switch (sc->sc_mode) {
+ case DWC_MODE_DEVICE:
+ temp = GUSBCFG_FORCEDEVMODE;
+ break;
+ case DWC_MODE_HOST:
+ temp = GUSBCFG_FORCEHOSTMODE;
+ break;
+ default:
+ temp = 0;
+ break;
+ }
+
/* select HSIC or non-HSIC mode */
- if (DWC_OTG_USE_HSIC) {
+ if (dwc_otg_use_hsic) {
DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG,
GUSBCFG_PHYIF |
- GUSBCFG_TRD_TIM_SET(5));
+ GUSBCFG_TRD_TIM_SET(5) | temp);
DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL,
0x000000EC);
@@ -1749,7 +3043,7 @@
} else {
DWC_OTG_WRITE_4(sc, DOTG_GUSBCFG,
GUSBCFG_ULPI_UTMI_SEL |
- GUSBCFG_TRD_TIM_SET(5));
+ GUSBCFG_TRD_TIM_SET(5) | temp);
DWC_OTG_WRITE_4(sc, DOTG_GOTGCTL, 0);
temp = DWC_OTG_READ_4(sc, DOTG_GLPMCFG);
@@ -1762,9 +3056,18 @@
DCTL_CGOUTNAK |
DCTL_CGNPINNAK);
+ /* disable USB port */
+ DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0xFFFFFFFF);
+
+ /* wait 10ms */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
/* enable USB port */
DWC_OTG_WRITE_4(sc, DOTG_PCGCCTL, 0);
+ /* wait 10ms */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
/* pull up D+ */
dwc_otg_pull_up(sc);
@@ -1776,42 +3079,62 @@
sc->sc_dev_ep_max = GHWCFG2_NUMDEVEPS_GET(temp);
+ if (sc->sc_dev_ep_max > DWC_OTG_MAX_ENDPOINTS)
+ sc->sc_dev_ep_max = DWC_OTG_MAX_ENDPOINTS;
+
+ sc->sc_host_ch_max = GHWCFG2_NUMHSTCHNL_GET(temp);
+
+ if (sc->sc_host_ch_max > DWC_OTG_MAX_CHANNELS)
+ sc->sc_host_ch_max = DWC_OTG_MAX_CHANNELS;
+
temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG4);
sc->sc_dev_in_ep_max = GHWCFG4_NUM_IN_EP_GET(temp);
- DPRINTF("Total FIFO size = %d bytes, Device EPs = %d/%d\n",
- sc->sc_fifo_size, sc->sc_dev_ep_max, sc->sc_dev_in_ep_max);
+ DPRINTF("Total FIFO size = %d bytes, Device EPs = %d/%d Host CHs = %d\n",
+ sc->sc_fifo_size, sc->sc_dev_ep_max, sc->sc_dev_in_ep_max,
+ sc->sc_host_ch_max);
/* setup FIFO */
- if (dwc_otg_init_fifo(sc))
+ if (dwc_otg_init_fifo(sc, DWC_MODE_OTG))
return (EINVAL);
/* enable interrupts */
sc->sc_irq_mask = DWC_OTG_MSK_GINT_ENABLED;
DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask);
- /* enable all endpoint interrupts */
- temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2);
- if (temp & GHWCFG2_MPI) {
- uint8_t x;
-
- DPRINTF("Multi Process Interrupts\n");
-
- for (x = 0; x != sc->sc_dev_in_ep_max; x++) {
- DWC_OTG_WRITE_4(sc, DOTG_DIEPEACHINTMSK(x),
+ if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_DEVICE) {
+
+ /* enable all endpoint interrupts */
+ temp = DWC_OTG_READ_4(sc, DOTG_GHWCFG2);
+ if (temp & GHWCFG2_MPI) {
+ uint8_t x;
+
+ DPRINTF("Multi Process Interrupts\n");
+
+ for (x = 0; x != sc->sc_dev_in_ep_max; x++) {
+ DWC_OTG_WRITE_4(sc, DOTG_DIEPEACHINTMSK(x),
+ DIEPMSK_XFERCOMPLMSK);
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPEACHINTMSK(x), 0);
+ }
+ DWC_OTG_WRITE_4(sc, DOTG_DEACHINTMSK, 0xFFFF);
+ } else {
+ DWC_OTG_WRITE_4(sc, DOTG_DIEPMSK,
DIEPMSK_XFERCOMPLMSK);
- DWC_OTG_WRITE_4(sc, DOTG_DOEPEACHINTMSK(x), 0);
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPMSK, 0);
+ DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF);
}
- DWC_OTG_WRITE_4(sc, DOTG_DEACHINTMSK, 0xFFFF);
- } else {
- DWC_OTG_WRITE_4(sc, DOTG_DIEPMSK,
- DIEPMSK_XFERCOMPLMSK);
- DWC_OTG_WRITE_4(sc, DOTG_DOEPMSK, 0);
- DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF);
}
- /* enable global IRQ */
+ if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_HOST) {
+ /* setup clocks */
+ temp = DWC_OTG_READ_4(sc, DOTG_HCFG);
+ temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK);
+ temp |= (1 << HCFG_FSLSPCLKSEL_SHIFT);
+ DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp);
+ }
+
+ /* only enable global IRQ */
DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG,
GAHBCFG_GLBLINTRMSK);
@@ -1825,7 +3148,7 @@
DPRINTFN(5, "GOTCTL=0x%08x\n", temp);
dwc_otg_vbus_interrupt(sc,
- (temp & GOTGCTL_BSESVLD) ? 1 : 0);
+ (temp & (GOTGCTL_ASESVLD | GOTGCTL_BSESVLD)) ? 1 : 0);
USB_BUS_UNLOCK(&sc->sc_bus);
@@ -1841,6 +3164,9 @@
{
USB_BUS_LOCK(&sc->sc_bus);
+ /* stop host timer */
+ dwc_otg_timer_stop(sc);
+
/* set disconnect */
DWC_OTG_WRITE_4(sc, DOTG_DCTL,
DCTL_SFTDISCON);
@@ -1848,6 +3174,7 @@
/* turn off global IRQ */
DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, 0);
+ sc->sc_flags.port_enabled = 0;
sc->sc_flags.port_powered = 0;
sc->sc_flags.status_vbus = 0;
sc->sc_flags.status_bus_reset = 0;
@@ -1859,6 +3186,8 @@
dwc_otg_clocks_off(sc);
USB_BUS_UNLOCK(&sc->sc_bus);
+
+ usb_callout_drain(&sc->sc_timer);
}
static void
@@ -1884,14 +3213,13 @@
}
/*------------------------------------------------------------------------*
- * at91dci bulk support
- * at91dci control support
- * at91dci interrupt support
+ * DWC OTG bulk support
+ * DWC OTG control support
+ * DWC OTG interrupt support
*------------------------------------------------------------------------*/
static void
dwc_otg_device_non_isoc_open(struct usb_xfer *xfer)
{
- return;
}
static void
@@ -1903,7 +3231,6 @@
static void
dwc_otg_device_non_isoc_enter(struct usb_xfer *xfer)
{
- return;
}
static void
@@ -1923,35 +3250,41 @@
};
/*------------------------------------------------------------------------*
- * at91dci full speed isochronous support
+ * DWC OTG full speed isochronous support
*------------------------------------------------------------------------*/
static void
-dwc_otg_device_isoc_fs_open(struct usb_xfer *xfer)
+dwc_otg_device_isoc_open(struct usb_xfer *xfer)
{
- return;
}
static void
-dwc_otg_device_isoc_fs_close(struct usb_xfer *xfer)
+dwc_otg_device_isoc_close(struct usb_xfer *xfer)
{
dwc_otg_device_done(xfer, USB_ERR_CANCELLED);
}
static void
-dwc_otg_device_isoc_fs_enter(struct usb_xfer *xfer)
+dwc_otg_device_isoc_enter(struct usb_xfer *xfer)
{
struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
uint32_t temp;
uint32_t nframes;
+ uint8_t shift = usbd_xfer_get_fps_shift(xfer);
DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
xfer, xfer->endpoint->isoc_next, xfer->nframes);
- temp = DWC_OTG_READ_4(sc, DOTG_DSTS);
-
- /* get the current frame index */
-
- nframes = DSTS_SOFFN_GET(temp);
+ if (xfer->xroot->udev->flags.usb_mode == USB_MODE_HOST) {
+ temp = DWC_OTG_READ_4(sc, DOTG_HFNUM);
+
+ /* get the current frame index */
+ nframes = (temp & HFNUM_FRNUM_MASK);
+ } else {
+ temp = DWC_OTG_READ_4(sc, DOTG_DSTS);
+
+ /* get the current frame index */
+ nframes = DSTS_SOFFN_GET(temp);
+ }
if (sc->sc_flags.status_high_speed)
nframes /= 8;
@@ -1965,7 +3298,7 @@
temp = (nframes - xfer->endpoint->isoc_next) & DWC_OTG_FRAME_MASK;
if ((xfer->endpoint->is_synced == 0) ||
- (temp < xfer->nframes)) {
+ (temp < (((xfer->nframes << shift) + 7) / 8))) {
/*
* If there is data underflow or the pipe queue is
* empty we schedule the transfer a few frames ahead
@@ -1987,32 +3320,32 @@
*/
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;
+ (((xfer->nframes << shift) + 7) / 8);
/* setup TDs */
dwc_otg_setup_standard_chain(xfer);
+
+ /* compute frame number for next insertion */
+ xfer->endpoint->isoc_next += (xfer->nframes << shift);
}
static void
-dwc_otg_device_isoc_fs_start(struct usb_xfer *xfer)
+dwc_otg_device_isoc_start(struct usb_xfer *xfer)
{
/* start TD chain */
dwc_otg_start_standard_chain(xfer);
}
-struct usb_pipe_methods dwc_otg_device_isoc_fs_methods =
+struct usb_pipe_methods dwc_otg_device_isoc_methods =
{
- .open = dwc_otg_device_isoc_fs_open,
- .close = dwc_otg_device_isoc_fs_close,
- .enter = dwc_otg_device_isoc_fs_enter,
- .start = dwc_otg_device_isoc_fs_start,
+ .open = dwc_otg_device_isoc_open,
+ .close = dwc_otg_device_isoc_close,
+ .enter = dwc_otg_device_isoc_enter,
+ .start = dwc_otg_device_isoc_start,
};
/*------------------------------------------------------------------------*
- * at91dci root control support
+ * DWC OTG root control support
*------------------------------------------------------------------------*
* Simulate a hardware HUB by handling all the necessary requests.
*------------------------------------------------------------------------*/
@@ -2079,7 +3412,7 @@
'D', 0, 'W', 0, 'C', 0, 'O', 0, 'T', 0, 'G', 0
#define STRING_PRODUCT \
- 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \
+ 'O', 0, 'T', 0, 'G', 0, ' ', 0, 'R', 0, \
'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \
'U', 0, 'B', 0,
@@ -2352,9 +3685,9 @@
goto tr_valid;
tr_handle_clear_port_feature:
- if (index != 1) {
+ if (index != 1)
goto tr_stalled;
- }
+
DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
switch (value) {
@@ -2363,33 +3696,49 @@
break;
case UHF_PORT_ENABLE:
+ if (sc->sc_flags.status_device_mode == 0) {
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT,
+ sc->sc_hprt_val | HPRT_PRTENA);
+ }
sc->sc_flags.port_enabled = 0;
break;
+ case UHF_C_PORT_RESET:
+ sc->sc_flags.change_reset = 0;
+ break;
+
+ case UHF_C_PORT_ENABLE:
+ sc->sc_flags.change_enabled = 0;
+ break;
+
+ case UHF_C_PORT_OVER_CURRENT:
+ sc->sc_flags.change_over_current = 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;
+ if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) {
+ sc->sc_hprt_val = 0;
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, HPRT_PRTENA);
+ }
dwc_otg_pull_down(sc);
dwc_otg_clocks_off(sc);
break;
+
case UHF_C_PORT_CONNECTION:
/* clear connect change flag */
sc->sc_flags.change_connect = 0;
-
- if (!sc->sc_flags.status_bus_reset) {
- /* we are not connected */
- break;
- }
break;
+
case UHF_C_PORT_SUSPEND:
sc->sc_flags.change_suspend = 0;
break;
+
default:
err = USB_ERR_IOERROR;
goto done;
@@ -2404,15 +3753,53 @@
switch (value) {
case UHF_PORT_ENABLE:
- sc->sc_flags.port_enabled = 1;
break;
+
case UHF_PORT_SUSPEND:
+ if (sc->sc_flags.status_device_mode == 0) {
+ /* set suspend BIT */
+ sc->sc_hprt_val |= HPRT_PRTSUSP;
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+ /* generate HUB suspend event */
+ dwc_otg_suspend_irq(sc);
+ }
+ break;
+
case UHF_PORT_RESET:
+ if (sc->sc_flags.status_device_mode == 0) {
+
+ DPRINTF("PORT RESET\n");
+
+ /* enable PORT reset */
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val | HPRT_PRTRST);
+
+ /* Wait 62.5ms for reset to complete */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16);
+
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+
+ /* Wait 62.5ms for reset to complete */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 16);
+
+ /* reset FIFOs */
+ dwc_otg_init_fifo(sc, DWC_MODE_HOST);
+
+ sc->sc_flags.change_reset = 1;
+ } else {
+ err = USB_ERR_IOERROR;
+ }
+ break;
+
case UHF_PORT_TEST:
case UHF_PORT_INDICATOR:
/* nops */
break;
case UHF_PORT_POWER:
+ if (sc->sc_mode == DWC_MODE_HOST || sc->sc_mode == DWC_MODE_OTG) {
+ sc->sc_hprt_val |= HPRT_PRTPWR;
+ DWC_OTG_WRITE_4(sc, DOTG_HPRT, sc->sc_hprt_val);
+ }
sc->sc_flags.port_powered = 1;
break;
default:
@@ -2425,45 +3812,58 @@
DPRINTFN(9, "UR_GET_PORT_STATUS\n");
- if (index != 1) {
+ if (index != 1)
goto tr_stalled;
+
+ if (sc->sc_flags.status_vbus)
+ dwc_otg_clocks_on(sc);
+ else
+ dwc_otg_clocks_off(sc);
+
+ /* Select Device Side Mode */
+
+ if (sc->sc_flags.status_device_mode) {
+ value = UPS_PORT_MODE_DEVICE;
+ dwc_otg_timer_stop(sc);
+ } else {
+ value = 0;
+ dwc_otg_timer_start(sc);
}
- if (sc->sc_flags.status_vbus) {
- dwc_otg_clocks_on(sc);
- } else {
- dwc_otg_clocks_off(sc);
- }
-
- /* Select Device Side Mode */
-
- value = UPS_PORT_MODE_DEVICE;
-
- if (sc->sc_flags.status_high_speed) {
+
+ if (sc->sc_flags.status_high_speed)
value |= UPS_HIGH_SPEED;
- }
- if (sc->sc_flags.port_powered) {
+ else if (sc->sc_flags.status_low_speed)
+ value |= UPS_LOW_SPEED;
+
+ if (sc->sc_flags.port_powered)
value |= UPS_PORT_POWER;
- }
- if (sc->sc_flags.port_enabled) {
+
+ if (sc->sc_flags.port_enabled)
value |= UPS_PORT_ENABLED;
- }
+
+ if (sc->sc_flags.port_over_current)
+ value |= UPS_OVERCURRENT_INDICATOR;
+
if (sc->sc_flags.status_vbus &&
- sc->sc_flags.status_bus_reset) {
+ sc->sc_flags.status_bus_reset)
value |= UPS_CURRENT_CONNECT_STATUS;
- }
- if (sc->sc_flags.status_suspend) {
+
+ 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) {
+ if (sc->sc_flags.change_connect)
value |= UPS_C_CONNECT_STATUS;
- }
- if (sc->sc_flags.change_suspend) {
+ if (sc->sc_flags.change_suspend)
value |= UPS_C_SUSPEND;
- }
+ if (sc->sc_flags.change_reset)
+ value |= UPS_C_PORT_RESET;
+ if (sc->sc_flags.change_over_current)
+ value |= UPS_C_OVERCURRENT_INDICATOR;
+
USETW(sc->sc_hub_temp.ps.wPortChange, value);
len = sizeof(sc->sc_hub_temp.ps);
goto tr_valid;
@@ -2514,7 +3914,7 @@
if ((xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) {
ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */
- + 1 /* SYNC 2 */ ;
+ + 1 /* SYNC 2 */ + 1 /* SYNC 3 */;
} else {
ntd = xfer->nframes + 1 /* SYNC */ ;
@@ -2586,17 +3986,23 @@
if (udev->device_index != sc->sc_rt_addr) {
- if (udev->flags.usb_mode != USB_MODE_DEVICE) {
- /* not supported */
- return;
+ if (udev->flags.usb_mode == USB_MODE_DEVICE) {
+ if (udev->speed != USB_SPEED_FULL &&
+ udev->speed != USB_SPEED_HIGH) {
+ /* not supported */
+ return;
+ }
+ } else {
+ if (udev->speed != USB_SPEED_LOW &&
+ udev->speed != USB_SPEED_FULL &&
+ udev->speed != USB_SPEED_HIGH) {
+ /* not supported */
+ return;
+ }
}
- if (udev->speed != USB_SPEED_FULL &&
- udev->speed != USB_SPEED_HIGH) {
- /* not supported */
- return;
- }
+
if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS)
- ep->methods = &dwc_otg_device_isoc_fs_methods;
+ ep->methods = &dwc_otg_device_isoc_methods;
else
ep->methods = &dwc_otg_device_non_isoc_methods;
}
@@ -2622,6 +4028,70 @@
}
}
+static void
+dwc_otg_get_dma_delay(struct usb_device *udev, uint32_t *pus)
+{
+ /* DMA delay - wait until any use of memory is finished */
+ *pus = (2125); /* microseconds */
+}
+
+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);
+}
+
+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);
+}
+
struct usb_bus_methods dwc_otg_bus_methods =
{
.endpoint_init = &dwc_otg_ep_init,
@@ -2635,4 +4105,7 @@
.xfer_poll = &dwc_otg_do_poll,
.device_state_change = &dwc_otg_device_state_change,
.set_hw_power_sleep = &dwc_otg_set_hw_power_sleep,
+ .get_dma_delay = &dwc_otg_get_dma_delay,
+ .device_resume = &dwc_otg_device_resume,
+ .device_suspend = &dwc_otg_device_suspend,
};
More information about the Zrouter-src-freebsd
mailing list