usb: renesas_usbhs: add usbhs_pipe_attach() method
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Fri, 9 Dec 2011 02:28:24 +0000 (18:28 -0800)
committerFelipe Balbi <balbi@ti.com>
Tue, 13 Dec 2011 11:06:25 +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.
This patch adds usbhsh_pipe_attach/detach() functions for it.

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

index 5b6ae2a..c7f9be9 100644 (file)
  *
  * +--------+                                  pipes are reused for each uep.
  * | udev 1 |-+- [uep 0 (dcp) ] --+            pipe will be switched when
- * +--------+ |                          |             target device was changed
+ * +--------+ |                          |             other device requested
  *           +- [uep 1 (bulk)] --|---+            +--------------+
  *           |                   +--------------> | pipe0 (dcp)  |
- *           +- [uep 2 (bulk)] --|---|---+        +--------------+
- *                               |   |   |        | pipe1 (isoc) |
- * +--------+                    |   |   |        +--------------+
- * | udev 2 |-+- [uep 0 (dcp) ]        --+   +-- |------> | pipe2 (bulk) |
- * +--------+ |                          |   |   |        +--------------+
- *           +- [uep 1 (int) ] --|-+ |   +------> | pipe3 (bulk) |
- *                               | | |   |        +--------------+
- * +--------+                    | +-|---|------> | pipe4 (int)  |
- * | udev 3 |-+- [uep 0 (dcp) ]        --+   |   |        +--------------+
- * +--------+ |                              |   |        | ....         |
- *           +- [uep 1 (bulk)] ------+   |        | ....         |
+ *           +- [uep 2 (bulk)] -@    |            +--------------+
+ *                                   |            | pipe1 (isoc) |
+ * +--------+                        |            +--------------+
+ * | udev 2 |-+- [uep 0 (dcp) ]        -@    +----------> | pipe2 (bulk) |
+ * +--------+ |                                           +--------------+
+ *           +- [uep 1 (int) ] ----+     +------> | pipe3 (bulk) |
+ *                                 |     |        +--------------+
+ * +--------+                      +-----|------> | pipe4 (int)  |
+ * | udev 3 |-+- [uep 0 (dcp) ]        -@        |        +--------------+
+ * +--------+ |                                  |        | ....         |
+ *           +- [uep 1 (bulk)] -@        |        | ....         |
  *           |                           |
  *           +- [uep 2 (bulk)]-----------+
+ *
+ * @ : uep requested free pipe, but all have been used.
+ *     now it is waiting for free pipe
  */
 
 
 /*
  *             struct
  */
-struct usbhsh_pipe_info {
-       unsigned int            usr_cnt; /* see usbhsh_endpoint_alloc() */
-};
-
 struct usbhsh_request {
        struct urb              *urb;
        struct usbhs_pkt        pkt;
@@ -82,12 +81,10 @@ struct usbhsh_device {
 };
 
 struct usbhsh_ep {
-       struct usbhs_pipe       *pipe;
+       struct usbhs_pipe       *pipe;   /* attached pipe */
        struct usbhsh_device    *udev;   /* attached udev */
        struct usb_host_endpoint *ep;
        struct list_head        ep_list; /* list to usbhsh_device */
-
-       int maxp;
 };
 
 #define USBHSH_DEVICE_MAX      10 /* see DEVADDn / DCPMAXP / PIPEMAXP */
@@ -98,9 +95,6 @@ struct usbhsh_hpriv {
 
        struct usbhsh_device    udev[USBHSH_DEVICE_MAX];
 
-       struct usbhsh_pipe_info *pipe_info;
-       int                      pipe_size;
-
        u32     port_stat;      /* USB_PORT_STAT_xxx */
 
        struct completion       setup_ack_done;
@@ -115,17 +109,6 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host";
 #define usbhsh_priv_to_hpriv(priv) \
        container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod)
 
-#define __usbhsh_for_each_hpipe(start, pos, h, i)      \
-       for (i = start, pos = (h)->hpipe + i;           \
-            i < (h)->hpipe_size;                       \
-            i++, pos = (h)->hpipe + i)
-
-#define usbhsh_for_each_hpipe(pos, hpriv, i)   \
-       __usbhsh_for_each_hpipe(1, pos, hpriv, i)
-
-#define usbhsh_for_each_hpipe_with_dcp(pos, hpriv, i)  \
-       __usbhsh_for_each_hpipe(0, pos, hpriv, i)
-
 #define __usbhsh_for_each_udev(start, pos, h, i)       \
        for (i = start, pos = (h)->udev + i;            \
             i < USBHSH_DEVICE_MAX;                     \
@@ -158,7 +141,7 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host";
 #define usbhsh_udev_to_usbv(h) ((h)->usbv)
 #define usbhsh_udev_is_used(h) usbhsh_udev_to_usbv(h)
 
-#define usbhsh_pipe_info(p)    ((p)->mod_private)
+#define usbhsh_pipe_to_uep(p)  ((p)->mod_private)
 
 #define usbhsh_device_parent(d)                (usbhsh_usbv_to_udev((d)->usbv->parent))
 #define usbhsh_device_hubport(d)       ((d)->usbv->portnum)
@@ -208,106 +191,166 @@ static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
 }
 
 /*
- *             end-point control
+ *             pipe control
  */
 static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
                                               struct urb *urb);
-static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
-                                 struct urb *urb,
-                                 gfp_t mem_flags)
+
+static int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv,
+                             struct urb *urb)
 {
        struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+       struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
        struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
-       struct usb_host_endpoint *ep = urb->ep;
-       struct usbhsh_ep *uep;
-       struct usbhsh_pipe_info *info;
-       struct usbhs_pipe *best_pipe = NULL;
+       struct usbhs_pipe *pipe;
+       struct usb_endpoint_descriptor *desc = &urb->ep->desc;
        struct device *dev = usbhs_priv_to_dev(priv);
-       struct usb_endpoint_descriptor *desc = &ep->desc;
        unsigned long flags;
-
-       uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
-       if (!uep) {
-               dev_err(dev, "usbhsh_ep alloc fail\n");
-               return -ENOMEM;
-       }
+       int dir_in_req = !!usb_pipein(urb->pipe);
+       int is_dcp = usb_endpoint_xfer_control(desc);
+       int i, dir_in;
+       int ret = -EBUSY;
 
        /********************  spin lock ********************/
        usbhs_lock(priv, flags);
 
-       /*
-        * find best pipe for endpoint
-        * see
-        *      HARDWARE LIMITATION
-        */
-       if (usb_endpoint_xfer_control(desc)) {
-               /* best pipe is DCP */
-               best_pipe = usbhsh_hpriv_to_dcp(hpriv);
-       } else {
-               struct usbhs_pipe *pipe;
-               unsigned int min_usr = ~0;
-               int dir_in_req = !!usb_pipein(urb->pipe);
-               int i, dir_in;
+       if (unlikely(usbhsh_uep_to_pipe(uep))) {
+               dev_err(dev, "uep already has pipe\n");
+               goto usbhsh_pipe_attach_done;
+       }
 
-               usbhs_for_each_pipe(pipe, priv, i) {
-                       if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
-                               continue;
+       usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+
+               /* check pipe type */
+               if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
+                       continue;
 
+               /* check pipe direction if normal pipe */
+               if (!is_dcp) {
                        dir_in = !!usbhs_pipe_is_dir_in(pipe);
                        if (0 != (dir_in - dir_in_req))
                                continue;
+               }
 
-                       info = usbhsh_pipe_info(pipe);
-                       if (min_usr > info->usr_cnt) {
-                               min_usr         = info->usr_cnt;
-                               best_pipe       = pipe;
-                       }
+               /* check pipe is free */
+               if (usbhsh_pipe_to_uep(pipe))
+                       continue;
+
+               /*
+                * attach pipe to uep
+                *
+                * usbhs_pipe_config_update() should be called after
+                * usbhs_set_device_config()
+                * see
+                *  DCPMAXP/PIPEMAXP
+                */
+               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),
+                                        usb_endpoint_maxp(desc));
+
+               dev_dbg(dev, "%s [%d-%d(%s:%s)]\n", __func__,
+                       usbhsh_device_number(hpriv, udev),
+                       usb_endpoint_num(desc),
+                       usbhs_pipe_name(pipe),
+                       dir_in_req ? "in" : "out");
+
+               ret = 0;
+               break;
        }
 
-       if (best_pipe) {
-               /* update pipe user count */
-               info = usbhsh_pipe_info(best_pipe);
-               info->usr_cnt++;
+usbhsh_pipe_attach_done:
+       usbhs_unlock(priv, flags);
+       /********************  spin unlock ******************/
 
-               /* init this endpoint, and attach it to udev */
-               INIT_LIST_HEAD(&uep->ep_list);
-               list_add_tail(&uep->ep_list, &udev->ep_list_head);
+       return ret;
+}
+
+static void usbhsh_pipe_detach(struct usbhsh_hpriv *hpriv,
+                              struct usbhsh_ep *uep)
+{
+       struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+       struct usbhs_pipe *pipe;
+       struct device *dev = usbhs_priv_to_dev(priv);
+       unsigned long flags;
+
+       /********************  spin lock ********************/
+       usbhs_lock(priv, flags);
+
+       pipe = usbhsh_uep_to_pipe(uep);
+
+       if (unlikely(!pipe)) {
+               dev_err(dev, "uep doens't have pipe\n");
+       } else {
+               struct usb_host_endpoint *ep = usbhsh_uep_to_ep(uep);
+               struct usbhsh_device *udev = usbhsh_uep_to_udev(uep);
+
+               /* detach pipe from uep */
+               usbhsh_uep_to_pipe(uep)         = NULL;
+               usbhsh_pipe_to_uep(pipe)        = NULL;
+
+               dev_dbg(dev, "%s [%d-%d(%s)]\n", __func__,
+                       usbhsh_device_number(hpriv, udev),
+                       usb_endpoint_num(&ep->desc),
+                       usbhs_pipe_name(pipe));
        }
 
        usbhs_unlock(priv, flags);
        /********************  spin unlock ******************/
+}
 
-       if (unlikely(!best_pipe)) {
-               dev_err(dev, "couldn't find best pipe\n");
-               kfree(uep);
-               return -EIO;
+/*
+ *             endpoint control
+ */
+static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
+                                 struct urb *urb,
+                                 gfp_t mem_flags)
+{
+       struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+       struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
+       struct usb_host_endpoint *ep = urb->ep;
+       struct usbhsh_ep *uep;
+       struct device *dev = usbhs_priv_to_dev(priv);
+       struct usb_endpoint_descriptor *desc = &ep->desc;
+       unsigned long flags;
+
+       uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
+       if (!uep) {
+               dev_err(dev, "usbhsh_ep alloc fail\n");
+               return -ENOMEM;
        }
 
+       /********************  spin lock ********************/
+       usbhs_lock(priv, flags);
+
        /*
-        * init uep
+        * init endpoint
         */
-       uep->pipe       = best_pipe;
-       uep->maxp       = usb_endpoint_maxp(desc);
+       INIT_LIST_HEAD(&uep->ep_list);
+       list_add_tail(&uep->ep_list, &udev->ep_list_head);
+
        usbhsh_uep_to_udev(uep) = udev;
        usbhsh_uep_to_ep(uep)   = ep;
        usbhsh_ep_to_uep(ep)    = uep;
 
-       /*
-        * usbhs_pipe_config_update() should be called after
-        * usbhs_set_device_config()
-        * see
-        *  DCPMAXP/PIPEMAXP
-        */
-       usbhs_pipe_sequence_data0(uep->pipe);
-       usbhs_pipe_config_update(uep->pipe,
-                                usbhsh_device_number(hpriv, udev),
-                                usb_endpoint_num(desc),
-                                uep->maxp);
+       usbhs_unlock(priv, flags);
+       /********************  spin unlock ******************/
 
-       dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
+       dev_dbg(dev, "%s [%d-%d]\n", __func__,
                usbhsh_device_number(hpriv, udev),
-               usbhs_pipe_name(uep->pipe), uep);
+               usb_endpoint_num(desc));
 
        return 0;
 }
@@ -318,27 +361,24 @@ static void usbhsh_endpoint_detach(struct usbhsh_hpriv *hpriv,
        struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
        struct device *dev = usbhs_priv_to_dev(priv);
        struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
-       struct usbhsh_pipe_info *info;
        unsigned long flags;
 
        if (!uep)
                return;
 
-       dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
+       dev_dbg(dev, "%s [%d-%d]\n", __func__,
                usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
-               usbhs_pipe_name(uep->pipe), uep);
+               usb_endpoint_num(&ep->desc));
+
+       if (usbhsh_uep_to_pipe(uep))
+               usbhsh_pipe_detach(hpriv, uep);
 
        /********************  spin lock ********************/
        usbhs_lock(priv, flags);
 
-       info = usbhsh_pipe_info(uep->pipe);
-       info->usr_cnt--;
-
        /* remove this endpoint from udev */
        list_del_init(&uep->ep_list);
 
-       uep->pipe       = NULL;
-       uep->maxp       = 0;
        usbhsh_uep_to_udev(uep) = NULL;
        usbhsh_uep_to_ep(uep)   = NULL;
        usbhsh_ep_to_uep(ep)    = NULL;
@@ -545,6 +585,7 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
        struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
        struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
        struct urb *urb = ureq->urb;
+       struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
        struct device *dev = usbhs_priv_to_dev(priv);
 
        dev_dbg(dev, "%s\n", __func__);
@@ -559,6 +600,8 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
 
        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,
@@ -811,7 +854,7 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
        struct usb_host_endpoint *ep = urb->ep;
        struct usbhsh_device *new_udev = NULL;
        int is_dir_in = usb_pipein(urb->pipe);
-
+       int i;
        int ret;
 
        dev_dbg(dev, "%s (%s)\n", __func__, is_dir_in ? "in" : "out");
@@ -822,6 +865,7 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
 
        /*
         * attach udev if needed
+        * see [image of mod_host]
         */
        if (!usbhsh_device_get(hpriv, urb)) {
                new_udev = usbhsh_device_attach(hpriv, urb);
@@ -833,6 +877,7 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
 
        /*
         * attach endpoint if needed
+        * see [image of mod_host]
         */
        if (!usbhsh_ep_to_uep(ep)) {
                ret = usbhsh_endpoint_attach(hpriv, urb, mem_flags);
@@ -840,6 +885,20 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
                        goto usbhsh_urb_enqueue_error_free_device;
        }
 
+       /*
+        * attach pipe to endpoint
+        * see [image of mod_host]
+        */
+       for (i = 0; i < 1024; i++) {
+               ret = usbhsh_pipe_attach(hpriv, urb);
+               if (ret < 0)
+                       msleep(100);
+               else
+                       break;
+       }
+       if (ret < 0)
+               goto usbhsh_urb_enqueue_error_free_endpoint;
+
        /*
         * push packet
         */
@@ -850,6 +909,8 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
 
        return ret;
 
+usbhsh_urb_enqueue_error_free_endpoint:
+       usbhsh_endpoint_detach(hpriv, ep);
 usbhsh_urb_enqueue_error_free_device:
        if (new_udev)
                usbhsh_device_detach(hpriv, new_udev);
@@ -1192,7 +1253,6 @@ static int usbhsh_irq_setup_err(struct usbhs_priv *priv,
 static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
 {
        struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
-       struct usbhsh_pipe_info *pipe_info = hpriv->pipe_info;
        struct usbhs_pipe *pipe;
        u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
        int pipe_size = usbhs_get_dparam(priv, pipe_size);
@@ -1201,7 +1261,6 @@ static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
        /* init all pipe */
        old_type = USB_ENDPOINT_XFER_CONTROL;
        for (i = 0; i < pipe_size; i++) {
-               pipe_info[i].usr_cnt    = 0;
 
                /*
                 * data "output" will be finished as soon as possible,
@@ -1235,7 +1294,7 @@ static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
                                                 dir_in);
                }
 
-               pipe->mod_private = pipe_info + i;
+               pipe->mod_private = NULL;
        }
 }
 
@@ -1312,10 +1371,8 @@ int usbhs_mod_host_probe(struct usbhs_priv *priv)
 {
        struct usbhsh_hpriv *hpriv;
        struct usb_hcd *hcd;
-       struct usbhsh_pipe_info *pipe_info;
        struct usbhsh_device *udev;
        struct device *dev = usbhs_priv_to_dev(priv);
-       int pipe_size = usbhs_get_dparam(priv, pipe_size);
        int i;
 
        /* initialize hcd */
@@ -1325,12 +1382,6 @@ int usbhs_mod_host_probe(struct usbhs_priv *priv)
                return -ENOMEM;
        }
 
-       pipe_info = kzalloc(sizeof(*pipe_info) * pipe_size, GFP_KERNEL);
-       if (!pipe_info) {
-               dev_err(dev, "Could not allocate pipe_info\n");
-               goto usbhs_mod_host_probe_err;
-       }
-
        /*
         * CAUTION
         *
@@ -1350,8 +1401,6 @@ int usbhs_mod_host_probe(struct usbhs_priv *priv)
        hpriv->mod.name         = "host";
        hpriv->mod.start        = usbhsh_start;
        hpriv->mod.stop         = usbhsh_stop;
-       hpriv->pipe_info        = pipe_info;
-       hpriv->pipe_size        = pipe_size;
        usbhsh_port_stat_init(hpriv);
 
        /* init all device */
@@ -1363,11 +1412,6 @@ int usbhs_mod_host_probe(struct usbhs_priv *priv)
        dev_info(dev, "host probed\n");
 
        return 0;
-
-usbhs_mod_host_probe_err:
-       usb_put_hcd(hcd);
-
-       return -ENOMEM;
 }
 
 int usbhs_mod_host_remove(struct usbhs_priv *priv)