Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5679d07 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Felipe Balbi
Browse files

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent e4c57ded
Loading
Loading
Loading
Loading
+168 −124
Original line number Diff line number Diff line
@@ -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_with_dcp(pipe, priv, i) {

		usbhs_for_each_pipe(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;
		}

		/* check pipe is free */
		if (usbhsh_pipe_to_uep(pipe))
			continue;

			info = usbhsh_pipe_info(pipe);
			if (min_usr > info->usr_cnt) {
				min_usr		= info->usr_cnt;
				best_pipe	= pipe;
		/*
		 * 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;
	}

usbhsh_pipe_attach_done:
	usbhs_unlock(priv, flags);
	/********************  spin unlock ******************/

	return ret;
}

	if (best_pipe) {
		/* update pipe user count */
		info = usbhsh_pipe_info(best_pipe);
		info->usr_cnt++;
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;

		/* init this endpoint, and attach it to udev */
		INIT_LIST_HEAD(&uep->ep_list);
		list_add_tail(&uep->ep_list, &udev->ep_list_head);
	/********************  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)