diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2011-12-08 18:28:24 -0800 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2011-12-13 13:06:25 +0200 |
commit | e5679d07a6ca5512070fb5e65dcc66eeb5087d0d (patch) | |
tree | 26a0a5eb4bdad75e8661a67e97f90ff114d6798f /drivers/usb | |
parent | e4c57ded48d9bad95a4d7254e75a81f7abcffef9 (diff) | |
download | linux-e5679d07a6ca5512070fb5e65dcc66eeb5087d0d.tar.bz2 |
usb: renesas_usbhs: add usbhs_pipe_attach() method
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/renesas_usbhs/mod_host.c | 292 |
1 files changed, 168 insertions, 124 deletions
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index 5b6ae2a8d303..c7f9be9f5c17 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -45,32 +45,31 @@ * * +--------+ 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); @@ -841,6 +886,20 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd, } /* + * 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 */ if (usb_pipecontrol(urb->pipe)) @@ -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) |