svn commit: r257041 - stable/9/sys/dev/usb/controller
Hans Petter Selasky
hselasky at FreeBSD.org
Thu Oct 24 06:22:44 UTC 2013
Author: hselasky
Date: Thu Oct 24 06:22:43 2013
New Revision: 257041
URL: http://svnweb.freebsd.org/changeset/base/257041
Log:
MFC r252912, r254828 and r256548:
Add host mode support to the Mentor Graphics USB OTG controller driver.
PR: usb/181987
Modified:
stable/9/sys/dev/usb/controller/musb_otg.c
stable/9/sys/dev/usb/controller/musb_otg.h
stable/9/sys/dev/usb/controller/musb_otg_atmelarm.c
Directory Properties:
stable/9/sys/ (props changed)
stable/9/sys/dev/ (props changed)
Modified: stable/9/sys/dev/usb/controller/musb_otg.c
==============================================================================
--- stable/9/sys/dev/usb/controller/musb_otg.c Thu Oct 24 06:06:17 2013 (r257040)
+++ stable/9/sys/dev/usb/controller/musb_otg.c Thu Oct 24 06:22:43 2013 (r257041)
@@ -90,6 +90,8 @@ SYSCTL_INT(_hw_usb_musbotg, OID_AUTO, de
&musbotgdebug, 0, "Debug level");
#endif
+#define MAX_NAK_TO 16
+
/* prototypes */
struct usb_bus_methods musbotg_bus_methods;
@@ -98,17 +100,35 @@ struct usb_pipe_methods musbotg_device_c
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.
@@ -123,6 +143,64 @@ static const struct usb_hw_ep_profile mu
}
};
+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)
@@ -213,6 +291,46 @@ musbotg_pull_down(struct musbotg_softc *
}
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;
@@ -243,7 +361,7 @@ musbotg_set_address(struct musbotg_softc
}
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;
@@ -253,6 +371,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);
@@ -269,8 +396,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);
@@ -289,6 +418,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)) {
@@ -337,6 +467,8 @@ musbotg_setup_rx(struct musbotg_td *td)
} else {
sc->sc_dv_addr = 0xFF;
}
+
+ musbotg_channel_free(sc, td);
return (0); /* complete */
not_complete:
@@ -350,10 +482,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;
@@ -496,7 +735,7 @@ musbotg_setup_data_rx(struct musbotg_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;
@@ -614,77 +853,943 @@ musbotg_setup_data_tx(struct musbotg_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);
- if (sc->sc_ep0_busy) {
- sc->sc_ep0_busy = 0;
- sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND;
- MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
- sc->sc_ep0_cmd = 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_DATAEND) {
- /* wait for interrupt */
- return (1); /* not complete */
+ 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;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ if (sc->sc_ep0_busy) {
+ sc->sc_ep0_busy = 0;
+ sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 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_DATAEND) {
+ /* wait for interrupt */
+ return (1); /* not complete */
+ }
+ if (sc->sc_dv_addr != 0xFF) {
+ /* write function address */
+ musbotg_set_address(sc, sc->sc_dv_addr);
+ }
+
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+}
+
+static uint8_t
+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;
+ uint16_t count;
+ uint8_t csr;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 8; /* don't loop forever! */
+ got_short = 0;
+
+ /* 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->channel);
+
+repeat:
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ /* clear overrun */
+ if (csr & MUSB2_MASK_CSRL_RXOVERRUN) {
+ /* make sure we don't clear "RXPKTRDY" */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXPKTRDY);
+ }
+
+ /* check status */
+ if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY))
+ return (1); /* not complete */
+
+ /* 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);
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable-9
mailing list