From: supriya karanth <supriya.kara...@stericsson.com>

Generic changes for enabling DMA Mode1.
Cleanup of rxtsate function making programming
same for all platforms

Handles:
1) Known transfer length
      a) Non multiple of packet size
      b) Multiple of packet size

2) Unknown transfer lengths
      - Short packet indicates end of transfer

---------------> OUT Endpoint interrupt recieved
|                          |
|      ____________________|_________________
|      |                                     |
| -> No ongoing transfer   ->DMA Transfer ongoing and RXPKTRDY set?
|      |                   ->Short Packet recieved
|  request queued?         ->PAUSE DMA transfer, Read Residue
|      |                    ________________|____________
|      |                    |                            |
|      |                   residue!=0                 residue=0
|      |            -> abort DMA                    ->Resume DMA
|      |            ->Update request->actual_len    ->Wait for DMA
|      |            -> Clear DMA bits in CSR            completion
|      |            -> Read Short Packet                   |
|      |__________________________|                        |______
|                          |                                      |
|                     call rxstate                                |
|                    ->RXPKTRDY set? Read RXCOUNT                 |
|      _____________________|____________________                 |
|      |                                          |               |
| ->RXCOUNT == EP MAX packet Size   ->RXCOUNT < EP MAX packet Size|
| ->Program DMA in Mode1 for length -> Program in PIO / Mode0     |
|    which is multiple of packet    -> Read Short packet from FIFO|
|    size                           -> Update request->actual_len |
|       |                           -> call musb_giveback         |
|       |________________________________________|                |
|____________________________|                                    |
|                            |                                    |
|                            |____________________________________|
|                               |
|               DMA completion interrupt recieved
|        _______________________|_________________
|        |                                        |
|  ->req->len = req->actual            ->req->len < req->actual
|  ->Clear DMA bits                    -> Short packet expected
|  ->Call musb_giveback                ->Clear DMA bits
|       |                              ->wait for next OUT
|       |_________________________________________|
|_______________________________|

Signed-off-by: Supriya Karanth <supriya.kara...@stericsson.com>
Signed-off-by: Praveena NADAHALLY <praveen.nadaha...@stericsson.com>
Acked-by: Linus Walleij <linus.wall...@linaro.org>
---
 drivers/usb/musb/musb_dma.h    |   12 +++
 drivers/usb/musb/musb_gadget.c |  215 +++++++++++++++++++++++-----------------
 drivers/usb/musb/musb_gadget.h |    3 +
 3 files changed, 138 insertions(+), 92 deletions(-)

diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 1b6b827..7754515 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -116,6 +116,7 @@ struct dma_controller;
  * @max_len: the maximum number of bytes the channel can move in one
  *     transaction (typically representing many USB maximum-sized packets)
  * @actual_len: how many bytes have been transferred
+ * @prog_len: how many bytes have been programmed for transfer
  * @status: current channel status (updated e.g. on interrupt)
  * @desired_mode: true if mode 1 is desired; false if mode 0 is desired
  *
@@ -127,6 +128,7 @@ struct dma_channel {
        /* FIXME not void* private_data, but a dma_controller * */
        size_t                  max_len;
        size_t                  actual_len;
+       size_t                  prog_len;
        enum dma_channel_status status;
        bool                    desired_mode;
 };
@@ -155,6 +157,11 @@ dma_channel_status(struct dma_channel *c)
  * @channel_release: call this to release a DMA channel
  * @channel_abort: call this to abort a pending DMA transaction,
  *     returning it to FREE (but allocated) state
+ * @channel_pause: This function pauses the ongoing DMA transfer
+ * @channel_resume: This function resumes the ongoing DMA transfer
+ * @tx_status: Gets the residue of an ongoing DMA transfer
+ * @check_resiudue: checks if the residue of an ongoing DMA
+ *     transfer is valid
  *
  * Controllers manage dma channels.
  */
@@ -169,6 +176,11 @@ struct dma_controller {
                                                        dma_addr_t dma_addr,
                                                        u32 length);
        int                     (*channel_abort)(struct dma_channel *);
+       int                     (*channel_pause)(struct dma_channel *);
+       int                     (*channel_resume)(struct dma_channel *);
+       int                     (*tx_status)(struct dma_channel *);
+       int                     (*check_residue)(struct dma_channel *,
+                                                       u32 residue);
        int                     (*is_compatible)(struct dma_channel *channel,
                                                        u16 maxpacket,
                                                        void *buf, u32 length);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 9db664a..9d47276 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -666,13 +666,17 @@ static void rxstate(struct musb *musb, struct 
musb_request *req)
                 * file_storage and f_mass_storage drivers
                 */
 
-               if (request->short_not_ok && fifo_count == musb_ep->packet_sz)
+               if (fifo_count == musb_ep->packet_sz)
                        use_mode_1 = 1;
                else
                        use_mode_1 = 0;
 
+#ifdef CONFIG_USB_TUSB_OMAP_DMA
+               if (tusb_dma_omap())
+                       use_mode_1 = 0;
+#endif
+
                if (request->actual < request->length) {
-#ifdef CONFIG_USB_INVENTRA_DMA
                        struct dma_controller   *c;
                        struct dma_channel      *channel;
                        int                     use_dma = 0;
@@ -681,45 +685,35 @@ static void rxstate(struct musb *musb, struct 
musb_request *req)
                        c = musb->dma_controller;
                        channel = musb_ep->dma;
 
-       /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in
-        * mode 0 only. So we do not get endpoint interrupts due to DMA
-        * completion. We only get interrupts from DMA controller.
-        *
-        * We could operate in DMA mode 1 if we knew the size of the tranfer
-        * in advance. For mass storage class, request->length = what the host
-        * sends, so that'd work.  But for pretty much everything else,
-        * request->length is routinely more than what the host sends. For
-        * most these gadgets, end of is signified either by a short packet,
-        * or filling the last byte of the buffer.  (Sending extra data in
-        * that last pckate should trigger an overflow fault.)  But in mode 1,
-        * we don't get DMA completion interrupt for short packets.
-        *
-        * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1),
-        * to get endpoint interrupt on every DMA req, but that didn't seem
-        * to work reliably.
-        *
-        * REVISIT an updated g_file_storage can set req->short_not_ok, which
-        * then becomes usable as a runtime "use mode 1" hint...
-        */
-
-                       /* Experimental: Mode1 works with mass storage use 
cases */
                        if (use_mode_1) {
                                csr |= MUSB_RXCSR_AUTOCLEAR;
                                musb_writew(epio, MUSB_RXCSR, csr);
                                csr |= MUSB_RXCSR_DMAENAB;
                                musb_writew(epio, MUSB_RXCSR, csr);
 
+                               musb_writew(epio, MUSB_RXCSR,
+                                               csr | MUSB_RXCSR_DMAMODE);
+
+#ifdef CONFIG_USB_INVENTRA_DMA
                                /*
                                 * this special sequence (enabling and then
                                 * disabling MUSB_RXCSR_DMAMODE) is required
                                 * to get DMAReq to activate
                                 */
-                               musb_writew(epio, MUSB_RXCSR,
-                                               csr | MUSB_RXCSR_DMAMODE);
                                musb_writew(epio, MUSB_RXCSR, csr);
-
+#endif
                                transfer_size = min(request->length - 
request->actual,
                                                channel->max_len);
+
+                               /* Program the transfer length to be
+                                * a multiple of packet size because
+                                * short packets cant be transferred
+                                * over mode1
+                                */
+                               transfer_size = transfer_size -
+                                               (transfer_size
+                                                % musb_ep->packet_sz);
+                               musb_ep->dma->prog_len = transfer_size;
                                musb_ep->dma->desired_mode = 1;
 
                        } else {
@@ -742,54 +736,16 @@ static void rxstate(struct musb *musb, struct 
musb_request *req)
                                        + request->actual,
                                        transfer_size);
 
-                       if (use_dma)
+                       if (use_dma) {
+                               dev_dbg(musb->controller,
+                                       "%s OUT/RX DMA Size %d/%d maxpacket 
%d\n",
+                                               musb_ep->end_point.name,
+                                               fifo_count, transfer_size,
+                                               musb_ep->packet_sz);
                                return;
-#elif defined(CONFIG_USB_UX500_DMA)
-                       if (request->actual < request->length) {
-                               struct dma_controller *c;
-                               struct dma_channel *channel;
-                               int transfer_size = 0;
-
-                               c = musb->dma_controller;
-                               channel = musb_ep->dma;
-
-                               /* In case first packet is short */
-                               if (fifo_count < musb_ep->packet_sz)
-                                       transfer_size = fifo_count;
-                               else if (request->short_not_ok)
-                                       transfer_size = min(request->length -
-                                                       request->actual,
-                                                       channel->max_len);
-                               else
-                                       transfer_size = min(request->length -
-                                                       request->actual,
-                                                       (unsigned)fifo_count);
-
-                               csr &= ~MUSB_RXCSR_DMAMODE;
-                               csr |= (MUSB_RXCSR_DMAENAB |
-                                       MUSB_RXCSR_AUTOCLEAR);
-
-                               musb_writew(epio, MUSB_RXCSR, csr);
-
-                               if (transfer_size <= musb_ep->packet_sz) {
-                                       musb_ep->dma->desired_mode = 0;
-                               } else {
-                                       musb_ep->dma->desired_mode = 1;
-                                       /* Mode must be set after DMAENAB */
-                                       csr |= MUSB_RXCSR_DMAMODE;
-                                       musb_writew(epio, MUSB_RXCSR, csr);
-                               }
-
-                               if (c->channel_program(channel,
-                                                       musb_ep->packet_sz,
-                                                       channel->desired_mode,
-                                                       request->dma
-                                                       + request->actual,
-                                                       transfer_size))
-
-                                       return;
                        }
-#endif /* Mentor's DMA */
+
+                       /* DMA programming failed. Use PIO mode */
 
                        len = request->length - request->actual;
                        dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, 
maxpacket %d\n",
@@ -799,22 +755,6 @@ static void rxstate(struct musb *musb, struct musb_request 
*req)
 
                        fifo_count = min_t(unsigned, len, fifo_count);
 
-#ifdef CONFIG_USB_TUSB_OMAP_DMA
-                       if (tusb_dma_omap()) {
-                               struct dma_controller *c = musb->dma_controller;
-                               struct dma_channel *channel = musb_ep->dma;
-                               u32 dma_addr = request->dma + request->actual;
-                               int ret;
-
-                               ret = c->channel_program(channel,
-                                               musb_ep->packet_sz,
-                                               channel->desired_mode,
-                                               dma_addr,
-                                               fifo_count);
-                               if (ret)
-                                       return;
-                       }
-#endif
                        /*
                         * Unmap the dma buffer back to cpu if dma channel
                         * programming fails. This buffer is mapped if the
@@ -863,6 +803,10 @@ void musb_g_rx(struct musb *musb, u8 epnum)
        void __iomem            *epio = musb->endpoints[epnum].regs;
        struct dma_channel      *dma;
        struct musb_hw_ep       *hw_ep = &musb->endpoints[epnum];
+       u16                     len;
+       u32                     residue;
+       struct dma_controller   *c = musb->dma_controller;
+       int                     status;
 
        if (hw_ep->is_shared_fifo)
                musb_ep = &hw_ep->ep_in;
@@ -872,8 +816,12 @@ void musb_g_rx(struct musb *musb, u8 epnum)
        musb_ep_select(mbase, epnum);
 
        req = next_request(musb_ep);
-       if (!req)
+       if (!req) {
+               musb_ep->rx_pending = 1;
+               dev_dbg(musb->controller, "Packet recieved on %s but no request 
queued\n",
+                       musb_ep->end_point.name);
                return;
+       }
 
        request = &req->request;
 
@@ -905,9 +853,56 @@ void musb_g_rx(struct musb *musb, u8 epnum)
        }
 
        if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
-               /* "should not happen"; likely RXPKTRDY pending for DMA */
+
                dev_dbg(musb->controller, "%s busy, csr %04x\n",
                        musb_ep->end_point.name, csr);
+
+               /* For short_not_ok type transfers and mode0 transfers */
+               if (dma->desired_mode == 0 || request->short_not_ok)
+                       return;
+
+               if (!(csr & MUSB_RXCSR_RXPKTRDY)) {
+                       dev_dbg(musb->controller,
+                               "%s: %s, DMA busy and Packet not ready\n",
+                               __func__, musb_ep->end_point.name);
+                       return;
+               }
+
+               /* For Mode1 we get here for the last short packet */
+               len = musb_readw(epio, MUSB_RXCOUNT);
+
+               /* We should get here only for a short packet.*/
+               if (len == musb_ep->packet_sz) {
+                       dev_dbg(musb->controller,
+                               "%s: %s, Packet not short RXCOUNT=%d\n",
+                               __func__, musb_ep->end_point.name, len);
+                       return;
+               }
+               /* Pause the channel to get the correct transfer residue.*/
+               status = c->channel_pause(musb_ep->dma);
+               residue = c->tx_status(musb_ep->dma);
+               status = c->check_residue(musb_ep->dma, residue);
+
+               if (status) {
+                       /* Something's wrong */
+                       status = c->channel_resume(musb_ep->dma);
+                       return;
+               }
+               /* In cases when we don't know the transfer length the short
+                * packet indicates end of current transfer.
+                */
+               status = c->channel_abort(musb_ep->dma);
+               /* Update with the actual number of bytes transferred */
+               request->actual = musb_ep->dma->prog_len - residue;
+               /* Clear DMA bits in the CSR */
+               csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB
+                               | MUSB_RXCSR_DMAMODE);
+               musb_writew(epio, MUSB_RXCSR, csr);
+               /* Proceed to read the short packet */
+               rxstate(musb, req);
+               /* Don't program next transfer, it will tamper with the DMA
+                * busy condition. Wait for next OUT
+                */
                return;
        }
 
@@ -936,6 +931,26 @@ void musb_g_rx(struct musb *musb, u8 epnum)
                        musb_writew(epio, MUSB_RXCSR, csr);
                }
 
+               /* We get here after DMA completion */
+               if ((dma->desired_mode == 1) && (!request->short_not_ok)) {
+                       /* Incomplete? wait for next OUT packet */
+                       if (request->actual < request->length) {
+                               dev_dbg(musb->controller,
+                                       "%s: %s, Wait for next OUT\n",
+                                       __func__, musb_ep->end_point.name);
+                       } else if (request->actual == request->length) {
+                               dev_dbg(musb->controller,
+                                       "%s: %s, Transfer over mode1 done\n",
+                                       __func__, musb_ep->end_point.name);
+                               musb_g_giveback(musb_ep, request, 0);
+                       } else {
+                               dev_dbg(musb->controller,
+                                       "%s: %s, Transfer length exceeded!!\n",
+                                       __func__, musb_ep->end_point.name);
+                       }
+                       return;
+               }
+
                /* incomplete, and not short? wait for next IN packet */
                if ((request->actual < request->length)
                                && (musb_ep->dma->actual_len
@@ -1305,8 +1320,24 @@ static int musb_gadget_queue(struct usb_ep *ep, struct 
usb_request *req,
        list_add_tail(&request->list, &musb_ep->req_list);
 
        /* it this is the head of the queue, start i/o ... */
-       if (!musb_ep->busy && &request->list == musb_ep->req_list.next)
+       if (!musb_ep->busy && &request->list == musb_ep->req_list.next) {
+
+               /* In case of RX, if there is no packet pending to be read
+                * from fifo then wait for next interrupt
+                */
+               if (!request->tx) {
+                       if (!musb_ep->rx_pending) {
+                               dev_dbg(musb->controller, "No packet pending 
for %s\n",
+                                       ep->name);
+                               goto cleanup;
+                       } else {
+                               musb_ep->rx_pending = 0;
+                               dev_dbg(musb->controller, "Read packet from 
fifo %s\n",
+                                       ep->name);
+                       }
+               }
                musb_ep_restart(musb, request);
+       }
 
 cleanup:
        spin_unlock_irqrestore(&musb->lock, lockflags);
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index 66b7c5e..55eb686 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -90,6 +90,9 @@ struct musb_ep {
        u8                              busy;
 
        u8                              hb_mult;
+
+       /* true if packet is received in fifo and req_list is empty */
+       u8                              rx_pending;
 };
 
 static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to