Author: gonzo
Date: Sun Jul  7 04:18:35 2013
New Revision: 252912
URL: http://svnweb.freebsd.org/changeset/base/252912

Log:
  - Add initial host mode support for Mentor Graphics USB OTG controller
  - Sync musb_otg_atmelarm with new core logic API

Modified:
  head/sys/dev/usb/controller/musb_otg.c
  head/sys/dev/usb/controller/musb_otg.h
  head/sys/dev/usb/controller/musb_otg_atmelarm.c

Modified: head/sys/dev/usb/controller/musb_otg.c
==============================================================================
--- head/sys/dev/usb/controller/musb_otg.c      Sun Jul  7 04:16:31 2013        
(r252911)
+++ head/sys/dev/usb/controller/musb_otg.c      Sun Jul  7 04:18:35 2013        
(r252912)
@@ -95,6 +95,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;
@@ -103,17 +105,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.
@@ -128,6 +148,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)
@@ -218,6 +296,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;
@@ -248,7 +366,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;
@@ -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 
 }
 
 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;
@@ -606,90 +845,953 @@ musbotg_setup_data_tx(struct musbotg_td 
        /* check remainder */
        if (td->remainder == 0) {
                if (td->short_pkt) {
-                       sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_TXPKTRDY;
+                       sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_TXPKTRDY;
+                       return (0);     /* complete */
+               }
+               /* else we need to transmit a short packet */
+       }
+       /* write command */
+       MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+           MUSB2_MASK_CSR0L_TXPKTRDY);
+
+       return (1);                     /* not complete */
+}
+
+static uint8_t
+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;
+
+       /* 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) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to