Merge tag 'v3.14.25' into backport/v3.14.24-ltsi-rc1+v3.14.25/snapshot-merge.wip
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / usb / renesas_usbhs / fifo.c
index d49f9c3..b0c97a3 100644 (file)
@@ -108,19 +108,45 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
        return list_first_entry(&pipe->list, struct usbhs_pkt, node);
 }
 
+static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
+                             struct usbhs_fifo *fifo);
+static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
+                                struct usbhs_fifo *fifo);
+static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
+                                           struct usbhs_pkt *pkt);
+#define usbhsf_dma_map(p)      __usbhsf_dma_map_ctrl(p, 1)
+#define usbhsf_dma_unmap(p)    __usbhsf_dma_map_ctrl(p, 0)
+static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map);
 struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
 {
        struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
        unsigned long flags;
 
        /********************  spin lock ********************/
        usbhs_lock(priv, flags);
 
+       usbhs_pipe_disable(pipe);
+
        if (!pkt)
                pkt = __usbhsf_pkt_get(pipe);
 
-       if (pkt)
+       if (pkt) {
+               struct dma_chan *chan = NULL;
+
+               if (fifo)
+                       chan = usbhsf_dma_chan_get(fifo, pkt);
+               if (chan) {
+                       dmaengine_terminate_all(chan);
+                       usbhsf_fifo_clear(pipe, fifo);
+                       usbhsf_dma_unmap(pkt);
+               }
+
                __usbhsf_pkt_del(pkt);
+       }
+
+       if (fifo)
+               usbhsf_fifo_unselect(pipe, fifo);
 
        usbhs_unlock(priv, flags);
        /********************  spin unlock ******************/
@@ -544,6 +570,7 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
                usbhsf_send_terminator(pipe, fifo);
 
        usbhsf_tx_irq_ctrl(pipe, !*is_done);
+       usbhs_pipe_running(pipe, !*is_done);
        usbhs_pipe_enable(pipe);
 
        dev_dbg(dev, "  send %d (%d/ %d/ %d/ %d)\n",
@@ -570,12 +597,21 @@ usbhs_fifo_write_busy:
         * retry in interrupt
         */
        usbhsf_tx_irq_ctrl(pipe, 1);
+       usbhs_pipe_running(pipe, 1);
 
        return ret;
 }
 
+static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done)
+{
+       if (usbhs_pipe_is_running(pkt->pipe))
+               return 0;
+
+       return usbhsf_pio_try_push(pkt, is_done);
+}
+
 struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
-       .prepare = usbhsf_pio_try_push,
+       .prepare = usbhsf_pio_prepare_push,
        .try_run = usbhsf_pio_try_push,
 };
 
@@ -589,6 +625,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
        if (usbhs_pipe_is_busy(pipe))
                return 0;
 
+       if (usbhs_pipe_is_running(pipe))
+               return 0;
+
        /*
         * pipe enable to prepare packet receive
         */
@@ -597,6 +636,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
 
        usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
        usbhs_pipe_enable(pipe);
+       usbhs_pipe_running(pipe, 1);
        usbhsf_rx_irq_ctrl(pipe, 1);
 
        return 0;
@@ -642,6 +682,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
            (total_len < maxp)) {               /* short packet */
                *is_done = 1;
                usbhsf_rx_irq_ctrl(pipe, 0);
+               usbhs_pipe_running(pipe, 0);
                usbhs_pipe_disable(pipe);       /* disable pipe first */
        }
 
@@ -681,6 +722,14 @@ usbhs_fifo_read_end:
                usbhs_pipe_number(pipe),
                pkt->length, pkt->actual, *is_done, pkt->zero);
 
+       /*
+        * Transmission end
+        */
+       if (*is_done) {
+               if (usbhs_pipe_is_dcp(pipe))
+                       usbhs_dcp_control_transfer_done(pipe);
+       }
+
 usbhs_fifo_read_busy:
        usbhsf_fifo_unselect(pipe, fifo);
 
@@ -755,8 +804,6 @@ static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe,
        usbhs_bset(priv, fifo->sel, DREQE, dreqe);
 }
 
-#define usbhsf_dma_map(p)      __usbhsf_dma_map_ctrl(p, 1)
-#define usbhsf_dma_unmap(p)    __usbhsf_dma_map_ctrl(p, 0)
 static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
 {
        struct usbhs_pipe *pipe = pkt->pipe;
@@ -797,6 +844,7 @@ static void xfer_work(struct work_struct *work)
        dev_dbg(dev, "  %s %d (%d/ %d)\n",
                fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
 
+       usbhs_pipe_running(pipe, 1);
        usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
        usbhs_pipe_enable(pipe);
        usbhsf_dma_start(pipe, fifo);
@@ -828,6 +876,10 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
        if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
                goto usbhsf_pio_prepare_push;
 
+       /* return at this time if the pipe is running */
+       if (usbhs_pipe_is_running(pipe))
+               return 0;
+
        /* get enable DMA fifo */
        fifo = usbhsf_get_dma_fifo(priv, pkt);
        if (!fifo)
@@ -861,15 +913,29 @@ usbhsf_pio_prepare_push:
 static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
 {
        struct usbhs_pipe *pipe = pkt->pipe;
+       int is_short = pkt->trans % usbhs_pipe_get_maxpacket(pipe);
+
+       pkt->actual += pkt->trans;
 
-       pkt->actual = pkt->trans;
+       if (pkt->actual < pkt->length)
+               *is_done = 0;           /* there are remainder data */
+       else if (is_short)
+               *is_done = 1;           /* short packet */
+       else
+               *is_done = !pkt->zero;  /* send zero packet? */
 
-       *is_done = !pkt->zero;  /* send zero packet ? */
+       usbhs_pipe_running(pipe, !*is_done);
 
        usbhsf_dma_stop(pipe, pipe->fifo);
        usbhsf_dma_unmap(pkt);
        usbhsf_fifo_unselect(pipe, pipe->fifo);
 
+       if (!*is_done) {
+               /* change handler to PIO */
+               pkt->handler = &usbhs_fifo_pio_push_handler;
+               return pkt->handler->try_run(pkt, is_done);
+       }
+
        return 0;
 }
 
@@ -964,8 +1030,10 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
        if ((pkt->actual == pkt->length) ||     /* receive all data */
            (pkt->trans < maxp)) {              /* short packet */
                *is_done = 1;
+               usbhs_pipe_running(pipe, 0);
        } else {
                /* re-enable */
+               usbhs_pipe_running(pipe, 0);
                usbhsf_prepare_pop(pkt, is_done);
        }