summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorgonzo <gonzo@FreeBSD.org>2013-07-07 04:18:35 +0000
committergonzo <gonzo@FreeBSD.org>2013-07-07 04:18:35 +0000
commite53815624101b9a58df54ecf307d7399e774e635 (patch)
tree87be2140f069240ab4a763e02e14a7ee085e0650 /sys/dev/usb
parent512e5343cf6f5fa545734a03e0943f4d7ada4d57 (diff)
downloadFreeBSD-src-e53815624101b9a58df54ecf307d7399e774e635.zip
FreeBSD-src-e53815624101b9a58df54ecf307d7399e774e635.tar.gz
- Add initial host mode support for Mentor Graphics USB OTG controller
- Sync musb_otg_atmelarm with new core logic API
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/controller/musb_otg.c1595
-rw-r--r--sys/dev/usb/controller/musb_otg.h49
-rw-r--r--sys/dev/usb/controller/musb_otg_atmelarm.c28
3 files changed, 1568 insertions, 104 deletions
diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c
index 10dc90b..5113d7a 100644
--- a/sys/dev/usb/controller/musb_otg.c
+++ b/sys/dev/usb/controller/musb_otg.c
@@ -95,6 +95,8 @@ SYSCTL_INT(_hw_usb_musbotg, OID_AUTO, debug, CTLFLAG_RW,
&musbotgdebug, 0, "Debug level");
#endif
+#define MAX_NAK_TO 16
+
/* prototypes */
struct usb_bus_methods musbotg_bus_methods;
@@ -103,17 +105,35 @@ struct usb_pipe_methods musbotg_device_ctrl_methods;
struct usb_pipe_methods musbotg_device_intr_methods;
struct usb_pipe_methods musbotg_device_isoc_methods;
-static musbotg_cmd_t musbotg_setup_rx;
-static musbotg_cmd_t musbotg_setup_data_rx;
-static musbotg_cmd_t musbotg_setup_data_tx;
-static musbotg_cmd_t musbotg_setup_status;
-static musbotg_cmd_t musbotg_data_rx;
-static musbotg_cmd_t musbotg_data_tx;
+/* Control transfers: Device mode */
+static musbotg_cmd_t musbotg_dev_ctrl_setup_rx;
+static musbotg_cmd_t musbotg_dev_ctrl_data_rx;
+static musbotg_cmd_t musbotg_dev_ctrl_data_tx;
+static musbotg_cmd_t musbotg_dev_ctrl_status;
+
+/* Control transfers: Host mode */
+static musbotg_cmd_t musbotg_host_ctrl_setup_tx;
+static musbotg_cmd_t musbotg_host_ctrl_data_rx;
+static musbotg_cmd_t musbotg_host_ctrl_data_tx;
+static musbotg_cmd_t musbotg_host_ctrl_status_rx;
+static musbotg_cmd_t musbotg_host_ctrl_status_tx;
+
+/* Bulk, Interrupt, Isochronous: Device mode */
+static musbotg_cmd_t musbotg_dev_data_rx;
+static musbotg_cmd_t musbotg_dev_data_tx;
+
+/* Bulk, Interrupt, Isochronous: Host mode */
+static musbotg_cmd_t musbotg_host_data_rx;
+static musbotg_cmd_t musbotg_host_data_tx;
+
static void musbotg_device_done(struct usb_xfer *, usb_error_t);
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 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);
/*
* Here is a configuration that the chip supports.
@@ -128,6 +148,64 @@ static const struct usb_hw_ep_profile musbotg_ep_profile[1] = {
}
};
+static int
+musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td)
+{
+ int ch;
+ int ep;
+
+ ep = td->ep_no;
+
+ /* In device mode each EP got its own channel */
+ if (sc->sc_mode == MUSB2_DEVICE_MODE) {
+ musbotg_ep_int_set(sc, ep, 1);
+ return (ep);
+ }
+
+ /*
+ * All control transactions go through EP0
+ */
+ if (ep == 0) {
+ if (sc->sc_channel_mask & (1 << 0))
+ return (-1);
+ sc->sc_channel_mask |= (1 << 0);
+ musbotg_ep_int_set(sc, ep, 1);
+ 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);
+ }
+ }
+
+ DPRINTFN(-1, "No available channels. Mask: %04x\n", sc->sc_channel_mask);
+
+ return (-1);
+}
+
+static void
+musbotg_channel_free(struct musbotg_softc *sc, struct musbotg_td *td)
+{
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ if (sc->sc_mode == MUSB2_DEVICE_MODE)
+ return;
+
+ if (td == NULL)
+ return;
+ if (td->channel == -1)
+ return;
+
+ musbotg_ep_int_set(sc, td->channel, 0);
+ sc->sc_channel_mask &= ~(1 << td->channel);
+
+ td->channel = -1;
+}
+
static void
musbotg_get_hw_ep_profile(struct usb_device *udev,
const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
@@ -218,6 +296,46 @@ musbotg_pull_down(struct musbotg_softc *sc)
}
static void
+musbotg_suspend_host(struct musbotg_softc *sc)
+{
+ uint8_t temp;
+
+ if (sc->sc_flags.status_suspend) {
+ return;
+ }
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp |= MUSB2_MASK_SUSPMODE;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+ sc->sc_flags.status_suspend = 1;
+}
+
+static void
+musbotg_wakeup_host(struct musbotg_softc *sc)
+{
+ uint8_t temp;
+
+ if (!(sc->sc_flags.status_suspend)) {
+ return;
+ }
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp &= ~MUSB2_MASK_SUSPMODE;
+ temp |= MUSB2_MASK_RESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+
+ /* wait 20 milliseconds */
+ /* Wait for reset to complete. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp &= ~MUSB2_MASK_RESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+
+ sc->sc_flags.status_suspend = 0;
+}
+
+static void
musbotg_wakeup_peer(struct musbotg_softc *sc)
{
uint8_t temp;
@@ -248,7 +366,7 @@ musbotg_set_address(struct musbotg_softc *sc, uint8_t addr)
}
static uint8_t
-musbotg_setup_rx(struct musbotg_td *td)
+musbotg_dev_ctrl_setup_rx(struct musbotg_td *td)
{
struct musbotg_softc *sc;
struct usb_device_request req;
@@ -258,6 +376,15 @@ musbotg_setup_rx(struct musbotg_td *td)
/* get pointer to softc */
sc = MUSBOTG_PC2SC(td->pc);
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
/* select endpoint 0 */
MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
@@ -274,8 +401,10 @@ musbotg_setup_rx(struct musbotg_td *td)
/* do not stall at this point */
td->did_stall = 1;
/* wait for interrupt */
+ DPRINTFN(0, "CSR0 DATAEND\n");
goto not_complete;
}
+
if (csr & MUSB2_MASK_CSR0L_SENTSTALL) {
/* clear SENTSTALL */
MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
@@ -294,6 +423,7 @@ musbotg_setup_rx(struct musbotg_td *td)
sc->sc_ep0_busy = 0;
}
if (sc->sc_ep0_busy) {
+ DPRINTFN(0, "EP0 BUSY\n");
goto not_complete;
}
if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) {
@@ -342,6 +472,8 @@ musbotg_setup_rx(struct musbotg_td *td)
} else {
sc->sc_dv_addr = 0xFF;
}
+
+ musbotg_channel_free(sc, td);
return (0); /* complete */
not_complete:
@@ -355,10 +487,117 @@ not_complete:
return (1); /* not complete */
}
+static uint8_t
+musbotg_host_ctrl_setup_tx(struct musbotg_td *td)
+{
+ struct musbotg_softc *sc;
+ struct usb_device_request req;
+ uint8_t csr, csrh;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ /* Not ready yet yet */
+ if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+ return (1);
+
+ /* Failed */
+ if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+ MUSB2_MASK_CSR0L_ERROR))
+ {
+ /* Clear status bit */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+ td->error = 1;
+ }
+
+ if (csr & MUSB2_MASK_CSR0L_NAKTIMO) {
+ DPRINTFN(1, "NAK timeout\n");
+
+ if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+ csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+ csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ }
+
+ csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ td->error = 1;
+ }
+
+ if (td->error) {
+ musbotg_channel_free(sc, td);
+ return (0);
+ }
+
+ /* Fifo is not empty and there is no NAK timeout */
+ if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+ return (1);
+
+ /* check if we are complete */
+ if (td->remainder == 0) {
+ /* we are complete */
+ musbotg_channel_free(sc, td);
+ return (0);
+ }
+
+ /* copy data into real buffer */
+ usbd_copy_out(td->pc, 0, &req, sizeof(req));
+
+ /* send data */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req));
+
+ /* update offset and remainder */
+ td->offset += sizeof(req);
+ td->remainder -= sizeof(req);
+
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_TXPKTRDY |
+ MUSB2_MASK_CSR0L_SETUPPKT);
+
+ /* Just to be consistent, not used above */
+ td->transaction_started = 1;
+
+ return (1); /* in progress */
+}
+
/* Control endpoint only data handling functions (RX/TX/SYNC) */
static uint8_t
-musbotg_setup_data_rx(struct musbotg_td *td)
+musbotg_dev_ctrl_data_rx(struct musbotg_td *td)
{
struct usb_page_search buf_res;
struct musbotg_softc *sc;
@@ -501,7 +740,7 @@ musbotg_setup_data_rx(struct musbotg_td *td)
}
static uint8_t
-musbotg_setup_data_tx(struct musbotg_td *td)
+musbotg_dev_ctrl_data_tx(struct musbotg_td *td)
{
struct usb_page_search buf_res;
struct musbotg_softc *sc;
@@ -619,7 +858,363 @@ musbotg_setup_data_tx(struct musbotg_td *td)
}
static uint8_t
-musbotg_setup_status(struct musbotg_td *td)
+musbotg_host_ctrl_data_rx(struct musbotg_td *td)
+{
+ struct usb_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+ uint8_t got_short;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ got_short = 0;
+ if (!td->transaction_started) {
+ td->transaction_started = 1;
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0),
+ td->dev_addr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_REQPKT);
+
+ return (1);
+ }
+
+ if (csr & MUSB2_MASK_CSR0L_NAKTIMO) {
+ csr &= ~MUSB2_MASK_CSR0L_REQPKT;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ td->error = 1;
+ }
+
+ /* Failed */
+ if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+ MUSB2_MASK_CSR0L_ERROR))
+ {
+ /* Clear status bit */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+ td->error = 1;
+ }
+
+ if (td->error) {
+ musbotg_channel_free(sc, td);
+ return (0); /* we are complete */
+ }
+
+ if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY))
+ return (1); /* not yet */
+
+ /* get the packet byte count */
+ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+ /* verify the packet byte count */
+ if (count != td->max_frame_size) {
+ if (count < td->max_frame_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ uint32_t temp;
+
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf,
+ temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0),
+ (void *)(&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ usbd_copy_in(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* receive data */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ csr &= ~MUSB2_MASK_CSR0L_RXPKTRDY;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+
+ musbotg_channel_free(sc, td);
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+
+ td->transaction_started = 1;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_REQPKT);
+
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_host_ctrl_data_tx(struct musbotg_td *td)
+{
+ struct usb_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr, csrh;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* No free EPs */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+ MUSB2_MASK_CSR0L_ERROR)) {
+ /* clear status bits */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ td->error = 1;
+ }
+
+ if (csr & MUSB2_MASK_CSR0L_NAKTIMO ) {
+
+ if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+ csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+ csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ }
+
+ csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ td->error = 1;
+ }
+
+
+ if (td->error) {
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+
+ /*
+ * Wait while FIFO is empty.
+ * Do not flush it because it will cause transactions
+ * with size more then packet size. It might upset
+ * some devices
+ */
+ if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY)
+ return (1);
+
+ /* Packet still being processed */
+ if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+ return (1);
+
+ if (td->transaction_started) {
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+
+ /* We're not complete - more transactions required */
+ td->transaction_started = 0;
+ }
+
+ /* check for short packet */
+ count = td->max_frame_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+
+ while (count > 0) {
+ uint32_t temp;
+
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ usbd_copy_out(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag,
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(0),
+ sc->sc_bounce_buf, temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0),
+ ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* transmit data */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer,
+ buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* Function address */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+ /* TX NAK timeout */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_TXPKTRDY);
+
+ td->transaction_started = 1;
+
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_dev_ctrl_status(struct musbotg_td *td)
{
struct musbotg_softc *sc;
uint8_t csr;
@@ -649,11 +1244,166 @@ musbotg_setup_status(struct musbotg_td *td)
/* write function address */
musbotg_set_address(sc, sc->sc_dv_addr);
}
+
+ musbotg_channel_free(sc, td);
return (0); /* complete */
}
static uint8_t
-musbotg_data_rx(struct musbotg_td *td)
+musbotg_host_ctrl_status_rx(struct musbotg_td *td)
+{
+ struct musbotg_softc *sc;
+ uint8_t csr, csrh;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ if (!td->transaction_started) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0),
+ td->dev_addr);
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
+
+ /* RX NAK timeout */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+
+ td->transaction_started = 1;
+
+ /* Disable PING */
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH);
+ csrh |= MUSB2_MASK_CSR0H_PING_DIS;
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, csrh);
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_STATUSPKT |
+ MUSB2_MASK_CSR0L_REQPKT);
+
+ return (1); /* Just started */
+
+ }
+
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "IN STATUS csr=0x%02x\n", csr);
+
+ if (csr & MUSB2_MASK_CSR0L_RXPKTRDY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_RXPKTRDY_CLR);
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+
+ if (csr & MUSB2_MASK_CSR0L_NAKTIMO) {
+ csr &= ~ (MUSB2_MASK_CSR0L_STATUSPKT |
+ MUSB2_MASK_CSR0L_REQPKT);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+ td->error = 1;
+ }
+
+ /* Failed */
+ if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+ MUSB2_MASK_CSR0L_ERROR))
+ {
+ /* Clear status bit */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+ td->error = 1;
+ }
+
+ if (td->error) {
+ musbotg_channel_free(sc, td);
+ return (0);
+ }
+
+ return (1); /* Not ready yet */
+}
+
+static uint8_t
+musbotg_host_ctrl_status_tx(struct musbotg_td *td)
+{
+ struct musbotg_softc *sc;
+ uint8_t csr;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d/%d [%d@%d.%d/%02x]\n", td->channel, td->transaction_started,
+ td->dev_addr,td->haddr,td->hport, td->transfer_type);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ /* Not yet */
+ if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+ return (1);
+
+ /* Failed */
+ if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+ MUSB2_MASK_CSR0L_ERROR))
+ {
+ /* Clear status bit */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+
+ if (td->transaction_started) {
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, MUSB2_MASK_CSR0H_PING_DIS);
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+ /* TX NAK timeout */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+
+ td->transaction_started = 1;
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_STATUSPKT |
+ MUSB2_MASK_CSR0L_TXPKTRDY);
+
+ return (1); /* wait for interrupt */
+}
+
+static uint8_t
+musbotg_dev_data_rx(struct musbotg_td *td)
{
struct usb_page_search buf_res;
struct musbotg_softc *sc;
@@ -668,8 +1418,15 @@ musbotg_data_rx(struct musbotg_td *td)
/* get pointer to softc */
sc = MUSBOTG_PC2SC(td->pc);
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
/* select endpoint */
- MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no);
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
repeat:
/* read out FIFO status */
@@ -683,10 +1440,11 @@ repeat:
MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
MUSB2_MASK_CSRL_RXPKTRDY);
}
+
/* check status */
- if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) {
- return (1); /* not complete */
- }
+ if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY))
+ return (1); /* not complete */
+
/* get the packet byte count */
count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
@@ -703,6 +1461,7 @@ repeat:
} else {
/* invalid USB packet */
td->error = 1;
+ musbotg_channel_free(sc, td);
return (0); /* we are complete */
}
}
@@ -710,6 +1469,7 @@ repeat:
if (count > td->remainder) {
/* invalid USB packet */
td->error = 1;
+ musbotg_channel_free(sc, td);
return (0); /* we are complete */
}
while (count > 0) {
@@ -729,14 +1489,14 @@ repeat:
if (temp) {
/* receive data 4 bytes at a time */
bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
- MUSB2_REG_EPFIFO(td->ep_no), sc->sc_bounce_buf,
+ MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf,
temp / 4);
}
temp = count & 3;
if (temp) {
/* receive data 1 byte at a time */
bus_space_read_multi_1(sc->sc_io_tag,
- sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no),
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel),
((void *)&sc->sc_bounce_buf[count / 4]), temp);
}
usbd_copy_in(td->pc, td->offset,
@@ -752,7 +1512,7 @@ repeat:
/* receive data 4 bytes at a time */
bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
- MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
buf_res.length / 4);
temp = buf_res.length & ~3;
@@ -765,7 +1525,7 @@ repeat:
}
/* receive data */
bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
- MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
buf_res.length);
/* update counters */
@@ -781,6 +1541,7 @@ repeat:
if ((td->remainder == 0) || got_short) {
if (td->short_pkt) {
/* we are complete */
+ musbotg_channel_free(sc, td);
return (0);
}
/* else need to receive a zero length packet */
@@ -792,7 +1553,7 @@ repeat:
}
static uint8_t
-musbotg_data_tx(struct musbotg_td *td)
+musbotg_dev_data_tx(struct musbotg_td *td)
{
struct usb_page_search buf_res;
struct musbotg_softc *sc;
@@ -805,8 +1566,15 @@ musbotg_data_tx(struct musbotg_td *td)
/* get pointer to softc */
sc = MUSBOTG_PC2SC(td->pc);
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
/* select endpoint */
- MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no);
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
repeat:
@@ -850,14 +1618,14 @@ repeat:
if (temp) {
/* transmit data 4 bytes at a time */
bus_space_write_multi_4(sc->sc_io_tag,
- sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no),
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel),
sc->sc_bounce_buf, temp / 4);
}
temp = count & 3;
if (temp) {
/* receive data 1 byte at a time */
bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
- MUSB2_REG_EPFIFO(td->ep_no),
+ MUSB2_REG_EPFIFO(td->channel),
((void *)&sc->sc_bounce_buf[count / 4]), temp);
}
/* update offset and remainder */
@@ -870,7 +1638,7 @@ repeat:
/* transmit data 4 bytes at a time */
bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
- MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
buf_res.length / 4);
temp = buf_res.length & ~3;
@@ -883,7 +1651,7 @@ repeat:
}
/* transmit data */
bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
- MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
buf_res.length);
/* update counters */
@@ -892,6 +1660,9 @@ repeat:
td->remainder -= buf_res.length;
}
+ /* Max packet size */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet);
+
/* write command */
MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
MUSB2_MASK_CSRL_TXPKTRDY);
@@ -899,6 +1670,7 @@ repeat:
/* check remainder */
if (td->remainder == 0) {
if (td->short_pkt) {
+ musbotg_channel_free(sc, td);
return (0); /* complete */
}
/* else we need to transmit a short packet */
@@ -910,19 +1682,443 @@ repeat:
}
static uint8_t
+musbotg_host_data_rx(struct musbotg_td *td)
+{
+ struct usb_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr, csrh;
+ uint8_t to;
+ uint8_t got_short;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* No free EPs */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ to = 8; /* don't loop forever! */
+ got_short = 0;
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
+
+repeat:
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (!td->transaction_started) {
+ /* Function address */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(td->channel),
+ td->dev_addr);
+
+ /* SPLIT transaction */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(td->channel),
+ td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(td->channel),
+ td->hport);
+
+ /* RX NAK timeout */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+
+ /* Protocol, speed, device endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
+
+ /* Max packet size */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, td->max_packet);
+
+ /* Data Toggle */
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH);
+ DPRINTFN(4, "csrh=0x%02x\n", csrh);
+
+ csrh |= MUSB2_MASK_CSRH_RXDT_WREN;
+ if (td->toggle)
+ csrh |= MUSB2_MASK_CSRH_RXDT_VAL;
+ else
+ csrh &= ~MUSB2_MASK_CSRH_RXDT_VAL;
+
+ /* Set data toggle */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, csrh);
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXREQPKT);
+
+ td->transaction_started = 1;
+ return (1);
+ }
+
+ /* clear NAK timeout */
+ if (csr & MUSB2_MASK_CSRL_RXNAKTO) {
+ DPRINTFN(4, "NAK Timeout\n");
+ if (csr & MUSB2_MASK_CSRL_RXREQPKT) {
+ csr &= ~MUSB2_MASK_CSRL_RXREQPKT;
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, csr);
+
+ csr &= ~MUSB2_MASK_CSRL_RXNAKTO;
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, csr);
+ }
+
+ td->error = 1;
+ }
+
+ if (csr & MUSB2_MASK_CSRL_RXERROR) {
+ DPRINTFN(4, "RXERROR\n");
+ td->error = 1;
+ }
+
+ if (csr & MUSB2_MASK_CSRL_RXSTALL) {
+ DPRINTFN(4, "RXSTALL\n");
+ td->error = 1;
+ }
+
+ if (td->error) {
+ musbotg_channel_free(sc, td);
+ return (0); /* we are complete */
+ }
+
+ if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) {
+ /* No data available yet */
+ return (1);
+ }
+
+ td->toggle ^= 1;
+ /* get the packet byte count */
+ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+ DPRINTFN(4, "count=0x%04x\n", count);
+
+ /*
+ * Check for short or invalid packet:
+ */
+ if (count != td->max_frame_size) {
+ if (count < td->max_frame_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* we are complete */
+ }
+ }
+
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* we are complete */
+ }
+
+ while (count > 0) {
+ uint32_t temp;
+
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf,
+ temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_read_multi_1(sc->sc_io_tag,
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel),
+ ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ usbd_copy_in(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* receive data */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
+ buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* clear status bits */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ musbotg_channel_free(sc, td);
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+
+ /* Reset transaction state and restart */
+ td->transaction_started = 0;
+
+ if (--to)
+ goto repeat;
+
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_host_data_tx(struct musbotg_td *td)
+{
+ struct usb_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr, csrh;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td);
+
+ /* No free EPs */
+ if (td->channel == -1)
+ return (1);
+
+ DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (csr & (MUSB2_MASK_CSRL_TXSTALLED |
+ MUSB2_MASK_CSRL_TXERROR)) {
+ /* clear status bits */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+
+ if (csr & MUSB2_MASK_CSRL_TXNAKTO ) {
+ /*
+ * Flush TX FIFO before clearing NAK TO
+ */
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) {
+ csr |= MUSB2_MASK_CSRL_TXFFLUSH;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) {
+ csr |= MUSB2_MASK_CSRL_TXFFLUSH;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ }
+
+ csr &= ~MUSB2_MASK_CSRL_TXNAKTO;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+ td->error = 1;
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+
+ /*
+ * Wait while FIFO is empty.
+ * Do not flush it because it will cause transactions
+ * with size more then packet size. It might upset
+ * some devices
+ */
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY)
+ return (1);
+
+ /* Packet still being processed */
+ if (csr & MUSB2_MASK_CSRL_TXPKTRDY)
+ return (1);
+
+ if (td->transaction_started) {
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+
+ /* We're not complete - more transactions required */
+ td->transaction_started = 0;
+ }
+
+ /* check for short packet */
+ count = td->max_frame_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+
+ while (count > 0) {
+ uint32_t temp;
+
+ usbd_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ usbd_copy_out(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag,
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel),
+ sc->sc_bounce_buf, temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->channel),
+ ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* transmit data */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->channel), buf_res.buffer,
+ buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* Function address */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(td->channel),
+ td->dev_addr);
+
+ /* SPLIT transaction */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(td->channel),
+ td->haddr);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(td->channel),
+ td->hport);
+
+ /* TX NAK timeout */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+
+ /* Protocol, speed, device endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+ /* Max packet size */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet);
+
+ if (!td->transaction_started) {
+ csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+ DPRINTFN(4, "csrh=0x%02x\n", csrh);
+
+ csrh |= MUSB2_MASK_CSRH_TXDT_WREN;
+ if (td->toggle)
+ csrh |= MUSB2_MASK_CSRH_TXDT_VAL;
+ else
+ csrh &= ~MUSB2_MASK_CSRH_TXDT_VAL;
+
+ /* Set data toggle */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+ }
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXPKTRDY);
+
+ /* Update Data Toggle */
+ td->toggle ^= 1;
+ td->transaction_started = 1;
+
+ return (1); /* not complete */
+}
+
+static uint8_t
musbotg_xfer_do_fifo(struct usb_xfer *xfer)
{
struct musbotg_softc *sc;
struct musbotg_td *td;
DPRINTFN(8, "\n");
+ sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
td = xfer->td_transfer_cache;
while (1) {
+
if ((td->func) (td)) {
/* operation in progress */
break;
}
+
if (((void *)td) == xfer->td_transfer_last) {
goto done;
}
@@ -944,13 +2140,10 @@ musbotg_xfer_do_fifo(struct usb_xfer *xfer)
td = td->obj_next;
xfer->td_transfer_cache = td;
}
- return (1); /* not complete */
+ return (1); /* not complete */
done:
- sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
-
/* compute all actual lengths */
-
musbotg_standard_done(xfer);
return (0); /* complete */
@@ -1000,7 +2193,19 @@ musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on)
}
void
-musbotg_interrupt(struct musbotg_softc *sc)
+musbotg_connect_interrupt(struct musbotg_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+ musbotg_root_intr(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+musbotg_interrupt(struct musbotg_softc *sc,
+ uint16_t rxstat, uint16_t txstat, uint8_t stat)
{
uint16_t rx_status;
uint16_t tx_status;
@@ -1018,11 +2223,20 @@ repeat:
/* read all FIFO interrupts */
rx_status = MUSB2_READ_2(sc, MUSB2_REG_INTRX);
tx_status = MUSB2_READ_2(sc, MUSB2_REG_INTTX);
+ rx_status |= rxstat;
+ tx_status |= txstat;
+ usb_status |= stat;
+
+ /* Clear platform flags after first time */
+ rxstat = 0;
+ txstat = 0;
+ stat = 0;
/* check for any bus state change interrupts */
if (usb_status & (MUSB2_MASK_IRESET |
- MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP)) {
+ MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP |
+ MUSB2_MASK_ICONN | MUSB2_MASK_IDISC)) {
DPRINTFN(4, "real bus interrupt 0x%08x\n", usb_status);
@@ -1085,6 +2299,21 @@ repeat:
MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp);
}
}
+ if (usb_status &
+ (MUSB2_MASK_ICONN | MUSB2_MASK_IDISC))
+ sc->sc_flags.change_connect = 1;
+
+ /*
+ * Host Mode: There is no IRESET so assume bus is
+ * always in reset state once device is connected.
+ */
+ if (sc->sc_mode == MUSB2_HOST_MODE) {
+ if (usb_status & MUSB2_MASK_ICONN)
+ sc->sc_flags.status_bus_reset = 1;
+ if (usb_status & MUSB2_MASK_IDISC)
+ sc->sc_flags.status_bus_reset = 0;
+ }
+
/* complete root HUB interrupt endpoint */
musbotg_root_intr(sc);
}
@@ -1122,9 +2351,15 @@ musbotg_setup_standard_chain_sub(struct musbotg_std_temp *temp)
td->offset = temp->offset;
td->remainder = temp->len;
td->error = 0;
+ td->transaction_started = 0;
td->did_stall = temp->did_stall;
td->short_pkt = temp->short_pkt;
td->alt_next = temp->setup_alt_next;
+ td->channel = temp->channel;
+ td->dev_addr = temp->dev_addr;
+ td->haddr = temp->haddr;
+ td->hport = temp->hport;
+ td->transfer_type = temp->transfer_type;
}
static void
@@ -1135,11 +2370,18 @@ musbotg_setup_standard_chain(struct usb_xfer *xfer)
struct musbotg_td *td;
uint32_t x;
uint8_t ep_no;
+ uint8_t xfer_type;
+ enum usb_dev_speed speed;
+ int tx;
+ int dev_addr;
DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n",
xfer->address, UE_GET_ADDR(xfer->endpointno),
xfer->sumlen, usbd_get_speed(xfer->xroot->udev));
+ sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ ep_no = (xfer->endpointno & UE_ADDR);
+
temp.max_frame_size = xfer->max_frame_size;
td = xfer->td_start[0];
@@ -1147,6 +2389,9 @@ musbotg_setup_standard_chain(struct usb_xfer *xfer)
xfer->td_transfer_cache = td;
/* setup temp */
+ dev_addr = xfer->address;
+
+ xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
temp.pc = NULL;
temp.td = NULL;
@@ -1154,16 +2399,65 @@ musbotg_setup_standard_chain(struct usb_xfer *xfer)
temp.offset = 0;
temp.setup_alt_next = xfer->flags_int.short_frames_ok;
temp.did_stall = !xfer->flags_int.control_stall;
+ temp.channel = -1;
+ temp.dev_addr = dev_addr;
+ temp.haddr = xfer->xroot->udev->hs_hub_addr;
+ temp.hport = xfer->xroot->udev->hs_port_no;
+
+ if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
+ speed = usbd_get_speed(xfer->xroot->udev);
+ xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ temp.transfer_type = MUSB2_MASK_TI_SPEED_LO;
+ break;
+ case USB_SPEED_FULL:
+ temp.transfer_type = MUSB2_MASK_TI_SPEED_FS;
+ break;
+ case USB_SPEED_HIGH:
+ temp.transfer_type = MUSB2_MASK_TI_SPEED_HS;
+ break;
+ default:
+ temp.transfer_type = 0;
+ DPRINTFN(-1, "Invalid USB speed: %d\n", speed);
+ break;
+ }
- sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
- ep_no = (xfer->endpointno & UE_ADDR);
+ switch (xfer_type) {
+ case UE_CONTROL:
+ temp.transfer_type |= MUSB2_MASK_TI_PROTO_CTRL;
+ break;
+ case UE_ISOCHRONOUS:
+ temp.transfer_type |= MUSB2_MASK_TI_PROTO_ISOC;
+ break;
+ case UE_BULK:
+ temp.transfer_type |= MUSB2_MASK_TI_PROTO_BULK;
+ break;
+ case UE_INTERRUPT:
+ temp.transfer_type |= MUSB2_MASK_TI_PROTO_INTR;
+ break;
+ default:
+ DPRINTFN(-1, "Invalid USB transfer type: %d\n",
+ xfer_type);
+ break;
+ }
+
+ temp.transfer_type |= ep_no;
+ td->max_packet = xfer->max_packet_size;
+ td->toggle = xfer->endpoint->toggle_next;
+ }
/* check if we should prepend a setup message */
if (xfer->flags_int.control_xfr) {
if (xfer->flags_int.control_hdr) {
- temp.func = &musbotg_setup_rx;
+ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE)
+ temp.func = &musbotg_dev_ctrl_setup_rx;
+ else
+ temp.func = &musbotg_host_ctrl_setup_tx;
+
temp.len = xfer->frlengths[0];
temp.pc = xfer->frbuffers + 0;
temp.short_pkt = temp.len ? 1 : 0;
@@ -1176,16 +2470,38 @@ musbotg_setup_standard_chain(struct usb_xfer *xfer)
}
if (x != xfer->nframes) {
- if (xfer->endpointno & UE_DIR_IN) {
- if (xfer->flags_int.control_xfr)
- temp.func = &musbotg_setup_data_tx;
- else
- temp.func = &musbotg_data_tx;
+ tx = 0;
+
+ if (xfer->endpointno & UE_DIR_IN)
+ tx = 1;
+
+ if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
+ tx = !tx;
+
+ if (tx) {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &musbotg_host_ctrl_data_tx;
+ else
+ temp.func = &musbotg_host_data_tx;
+ } else {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &musbotg_host_ctrl_data_rx;
+ else
+ temp.func = &musbotg_host_data_rx;
+ }
+
} else {
- if (xfer->flags_int.control_xfr)
- temp.func = &musbotg_setup_data_rx;
- else
- temp.func = &musbotg_data_rx;
+ if (tx) {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &musbotg_dev_ctrl_data_tx;
+ else
+ temp.func = &musbotg_dev_data_tx;
+ } else {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &musbotg_dev_ctrl_data_rx;
+ else
+ temp.func = &musbotg_dev_data_rx;
+ }
}
/* setup "pc" pointer */
@@ -1246,7 +2562,14 @@ musbotg_setup_standard_chain(struct usb_xfer *xfer)
* Send a DATA1 message and invert the current
* endpoint direction.
*/
- temp.func = &musbotg_setup_status;
+ if (sc->sc_mode == MUSB2_DEVICE_MODE)
+ temp.func = &musbotg_dev_ctrl_status;
+ else {
+ if (xfer->endpointno & UE_DIR_IN)
+ temp.func = musbotg_host_ctrl_status_tx;
+ else
+ temp.func = musbotg_host_ctrl_status_rx;
+ }
musbotg_setup_standard_chain_sub(&temp);
}
}
@@ -1269,18 +2592,21 @@ musbotg_timeout(void *arg)
}
static void
-musbotg_ep_int_set(struct usb_xfer *xfer, uint8_t on)
+musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on)
{
- struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
uint16_t temp;
- uint8_t ep_no = xfer->endpointno & UE_ADDR;
/*
* Only enable the endpoint interrupt when we are
* actually waiting for data, hence we are dealing
* with level triggered interrupts !
*/
- if (ep_no == 0) {
+ DPRINTFN(1, "ep_no=%d, on=%d\n", channel, on);
+
+ if (channel == -1)
+ return;
+
+ if (channel == 0) {
temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE);
if (on)
temp |= MUSB2_MASK_EPINT(0);
@@ -1289,23 +2615,23 @@ musbotg_ep_int_set(struct usb_xfer *xfer, uint8_t on)
MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp);
} else {
- if (USB_GET_DATA_ISREAD(xfer)) {
- temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE);
- if (on)
- temp |= MUSB2_MASK_EPINT(ep_no);
- else
- temp &= ~MUSB2_MASK_EPINT(ep_no);
- MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp);
+ temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE);
+ if (on)
+ temp |= MUSB2_MASK_EPINT(channel);
+ else
+ temp &= ~MUSB2_MASK_EPINT(channel);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp);
- } else {
- temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE);
- if (on)
- temp |= MUSB2_MASK_EPINT(ep_no);
- else
- temp &= ~MUSB2_MASK_EPINT(ep_no);
- MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp);
- }
+ temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE);
+ if (on)
+ temp |= MUSB2_MASK_EPINT(channel);
+ else
+ temp &= ~MUSB2_MASK_EPINT(channel);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp);
}
+
+ if (sc->sc_ep_int_set)
+ sc->sc_ep_int_set(sc, channel, on);
}
static void
@@ -1316,8 +2642,6 @@ musbotg_start_standard_chain(struct usb_xfer *xfer)
/* poll one time */
if (musbotg_xfer_do_fifo(xfer)) {
- musbotg_ep_int_set(xfer, 1);
-
DPRINTFN(14, "enabled interrupts on endpoint\n");
/* put transfer on interrupt queue */
@@ -1359,6 +2683,8 @@ musbotg_standard_done_sub(struct usb_xfer *xfer)
do {
len = td->remainder;
+ xfer->endpoint->toggle_next = td->toggle;
+
if (xfer->aframes != xfer->nframes) {
/*
* Verify the length and subtract
@@ -1461,17 +2787,22 @@ done:
static void
musbotg_device_done(struct usb_xfer *xfer, usb_error_t error)
{
+ struct musbotg_td *td;
+ struct musbotg_softc *sc;
+
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
- DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
+ DPRINTFN(1, "xfer=%p, endpoint=%p, error=%d\n",
xfer, xfer->endpoint, error);
- if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
+ DPRINTFN(14, "disabled interrupts on endpoint\n");
- musbotg_ep_int_set(xfer, 0);
+ sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ td = xfer->td_transfer_cache;
+
+ if (td && (td->channel != -1))
+ musbotg_channel_free(sc, td);
- DPRINTFN(14, "disabled interrupts on endpoint\n");
- }
/* dequeue transfer and start next transfer */
usbd_transfer_done(xfer, error);
}
@@ -1716,11 +3047,15 @@ musbotg_init(struct musbotg_softc *sc)
if (sc->sc_clocks_on) {
(sc->sc_clocks_on) (sc->sc_clocks_arg);
}
+
/* wait a little for things to stabilise */
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
/* disable all interrupts */
+ temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
+ DPRINTF("pre-DEVCTL=0x%02x\n", temp);
+
MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0);
MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
@@ -1732,6 +3067,7 @@ musbotg_init(struct musbotg_softc *sc)
/* wait a little bit (10ms) */
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
/* disable double packet buffering */
MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, 0xFFFF);
MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, 0xFFFF);
@@ -1741,11 +3077,20 @@ musbotg_init(struct musbotg_softc *sc)
MUSB2_WRITE_1(sc, MUSB2_REG_POWER,
MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD);
- /* clear Session bit, if set */
+ if (sc->sc_mode == MUSB2_DEVICE_MODE) {
+ /* clear Session bit, if set */
+ temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
+ temp &= ~MUSB2_MASK_SESS;
+ MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
+ } else {
+ /* Enter session for Host mode */
+ temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
+ temp |= MUSB2_MASK_SESS;
+ MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
+ }
- temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
- temp &= ~MUSB2_MASK_SESS;
- MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
+ /* wait a little for things to stabilise */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10);
DPRINTF("DEVCTL=0x%02x\n", temp);
@@ -1881,8 +3226,11 @@ musbotg_init(struct musbotg_softc *sc)
/* turn on default interrupts */
- MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE,
- MUSB2_MASK_IRESET);
+ if (sc->sc_mode == MUSB2_HOST_MODE)
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0xff);
+ else
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE,
+ MUSB2_MASK_IRESET);
musbotg_clocks_off(sc);
@@ -2229,6 +3577,7 @@ musbotg_roothub_exec(struct usb_device *udev,
uint16_t len;
uint16_t value;
uint16_t index;
+ uint8_t reg;
usb_error_t err;
USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
@@ -2492,20 +3841,33 @@ tr_handle_clear_port_feature:
switch (value) {
case UHF_PORT_SUSPEND:
- musbotg_wakeup_peer(sc);
+ if (sc->sc_mode == MUSB2_HOST_MODE)
+ musbotg_wakeup_host(sc);
+ else
+ musbotg_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:
+ sc->sc_flags.change_enabled = 0;
+ break;
+
case UHF_C_PORT_OVER_CURRENT:
+ sc->sc_flags.change_over_current = 0;
+ break;
+
case UHF_C_PORT_RESET:
+ sc->sc_flags.change_reset = 0;
+ break;
+
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
/* nops */
break;
+
case UHF_PORT_POWER:
sc->sc_flags.port_powered = 0;
musbotg_pull_down(sc);
@@ -2534,7 +3896,35 @@ tr_handle_set_port_feature:
sc->sc_flags.port_enabled = 1;
break;
case UHF_PORT_SUSPEND:
+ if (sc->sc_mode == MUSB2_HOST_MODE)
+ musbotg_suspend_host(sc);
+ break;
+
case UHF_PORT_RESET:
+ if (sc->sc_mode == MUSB2_HOST_MODE) {
+ reg = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ reg |= MUSB2_MASK_RESET;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, reg);
+
+ /* Wait for 20 msec */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 5);
+
+ reg = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ reg &= ~MUSB2_MASK_RESET;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, reg);
+
+ /* determine line speed */
+ reg = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ if (reg & MUSB2_MASK_HSMODE)
+ sc->sc_flags.status_high_speed = 1;
+ else
+ sc->sc_flags.status_high_speed = 0;
+
+ sc->sc_flags.change_reset = 1;
+ } else
+ err = USB_ERR_IOERROR;
+ break;
+
case UHF_PORT_TEST:
case UHF_PORT_INDICATOR:
/* nops */
@@ -2564,7 +3954,10 @@ tr_handle_get_port_status:
}
/* Select Device Side Mode */
- value = UPS_PORT_MODE_DEVICE;
+ if (sc->sc_mode == MUSB2_DEVICE_MODE)
+ value = UPS_PORT_MODE_DEVICE;
+ else
+ value = 0;
if (sc->sc_flags.status_high_speed) {
value |= UPS_HIGH_SPEED;
@@ -2575,6 +3968,10 @@ tr_handle_get_port_status:
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) {
value |= UPS_CURRENT_CONNECT_STATUS;
@@ -2589,16 +3986,22 @@ tr_handle_get_port_status:
if (sc->sc_flags.change_connect) {
value |= UPS_C_CONNECT_STATUS;
- if (sc->sc_flags.status_vbus &&
- sc->sc_flags.status_bus_reset) {
- /* reset EP0 state */
- sc->sc_ep0_busy = 0;
- sc->sc_ep0_cmd = 0;
+ if (sc->sc_mode == MUSB2_DEVICE_MODE) {
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ /* reset EP0 state */
+ sc->sc_ep0_busy = 0;
+ sc->sc_ep0_cmd = 0;
+ }
}
}
- 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;
@@ -2734,6 +4137,17 @@ musbotg_xfer_unsetup(struct usb_xfer *xfer)
}
static void
+musbotg_get_dma_delay(struct usb_device *udev, uint32_t *pus)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus);
+
+ if (sc->sc_mode == MUSB2_HOST_MODE)
+ *pus = 2000; /* microseconds */
+ else
+ *pus = 0;
+}
+
+static void
musbotg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
struct usb_endpoint *ep)
{
@@ -2745,12 +4159,6 @@ musbotg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
sc->sc_rt_addr);
if (udev->device_index != sc->sc_rt_addr) {
-
- if ((udev->speed != USB_SPEED_FULL) &&
- (udev->speed != USB_SPEED_HIGH)) {
- /* not supported */
- return;
- }
switch (edesc->bmAttributes & UE_XFERTYPE) {
case UE_CONTROL:
ep->methods = &musbotg_device_ctrl_methods;
@@ -2794,6 +4202,7 @@ musbotg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
struct usb_bus_methods musbotg_bus_methods =
{
.endpoint_init = &musbotg_ep_init,
+ .get_dma_delay = &musbotg_get_dma_delay,
.xfer_setup = &musbotg_xfer_setup,
.xfer_unsetup = &musbotg_xfer_unsetup,
.get_hw_ep_profile = &musbotg_get_hw_ep_profile,
diff --git a/sys/dev/usb/controller/musb_otg.h b/sys/dev/usb/controller/musb_otg.h
index 3b889a9..649cab8 100644
--- a/sys/dev/usb/controller/musb_otg.h
+++ b/sys/dev/usb/controller/musb_otg.h
@@ -32,7 +32,7 @@
#ifndef _MUSB2_OTG_H_
#define _MUSB2_OTG_H_
-#define MUSB2_MAX_DEVICES (USB_MIN_DEVICES + 1)
+#define MUSB2_MAX_DEVICES USB_MAX_DEVICES
/* Common registers */
@@ -105,7 +105,8 @@
#define MUSB2_MASK_CSRL_TXSENTSTALL 0x20/* Device Mode */
#define MUSB2_MASK_CSRL_TXSTALLED 0x20 /* Host Mode */
#define MUSB2_MASK_CSRL_TXDT_CLR 0x40
-#define MUSB2_MASK_CSRL_TXINCOMP 0x80
+#define MUSB2_MASK_CSRL_TXINCOMP 0x80 /* Device mode */
+#define MUSB2_MASK_CSRL_TXNAKTO 0x80 /* Host mode */
/* Device Side Mode */
#define MUSB2_MASK_CSR0L_RXPKTRDY 0x01
@@ -118,6 +119,7 @@
#define MUSB2_MASK_CSR0L_SETUPEND_CLR 0x80
/* Host Side Mode */
+#define MUSB2_MASK_CSR0L_TXFIFONEMPTY 0x02
#define MUSB2_MASK_CSR0L_RXSTALL 0x04
#define MUSB2_MASK_CSR0L_SETUPPKT 0x08
#define MUSB2_MASK_CSR0L_ERROR 0x10
@@ -127,7 +129,7 @@
#define MUSB2_REG_TXCSRH (0x0003 + MUSB2_REG_INDEXED_CSR)
#define MUSB2_MASK_CSRH_TXDT_VAL 0x01 /* Host Mode */
-#define MUSB2_MASK_CSRH_TXDT_WR 0x02 /* Host Mode */
+#define MUSB2_MASK_CSRH_TXDT_WREN 0x02 /* Host Mode */
#define MUSB2_MASK_CSRH_TXDMAREQMODE 0x04
#define MUSB2_MASK_CSRH_TXDT_SWITCH 0x08
#define MUSB2_MASK_CSRH_TXDMAREQENA 0x10
@@ -138,14 +140,16 @@
#define MUSB2_MASK_CSR0H_FFLUSH 0x01 /* Device Side flush FIFO */
#define MUSB2_MASK_CSR0H_DT 0x02 /* Host Side data toggle */
-#define MUSB2_MASK_CSR0H_DT_SET 0x04 /* Host Side */
+#define MUSB2_MASK_CSR0H_DT_WREN 0x04 /* Host Side */
#define MUSB2_MASK_CSR0H_PING_DIS 0x08 /* Host Side */
#define MUSB2_REG_RXCSRL (0x0006 + MUSB2_REG_INDEXED_CSR)
#define MUSB2_MASK_CSRL_RXPKTRDY 0x01
#define MUSB2_MASK_CSRL_RXFIFOFULL 0x02
-#define MUSB2_MASK_CSRL_RXOVERRUN 0x04
-#define MUSB2_MASK_CSRL_RXDATAERR 0x08
+#define MUSB2_MASK_CSRL_RXOVERRUN 0x04 /* Device Mode */
+#define MUSB2_MASK_CSRL_RXERROR 0x04 /* Host Mode */
+#define MUSB2_MASK_CSRL_RXDATAERR 0x08 /* Device Mode */
+#define MUSB2_MASK_CSRL_RXNAKTO 0x08 /* Host Mode */
#define MUSB2_MASK_CSRL_RXFFLUSH 0x10
#define MUSB2_MASK_CSRL_RXSENDSTALL 0x20/* Device Mode */
#define MUSB2_MASK_CSRL_RXREQPKT 0x20 /* Host Mode */
@@ -156,7 +160,7 @@
#define MUSB2_REG_RXCSRH (0x0007 + MUSB2_REG_INDEXED_CSR)
#define MUSB2_MASK_CSRH_RXINCOMP 0x01
#define MUSB2_MASK_CSRH_RXDT_VAL 0x02 /* Host Mode */
-#define MUSB2_MASK_CSRH_RXDT_SET 0x04 /* Host Mode */
+#define MUSB2_MASK_CSRH_RXDT_WREN 0x04 /* Host Mode */
#define MUSB2_MASK_CSRH_RXDMAREQMODE 0x08
#define MUSB2_MASK_CSRH_RXNYET 0x10
#define MUSB2_MASK_CSRH_RXDMAREQENA 0x20
@@ -273,10 +277,13 @@
#define MUSB2_REG_TXHUBPORT(n) (0x0083 + (8*(n)))
#define MUSB2_REG_RXFADDR(n) (0x0084 + (8*(n)))
#define MUSB2_REG_RXHADDR(n) (0x0086 + (8*(n)))
-#define MUSB2_REG_RXHPORT(n) (0x0087 + (8*(n)))
+#define MUSB2_REG_RXHUBPORT(n) (0x0087 + (8*(n)))
#define MUSB2_EP_MAX 16 /* maximum number of endpoints */
+#define MUSB2_DEVICE_MODE 0
+#define MUSB2_HOST_MODE 1
+
#define MUSB2_READ_2(sc, reg) \
bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
@@ -310,12 +317,20 @@ struct musbotg_td {
uint32_t remainder;
uint16_t max_frame_size; /* packet_size * mult */
uint8_t ep_no;
+ uint8_t transfer_type;
+ uint8_t max_packet;
uint8_t error:1;
uint8_t alt_next:1;
uint8_t short_pkt:1;
uint8_t support_multi_buffer:1;
uint8_t did_stall:1;
uint8_t dma_enabled:1;
+ uint8_t transaction_started:1;
+ uint8_t dev_addr;
+ uint8_t toggle;
+ int8_t channel;
+ uint8_t haddr;
+ uint8_t hport;
};
struct musbotg_std_temp {
@@ -333,6 +348,11 @@ struct musbotg_std_temp {
*/
uint8_t setup_alt_next;
uint8_t did_stall;
+ uint8_t dev_addr;
+ int8_t channel;
+ uint8_t haddr;
+ uint8_t hport;
+ uint8_t transfer_type;
};
struct musbotg_config_desc {
@@ -349,6 +369,9 @@ union musbotg_hub_temp {
struct musbotg_flags {
uint8_t change_connect:1;
uint8_t change_suspend:1;
+ uint8_t change_reset:1;
+ uint8_t change_over_current:1;
+ uint8_t change_enabled: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 */
@@ -358,6 +381,7 @@ struct musbotg_flags {
uint8_t clocks_off:1;
uint8_t port_powered:1;
uint8_t port_enabled:1;
+ uint8_t port_over_current:1;
uint8_t d_pulled_up:1;
};
@@ -376,6 +400,7 @@ struct musbotg_softc {
void (*sc_clocks_on) (void *arg);
void (*sc_clocks_off) (void *arg);
+ void (*sc_ep_int_set) (struct musbotg_softc *sc, int ep, int on);
void *sc_clocks_arg;
uint32_t sc_bounce_buf[(1024 * 3) / 4]; /* bounce buffer */
@@ -390,15 +415,21 @@ struct musbotg_softc {
uint8_t sc_conf_data; /* copy of hardware register */
uint8_t sc_hub_idata[1];
+ uint16_t sc_channel_mask; /* 16 endpoints */
struct musbotg_flags sc_flags;
+ uint8_t sc_id;
+ uint8_t sc_mode;
+ void *sc_platform_data;
};
/* prototypes */
usb_error_t musbotg_init(struct musbotg_softc *sc);
void musbotg_uninit(struct musbotg_softc *sc);
-void musbotg_interrupt(struct musbotg_softc *sc);
+void musbotg_interrupt(struct musbotg_softc *sc,
+ uint16_t rxstat, uint16_t txstat, uint8_t stat);
void musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on);
+void musbotg_connect_interrupt(struct musbotg_softc *sc);
#endif /* _MUSB2_OTG_H_ */
diff --git a/sys/dev/usb/controller/musb_otg_atmelarm.c b/sys/dev/usb/controller/musb_otg_atmelarm.c
index bb3e60a..4e446eb 100644
--- a/sys/dev/usb/controller/musb_otg_atmelarm.c
+++ b/sys/dev/usb/controller/musb_otg_atmelarm.c
@@ -94,6 +94,26 @@ musbotg_clocks_off(void *arg)
#endif
}
+static void
+musbotg_wrapper_interrupt(void *arg)
+{
+
+ /*
+ * Nothing to do.
+ * Main driver takes care about everything
+ */
+ musbotg_interrupt(arg, 0, 0, 0);
+}
+
+static void
+musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on)
+{
+ /*
+ * Nothing to do.
+ * Main driver takes care about everything
+ */
+}
+
static int
musbotg_probe(device_t dev)
{
@@ -147,12 +167,16 @@ musbotg_attach(device_t dev)
}
device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus);
+ sc->sc_otg.sc_id = 0;
+ sc->sc_otg.sc_platform_data = sc;
+ sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE;
+
#if (__FreeBSD_version >= 700031)
err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
- NULL, (driver_intr_t *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
+ NULL, (driver_intr_t *)musbotg_wrapper_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
#else
err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
- (driver_intr_t *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
+ (driver_intr_t *)musbotg_wrapper_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
#endif
if (err) {
sc->sc_otg.sc_intr_hdl = NULL;
OpenPOWER on IntegriCloud