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

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 |  125 +++++++++++++++++++++++++--
 drivers/usb/musb/musb_gadget.h |    3 +
 drivers/usb/musb/ux500_dma.c   |  184 +++++++++++++++++++++++++++++++++++++++-
 4 files changed, 312 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 24d3921..bcad195 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 b6b84da..b351f52 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -806,14 +806,10 @@ static void rxstate(struct musb *musb, struct 
musb_request *req)
                                /* 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);
+                                                       channel->max_len);
 
                                csr &= ~MUSB_RXCSR_DMAMODE;
                                csr |= (MUSB_RXCSR_DMAENAB |
@@ -821,13 +817,23 @@ static void rxstate(struct musb *musb, struct 
musb_request *req)
 
                                musb_writew(epio, MUSB_RXCSR, csr);
 
-                               if (transfer_size <= musb_ep->packet_sz) {
+                               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);
+
+                                       /* 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;
                                }
 
                                if (c->channel_program(channel,
@@ -835,9 +841,16 @@ static void rxstate(struct musb *musb, struct musb_request 
*req)
                                                        channel->desired_mode,
                                                        request->dma
                                                        + request->actual,
-                                                       transfer_size))
+                                                       transfer_size)){
+
+                                       dev_dbg(musb->controller,
+                                               "%s OUT/RX DMA Size %d/%d 
maxpacket %d\n",
+                                                       musb_ep->end_point.name,
+                                                       len, transfer_size,
+                                                       musb_ep->packet_sz);
 
                                        return;
+                               }
                        }
 #endif /* Mentor's DMA */
 
@@ -915,6 +928,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;
@@ -924,8 +941,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;
 
@@ -960,7 +981,57 @@ void musb_g_rx(struct musb *musb, u8 epnum)
                /* "should not happen"; likely RXPKTRDY pending for DMA */
                dev_dbg(musb->controller, "%s busy, csr %04x\n",
                        musb_ep->end_point.name, csr);
+#ifndef CONFIG_USB_UX500_DMA
                return;
+#else
+               /* 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;
+#endif
        }
 
        if (dma && (csr & MUSB_RXCSR_DMAENAB)) {
@@ -988,6 +1059,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
@@ -1361,8 +1452,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)
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index f1059e7..6eddf14 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -106,7 +106,8 @@ static bool ux500_configure_channel(struct dma_channel 
*channel,
 
        direction = ux500_channel->is_tx ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
        addr_width = (len & 0x3) ? DMA_SLAVE_BUSWIDTH_1_BYTE :
-                                       DMA_SLAVE_BUSWIDTH_4_BYTES;
+                               ((dma_addr & 0x2) ? DMA_SLAVE_BUSWIDTH_2_BYTES :
+                                       DMA_SLAVE_BUSWIDTH_4_BYTES);
 
        slave_conf.direction = direction;
        slave_conf.src_addr = usb_fifo_addr;
@@ -134,6 +135,15 @@ static bool ux500_configure_channel(struct dma_channel 
*channel,
        return true;
 }
 
+/**
+ * ux500_dma_controller_allocate() - allocates the DMA channels
+ * @c: pointer to DMA controller
+ * @hw_ep: pointer to endpoint
+ * @is_tx: transmit or receive direction
+ *
+ * This function allocates the DMA channel and initializes
+ * the channel
+*/
 static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c,
                                struct musb_hw_ep *hw_ep, u8 is_tx)
 {
@@ -173,6 +183,13 @@ static struct dma_channel 
*ux500_dma_channel_allocate(struct dma_controller *c,
        return &(ux500_channel->channel);
 }
 
+/**
+ * ux500_dma_channel_release() - releases the DMA channel
+ * @channel:   channel to be released
+ *
+ * This function releases the DMA channel
+ *
+*/
 static void ux500_dma_channel_release(struct dma_channel *channel)
 {
        struct ux500_dma_channel *ux500_channel = channel->private_data;
@@ -192,13 +209,22 @@ static int ux500_dma_is_compatible(struct dma_channel 
*channel,
 {
        if ((maxpacket & 0x3)           ||
                ((int)buf & 0x3)        ||
-               (length < 512)          ||
-               (length & 0x3))
+               (length < 512))
                return false;
        else
                return true;
 }
 
+/**
+ * ux500_dma_channel_program() - Configures the channel and initiates transfer
+ * @channel:   pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: mode
+ * @dma_addr: physical address of memory
+ * @len: length
+ *
+ * This function configures the channel and initiates the DMA transfer
+*/
 static int ux500_dma_channel_program(struct dma_channel *channel,
                                u16 packet_sz, u8 mode,
                                dma_addr_t dma_addr, u32 len)
@@ -220,6 +246,12 @@ static int ux500_dma_channel_program(struct dma_channel 
*channel,
        return ret;
 }
 
+/**
+ * ux500_dma_channel_abort() - aborts the DMA transfer
+ * @channel:   pointer to DMA channel.
+ *
+ * This function aborts the DMA transfer.
+*/
 static int ux500_dma_channel_abort(struct dma_channel *channel)
 {
        struct ux500_dma_channel *ux500_channel = channel->private_data;
@@ -254,6 +286,124 @@ static int ux500_dma_channel_abort(struct dma_channel 
*channel)
        return 0;
 }
 
+/**
+ * ux500_dma_channel_pause() - pauses the DMA transfer
+ * @channel:   pointer to DMA channel.
+ *
+ * This function pauses the DMA transfer. This is needed to get
+ * the correct residue of an ongoing DMA transfer
+*/
+static int ux500_dma_channel_pause(struct dma_channel *channel)
+{
+       struct ux500_dma_channel *ux500_channel = channel->private_data;
+       struct ux500_dma_controller *controller = ux500_channel->controller;
+       struct musb *musb = controller->private_data;
+       int status;
+
+       status = ux500_channel->dma_chan->device->
+                       device_control(ux500_channel->dma_chan,
+                       DMA_PAUSE, 0);
+
+       dev_dbg(musb->controller,
+               "%s channel=%d, is_tx=%d, status=%d\n",
+               __func__, ux500_channel->ch_num, ux500_channel->is_tx
+               , status);
+       return status;
+}
+
+/**
+ * ux500_dma_channel_resume() - resumes the DMA transfer
+ * @channel:   pointer to DMA channel.
+ *
+ * This function resumes a paused DMA transfer.
+*/
+static int ux500_dma_channel_resume(struct dma_channel *channel)
+{
+       struct ux500_dma_channel *ux500_channel = channel->private_data;
+       struct ux500_dma_controller *controller = ux500_channel->controller;
+       struct musb *musb = controller->private_data;
+       int status;
+
+       status = ux500_channel->dma_chan->device->
+                       device_control(ux500_channel->dma_chan,
+                       DMA_RESUME, 0);
+       dev_dbg(musb->controller,
+               "%s channel=%d, is_tx=%d, status=%d\n",
+               __func__, ux500_channel->ch_num, ux500_channel->is_tx
+               , status);
+       return status;
+}
+
+/**
+ * ux500_dma_tx_status() - Gets the residue of an ongoing DMA transfer
+ * @channel:    pointer to DMA channel
+ *
+ * This function will get the number of bytes left to be transferred
+ * over the DMA
+ */
+static int ux500_dma_tx_status(struct dma_channel *channel)
+{
+       struct ux500_dma_channel *ux500_channel = channel->private_data;
+       struct ux500_dma_controller *controller = ux500_channel->controller;
+       struct musb *musb = controller->private_data;
+       struct dma_tx_state txstate;
+
+       ux500_channel->dma_chan->device->
+                       device_tx_status(ux500_channel->dma_chan,
+                                       ux500_channel->cookie, &txstate);
+       dev_dbg(musb->controller,
+               "%s channel=%d, is_tx=%d, residue=%d\n",
+               __func__, ux500_channel->ch_num, ux500_channel->is_tx
+               , txstate.residue);
+       return txstate.residue;
+}
+
+/**
+ * ux500_dma_check_residue() - Checks if the DMA transfer residue is valid
+ * @channel:    pointer to DMA channel
+ */
+static int ux500_dma_check_residue(struct dma_channel *channel, u32 residue)
+{
+       struct ux500_dma_channel *ux500_channel = channel->private_data;
+       struct ux500_dma_controller *controller = ux500_channel->controller;
+       struct musb *musb = controller->private_data;
+       int status;
+
+       dev_dbg(musb->controller,
+               "%s channel=%d, is_tx=%d, residue=%d\n",
+               __func__, ux500_channel->ch_num, ux500_channel->is_tx
+               , residue);
+
+       /* In cases where we know the transfer length and were expecting
+        * a DMA completion we could get into the DMA busy condition
+        * here if the next packet is short and the EP interrupt occurs
+        * before we recieve dma_completion interrupt for current transfer
+        * Wait for dma_completion. MUSB will interrupt us again for this
+        * short packet when we clear the DMA bits
+        */
+       if (!residue) {
+               dev_dbg(musb->controller,
+                     "%s: Wait for DMA completion\n",
+                     __func__);
+               status = -EINPROGRESS;
+       } else if (residue == ux500_channel->channel.prog_len) {
+               /* Nothing transferred over DMA? */
+               WARN_ON(1);
+               status = -EINVAL;
+       } else {
+               /* residue looks OK */
+               status = 0;
+       }
+
+       return status;
+}
+
+/**
+ * ux500_dma_controller_stop() - releases all the channels and frees the DMA 
pipes
+ * @c: pointer to DMA controller
+ *
+ * This function frees all of the logical channels and frees the DMA pipes
+*/
 static int ux500_dma_controller_stop(struct dma_controller *c)
 {
        struct ux500_dma_controller *controller = container_of(c,
@@ -285,6 +435,15 @@ static int ux500_dma_controller_stop(struct dma_controller 
*c)
        return 0;
 }
 
+
+/**
+ * ux500_dma_controller_start() - creates the logical channels pool and 
registers callbacks
+ * @c: pointer to DMA Controller
+ *
+ * This function requests the logical channels from the DMA driver and creates
+ * logical channels based on event lines and also registers the callbacks which
+ * are invoked after data transfer in the transmit or receive direction.
+*/
 static int ux500_dma_controller_start(struct dma_controller *c)
 {
        struct ux500_dma_controller *controller = container_of(c,
@@ -356,6 +515,12 @@ static int ux500_dma_controller_start(struct 
dma_controller *c)
        return 0;
 }
 
+/**
+ * dma_controller_destroy() - deallocates the DMA controller
+ * @c: pointer to dma controller.
+ *
+ * This function deallocates the DMA controller.
+*/
 void dma_controller_destroy(struct dma_controller *c)
 {
        struct ux500_dma_controller *controller = container_of(c,
@@ -364,6 +529,15 @@ void dma_controller_destroy(struct dma_controller *c)
        kfree(controller);
 }
 
+/**
+ * dma_controller_create() - creates the dma controller and initializes 
callbacks
+ *
+ * @musb:      pointer to mentor core driver data instance|
+ * @base:      base address of musb registers.
+ *
+ * This function creates the DMA controller and initializes the callbacks
+ * that are invoked from the Mentor IP core.
+*/
 struct dma_controller *__devinit
 dma_controller_create(struct musb *musb, void __iomem *base)
 {
@@ -387,6 +561,10 @@ dma_controller_create(struct musb *musb, void __iomem 
*base)
        controller->controller.channel_release = ux500_dma_channel_release;
        controller->controller.channel_program = ux500_dma_channel_program;
        controller->controller.channel_abort = ux500_dma_channel_abort;
+       controller->controller.channel_pause = ux500_dma_channel_pause;
+       controller->controller.channel_resume = ux500_dma_channel_resume;
+       controller->controller.tx_status = ux500_dma_tx_status;
+       controller->controller.check_residue = ux500_dma_check_residue;
        controller->controller.is_compatible = ux500_dma_is_compatible;
 
        return &controller->controller;
-- 
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