usb: renesas_usbhs: add pipe/fifo link
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Mon, 6 Jun 2011 05:18:50 +0000 (14:18 +0900)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 7 Jun 2011 16:10:09 +0000 (09:10 -0700)
renesas_usbhs has CFIFO which is for PIO transfer,
and D0FIFO/D1FIFO which are for DMA transfer.
The pipe selects one of these fifo when it send/recv data.
But fifo must not be selected to different pipe in same time.
This patch add pipe/fifo link for each other,
and fifo is not selected by another pipe until it is unselected.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/renesas_usbhs/fifo.h
drivers/usb/renesas_usbhs/pipe.c
drivers/usb/renesas_usbhs/pipe.h

index 53e2b35..8852423 100644 (file)
@@ -21,6 +21,8 @@
 
 #define usbhsf_get_cfifo(p)    (&((p)->fifo_info.cfifo))
 
+#define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */
+
 /*
  *             packet info function
  */
@@ -237,6 +239,15 @@ static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv,
        return usbhs_read(priv, fifo->ctr) & DTLN_MASK;
 }
 
+static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
+                                struct usbhs_fifo *fifo)
+{
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+       usbhs_pipe_select_fifo(pipe, NULL);
+       usbhs_write(priv, fifo->sel, 0);
+}
+
 static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
                              struct usbhs_fifo *fifo,
                              int write)
@@ -247,6 +258,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
        u16 mask = ((1 << 5) | 0xF);            /* mask of ISEL | CURPIPE */
        u16 base = usbhs_pipe_number(pipe);     /* CURPIPE */
 
+       if (usbhs_pipe_is_busy(pipe) ||
+           usbhsf_fifo_is_busy(fifo))
+               return -EBUSY;
+
        if (usbhs_pipe_is_dcp(pipe))
                base |= (1 == write) << 5;      /* ISEL */
 
@@ -255,8 +270,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
 
        /* check ISEL and CURPIPE value */
        while (timeout--) {
-               if (base == (mask & usbhs_read(priv, fifo->sel)))
+               if (base == (mask & usbhs_read(priv, fifo->sel))) {
+                       usbhs_pipe_select_fifo(pipe, fifo);
                        return 0;
+               }
                udelay(10);
        }
 
@@ -283,7 +300,7 @@ static int usbhsf_try_push(struct usbhs_pkt *pkt, int *is_done)
 
        ret = usbhsf_fifo_select(pipe, fifo, 1);
        if (ret < 0)
-               goto usbhs_fifo_write_busy;
+               return 0;
 
        ret = usbhs_pipe_is_accessible(pipe);
        if (ret < 0)
@@ -347,9 +364,13 @@ static int usbhsf_try_push(struct usbhs_pkt *pkt, int *is_done)
                        usbhs_dcp_control_transfer_done(pipe);
        }
 
+       usbhsf_fifo_unselect(pipe, fifo);
+
        return 0;
 
 usbhs_fifo_write_busy:
+       usbhsf_fifo_unselect(pipe, fifo);
+
        /*
         * pipe is busy.
         * retry in interrupt
@@ -367,16 +388,13 @@ struct usbhs_pkt_handle usbhs_fifo_push_handler = {
 static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
 {
        struct usbhs_pipe *pipe = pkt->pipe;
-       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
-       struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
-       int ret;
+
+       if (usbhs_pipe_is_busy(pipe))
+               return 0;
 
        /*
-        * select pipe and enable it to prepare packet receive
+        * pipe enable to prepare packet receive
         */
-       ret = usbhsf_fifo_select(pipe, fifo, 0);
-       if (ret < 0)
-               return ret;
 
        usbhs_pipe_enable(pipe);
        usbhsf_rx_irq_ctrl(pipe, 1);
@@ -400,11 +418,11 @@ static int usbhsf_try_pop(struct usbhs_pkt *pkt, int *is_done)
 
        ret = usbhsf_fifo_select(pipe, fifo, 0);
        if (ret < 0)
-               return ret;
+               return 0;
 
        ret = usbhsf_fifo_barrier(priv, fifo);
        if (ret < 0)
-               return ret;
+               goto usbhs_fifo_read_busy;
 
        rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
 
@@ -457,7 +475,10 @@ usbhs_fifo_read_end:
                usbhs_pipe_number(pipe),
                pkt->length, pkt->actual, *is_done, pkt->zero);
 
-       return 0;
+usbhs_fifo_read_busy:
+       usbhsf_fifo_unselect(pipe, fifo);
+
+       return ret;
 }
 
 struct usbhs_pkt_handle usbhs_fifo_pop_handler = {
@@ -551,11 +572,14 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv,
 void usbhs_fifo_init(struct usbhs_priv *priv)
 {
        struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+       struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv);
 
        mod->irq_empty          = usbhsf_irq_empty;
        mod->irq_ready          = usbhsf_irq_ready;
        mod->irq_bempsts        = 0;
        mod->irq_brdysts        = 0;
+
+       cfifo->pipe     = NULL;
 }
 
 void usbhs_fifo_quit(struct usbhs_priv *priv)
index 04d000a..4292f8c 100644 (file)
@@ -23,6 +23,8 @@ struct usbhs_fifo {
        u32 port;       /* xFIFO */
        u32 sel;        /* xFIFOSEL */
        u32 ctr;        /* xFIFOCTR */
+
+       struct usbhs_pipe       *pipe;
 };
 
 struct usbhs_fifo_info {
index 56137d5..c050587 100644 (file)
@@ -562,6 +562,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
                        info->bufnmb_last++;
 
                usbhsp_flags_init(pipe);
+               pipe->fifo = NULL;
                pipe->mod_private = NULL;
                INIT_LIST_HEAD(&pipe->list);
 
@@ -620,6 +621,18 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
        return pipe;
 }
 
+void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo)
+{
+       if (pipe->fifo)
+               pipe->fifo->pipe = NULL;
+
+       pipe->fifo = fifo;
+
+       if (fifo)
+               fifo->pipe = pipe;
+}
+
+
 /*
  *             dcp control
  */
index 20e3cf4..484adbe 100644 (file)
@@ -27,6 +27,7 @@ struct usbhs_pipe {
        u32 pipe_type;  /* USB_ENDPOINT_XFER_xxx */
 
        struct usbhs_priv *priv;
+       struct usbhs_fifo *fifo;
        struct list_head list;
 
        u32 flags;
@@ -88,10 +89,13 @@ int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
 void usbhs_pipe_enable(struct usbhs_pipe *pipe);
 void usbhs_pipe_disable(struct usbhs_pipe *pipe);
 void usbhs_pipe_stall(struct usbhs_pipe *pipe);
+void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
 
 #define usbhs_pipe_to_priv(p)  ((p)->priv)
 #define usbhs_pipe_number(p)   (int)((p) - (p)->priv->pipe_info.pipe)
 #define usbhs_pipe_is_dcp(p)   ((p)->priv->pipe_info.pipe == (p))
+#define usbhs_pipe_to_fifo(p)  ((p)->fifo)
+#define usbhs_pipe_is_busy(p)  usbhs_pipe_to_fifo(p)
 
 /*
  * dcp control