usb: renesas_usbhs: care pipe sequence
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Fri, 9 Dec 2011 02:28:54 +0000 (18:28 -0800)
committerFelipe Balbi <balbi@ti.com>
Tue, 13 Dec 2011 11:06:26 +0000 (13:06 +0200)
driver has to re-use the limited pipe for each device/endpoint
when it is USB host hub mode, since number of pipe has limitation.

Then, each pipe should care own pipe sequence for next packet.
This patch adds sequence control.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/renesas_usbhs/fifo.h
drivers/usb/renesas_usbhs/mod_gadget.c
drivers/usb/renesas_usbhs/mod_host.c
drivers/usb/renesas_usbhs/pipe.c

index ffdf5d15085ebbe845dbd54474c8a0e5b9464061..b51fcd80d244b57ca75a540276ff45b1c1c3c8c0 100644 (file)
@@ -56,7 +56,7 @@ static struct usbhs_pkt_handle usbhsf_null_handler = {
 void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
                    void (*done)(struct usbhs_priv *priv,
                                 struct usbhs_pkt *pkt),
-                   void *buf, int len, int zero)
+                   void *buf, int len, int zero, int sequence)
 {
        struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
        struct device *dev = usbhs_priv_to_dev(priv);
@@ -90,6 +90,7 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
        pkt->zero       = zero;
        pkt->actual     = 0;
        pkt->done       = done;
+       pkt->sequence   = sequence;
 
        usbhs_unlock(priv, flags);
        /********************  spin unlock ******************/
@@ -481,6 +482,9 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
        int i, ret, len;
        int is_short;
 
+       usbhs_pipe_data_sequence(pipe, pkt->sequence);
+       pkt->sequence = -1; /* -1 sequence will be ignored */
+
        ret = usbhsf_fifo_select(pipe, fifo, 1);
        if (ret < 0)
                return 0;
@@ -584,6 +588,8 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
        /*
         * pipe enable to prepare packet receive
         */
+       usbhs_pipe_data_sequence(pipe, pkt->sequence);
+       pkt->sequence = -1; /* -1 sequence will be ignored */
 
        usbhs_pipe_enable(pipe);
        usbhsf_rx_irq_ctrl(pipe, 1);
@@ -641,6 +647,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
         * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
         */
        if (0 == rcv_len) {
+               pkt->zero = 1;
                usbhsf_fifo_clear(pipe, fifo);
                goto usbhs_fifo_read_end;
        }
index 32a7b246b28db860719a7c31967993a938fdfbbd..f68609c0f489b6df2c662cbc86e2adf85c193c98 100644 (file)
@@ -59,6 +59,7 @@ struct usbhs_pkt {
        int trans;
        int actual;
        int zero;
+       int sequence;
 };
 
 struct usbhs_pkt_handle {
@@ -95,7 +96,7 @@ void usbhs_pkt_init(struct usbhs_pkt *pkt);
 void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
                    void (*done)(struct usbhs_priv *priv,
                                 struct usbhs_pkt *pkt),
-                   void *buf, int len, int zero);
+                   void *buf, int len, int zero, int sequence);
 struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt);
 void usbhs_pkt_start(struct usbhs_pipe *pipe);
 
index cf3141c330a327b27b9c59a67c28e14d7d1c3272..db2a1c6a0866f79c2ef8c1fcf62709c371715f9d 100644 (file)
@@ -154,7 +154,7 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep,
        req->actual = 0;
        req->status = -EINPROGRESS;
        usbhs_pkt_push(pipe, pkt, usbhsg_queue_done,
-                      req->buf, req->length, req->zero);
+                      req->buf, req->length, req->zero, -1);
        usbhs_pkt_start(pipe);
 
        dev_dbg(dev, "pipe %d : queue push (%d)\n",
index c7f9be9f5c17317273cf23e53c600383343b470e..72ee8e55e717e5e8c30f221862b53a05f46ccdf2 100644 (file)
@@ -193,6 +193,48 @@ static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
 /*
  *             pipe control
  */
+static void usbhsh_endpoint_sequence_save(struct usbhsh_hpriv *hpriv,
+                                         struct urb *urb,
+                                         struct usbhs_pkt *pkt)
+{
+       int len = urb->actual_length;
+       int maxp = usb_endpoint_maxp(&urb->ep->desc);
+       int t = 0;
+
+       /* DCP is out of sequence control */
+       if (usb_pipecontrol(urb->pipe))
+               return;
+
+       /*
+        * renesas_usbhs pipe has a limitation in a number.
+        * So, driver should re-use the limited pipe for each device/endpoint.
+        * DATA0/1 sequence should be saved for it.
+        * see [image of mod_host]
+        *     [HARDWARE LIMITATION]
+        */
+
+       /*
+        * next sequence depends on actual_length
+        *
+        * ex) actual_length = 1147, maxp = 512
+        * data0 : 512
+        * data1 : 512
+        * data0 : 123
+        * data1 is the next sequence
+        */
+       t = len / maxp;
+       if (len % maxp)
+               t++;
+       if (pkt->zero)
+               t++;
+       t %= 2;
+
+       if (t)
+               usb_dotoggle(urb->dev,
+                            usb_pipeendpoint(urb->pipe),
+                            usb_pipeout(urb->pipe));
+}
+
 static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
                                               struct urb *urb);
 
@@ -247,15 +289,6 @@ static int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv,
                usbhsh_uep_to_pipe(uep)         = pipe;
                usbhsh_pipe_to_uep(pipe)        = uep;
 
-               if (!usb_gettoggle(urb->dev,
-                                  usb_pipeendpoint(urb->pipe),
-                                  usb_pipeout(urb->pipe))) {
-                       usbhs_pipe_sequence_data0(pipe);
-                       usb_settoggle(urb->dev,
-                                     usb_pipeendpoint(urb->pipe),
-                                     usb_pipeout(urb->pipe), 1);
-               }
-
                usbhs_pipe_config_update(pipe,
                                         usbhsh_device_number(hpriv, udev),
                                         usb_endpoint_num(desc),
@@ -598,10 +631,11 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
        urb->actual_length = pkt->actual;
        usbhsh_ureq_free(hpriv, ureq);
 
+       usbhsh_endpoint_sequence_save(hpriv, urb, pkt);
+       usbhsh_pipe_detach(hpriv, uep);
+
        usb_hcd_unlink_urb_from_ep(hcd, urb);
        usb_hcd_giveback_urb(hcd, urb, 0);
-
-       usbhsh_pipe_detach(hpriv, uep);
 }
 
 static int usbhsh_queue_push(struct usb_hcd *hcd,
@@ -614,7 +648,7 @@ static int usbhsh_queue_push(struct usb_hcd *hcd,
        struct device *dev = usbhsh_hcd_to_dev(hcd);
        struct usbhsh_request *ureq;
        void *buf;
-       int len;
+       int len, sequence;
 
        if (usb_pipeisoc(urb->pipe)) {
                dev_err(dev, "pipe iso is not supported now\n");
@@ -636,9 +670,15 @@ static int usbhsh_queue_push(struct usb_hcd *hcd,
        buf = (void *)(urb->transfer_buffer + urb->actual_length);
        len = urb->transfer_buffer_length - urb->actual_length;
 
+       sequence = usb_gettoggle(urb->dev,
+                                usb_pipeendpoint(urb->pipe),
+                                usb_pipeout(urb->pipe));
+
        dev_dbg(dev, "%s\n", __func__);
        usbhs_pkt_push(pipe, &ureq->pkt, usbhsh_queue_done,
-                      buf, len, (urb->transfer_flags & URB_ZERO_PACKET));
+                      buf, len, (urb->transfer_flags & URB_ZERO_PACKET),
+                      sequence);
+
        usbhs_pkt_start(pipe);
 
        return 0;
@@ -741,7 +781,8 @@ static int usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv,
                       usbhsh_data_stage_packet_done,
                       urb->transfer_buffer,
                       urb->transfer_buffer_length,
-                      (urb->transfer_flags & URB_ZERO_PACKET));
+                      (urb->transfer_flags & URB_ZERO_PACKET),
+                      -1);
 
        return 0;
 }
@@ -770,7 +811,7 @@ static int usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv,
                       usbhsh_queue_done,
                       NULL,
                       urb->transfer_buffer_length,
-                      0);
+                      0, -1);
 
        return 0;
 }
index c36ad4c3a25988fe91ec1f192fadbc6601606e72..c2559e80d41fa384c518e325aad682b3a801bd65 100644 (file)
@@ -478,10 +478,27 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe)
        return usbhsp_flags_has(pipe, IS_DIR_HOST);
 }
 
-void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data)
+void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
 {
        u16 mask = (SQCLR | SQSET);
-       u16 val = (data) ? SQSET : SQCLR;
+       u16 val;
+
+       /*
+        * sequence
+        *  0  : data0
+        *  1  : data1
+        *  -1 : no change
+        */
+       switch (sequence) {
+       case 0:
+               val = SQCLR;
+               break;
+       case 1:
+               val = SQSET;
+               break;
+       default:
+               return;
+       }
 
        usbhsp_pipectrl_set(pipe, mask, val);
 }