162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Renesas USB driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 Renesas Solutions Corp.
662306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/list.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/usb.h>
1462306a36Sopenharmony_ci#include <linux/usb/hcd.h>
1562306a36Sopenharmony_ci#include "common.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci *** HARDWARE LIMITATION ***
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * 1) renesas_usbhs has a limited number of controllable devices.
2162306a36Sopenharmony_ci *    it can control only 9 devices in generally.
2262306a36Sopenharmony_ci *	see DEVADDn / DCPMAXP / PIPEMAXP.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * 2) renesas_usbhs pipe number is limited.
2562306a36Sopenharmony_ci *    the pipe will be re-used for each devices.
2662306a36Sopenharmony_ci *    so, software should control DATA0/1 sequence of each devices.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci *		image of mod_host
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * +--------+
3462306a36Sopenharmony_ci * | udev 0 | --> it is used when set address
3562306a36Sopenharmony_ci * +--------+
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * +--------+					pipes are reused for each uep.
3862306a36Sopenharmony_ci * | udev 1 |-+- [uep 0 (dcp) ] --+		pipe will be switched when
3962306a36Sopenharmony_ci * +--------+ |			  |		other device requested
4062306a36Sopenharmony_ci *	      +- [uep 1 (bulk)]	--|---+		   +--------------+
4162306a36Sopenharmony_ci *	      |			  +--------------> | pipe0 (dcp)  |
4262306a36Sopenharmony_ci *	      +- [uep 2 (bulk)]	-@    |		   +--------------+
4362306a36Sopenharmony_ci *				      |		   | pipe1 (isoc) |
4462306a36Sopenharmony_ci * +--------+			      |		   +--------------+
4562306a36Sopenharmony_ci * | udev 2 |-+- [uep 0 (dcp) ]	-@    +----------> | pipe2 (bulk) |
4662306a36Sopenharmony_ci * +--------+ |					   +--------------+
4762306a36Sopenharmony_ci *	      +- [uep 1 (int) ]	----+	  +------> | pipe3 (bulk) |
4862306a36Sopenharmony_ci *				    |	  |	   +--------------+
4962306a36Sopenharmony_ci * +--------+			    +-----|------> | pipe4 (int)  |
5062306a36Sopenharmony_ci * | udev 3 |-+- [uep 0 (dcp) ]	-@	  |	   +--------------+
5162306a36Sopenharmony_ci * +--------+ |				  |	   | ....	  |
5262306a36Sopenharmony_ci *	      +- [uep 1 (bulk)]	-@	  |	   | ....	  |
5362306a36Sopenharmony_ci *	      |				  |
5462306a36Sopenharmony_ci *	      +- [uep 2 (bulk)]-----------+
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * @ :	uep requested free pipe, but all have been used.
5762306a36Sopenharmony_ci *	now it is waiting for free pipe
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci *		struct
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistruct usbhsh_request {
6562306a36Sopenharmony_ci	struct urb		*urb;
6662306a36Sopenharmony_ci	struct usbhs_pkt	pkt;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistruct usbhsh_device {
7062306a36Sopenharmony_ci	struct usb_device	*usbv;
7162306a36Sopenharmony_ci	struct list_head	ep_list_head; /* list of usbhsh_ep */
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistruct usbhsh_ep {
7562306a36Sopenharmony_ci	struct usbhs_pipe	*pipe;   /* attached pipe */
7662306a36Sopenharmony_ci	struct usbhsh_device	*udev;   /* attached udev */
7762306a36Sopenharmony_ci	struct usb_host_endpoint *ep;
7862306a36Sopenharmony_ci	struct list_head	ep_list; /* list to usbhsh_device */
7962306a36Sopenharmony_ci	unsigned int		counter; /* pipe attach counter */
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define USBHSH_DEVICE_MAX	10 /* see DEVADDn / DCPMAXP / PIPEMAXP */
8362306a36Sopenharmony_ci#define USBHSH_PORT_MAX		 7 /* see DEVADDn :: HUBPORT */
8462306a36Sopenharmony_cistruct usbhsh_hpriv {
8562306a36Sopenharmony_ci	struct usbhs_mod	mod;
8662306a36Sopenharmony_ci	struct usbhs_pipe	*dcp;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	struct usbhsh_device	udev[USBHSH_DEVICE_MAX];
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	u32	port_stat;	/* USB_PORT_STAT_xxx */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	struct completion	setup_ack_done;
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const char usbhsh_hcd_name[] = "renesas_usbhs host";
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci *		macro
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_ci#define usbhsh_priv_to_hpriv(priv) \
10262306a36Sopenharmony_ci	container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod)
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define __usbhsh_for_each_udev(start, pos, h, i)	\
10562306a36Sopenharmony_ci	for ((i) = start;						\
10662306a36Sopenharmony_ci	     ((i) < USBHSH_DEVICE_MAX) && ((pos) = (h)->udev + (i));	\
10762306a36Sopenharmony_ci	     (i)++)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define usbhsh_for_each_udev(pos, hpriv, i)	\
11062306a36Sopenharmony_ci	__usbhsh_for_each_udev(1, pos, hpriv, i)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i)	\
11362306a36Sopenharmony_ci	__usbhsh_for_each_udev(0, pos, hpriv, i)
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define usbhsh_hcd_to_hpriv(h)	(struct usbhsh_hpriv *)((h)->hcd_priv)
11662306a36Sopenharmony_ci#define usbhsh_hcd_to_dev(h)	((h)->self.controller)
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define usbhsh_hpriv_to_priv(h)	((h)->mod.priv)
11962306a36Sopenharmony_ci#define usbhsh_hpriv_to_dcp(h)	((h)->dcp)
12062306a36Sopenharmony_ci#define usbhsh_hpriv_to_hcd(h)	\
12162306a36Sopenharmony_ci	container_of((void *)h, struct usb_hcd, hcd_priv)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define usbhsh_ep_to_uep(u)	((u)->hcpriv)
12462306a36Sopenharmony_ci#define usbhsh_uep_to_pipe(u)	((u)->pipe)
12562306a36Sopenharmony_ci#define usbhsh_uep_to_udev(u)	((u)->udev)
12662306a36Sopenharmony_ci#define usbhsh_uep_to_ep(u)	((u)->ep)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#define usbhsh_urb_to_ureq(u)	((u)->hcpriv)
12962306a36Sopenharmony_ci#define usbhsh_urb_to_usbv(u)	((u)->dev)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define usbhsh_usbv_to_udev(d)	dev_get_drvdata(&(d)->dev)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define usbhsh_udev_to_usbv(h)	((h)->usbv)
13462306a36Sopenharmony_ci#define usbhsh_udev_is_used(h)	usbhsh_udev_to_usbv(h)
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define usbhsh_pipe_to_uep(p)	((p)->mod_private)
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define usbhsh_device_parent(d)		(usbhsh_usbv_to_udev((d)->usbv->parent))
13962306a36Sopenharmony_ci#define usbhsh_device_hubport(d)	((d)->usbv->portnum)
14062306a36Sopenharmony_ci#define usbhsh_device_number(h, d)	((int)((d) - (h)->udev))
14162306a36Sopenharmony_ci#define usbhsh_device_nth(h, d)		((h)->udev + d)
14262306a36Sopenharmony_ci#define usbhsh_device0(h)		usbhsh_device_nth(h, 0)
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#define usbhsh_port_stat_init(h)	((h)->port_stat = 0)
14562306a36Sopenharmony_ci#define usbhsh_port_stat_set(h, s)	((h)->port_stat |= (s))
14662306a36Sopenharmony_ci#define usbhsh_port_stat_clear(h, s)	((h)->port_stat &= ~(s))
14762306a36Sopenharmony_ci#define usbhsh_port_stat_get(h)		((h)->port_stat)
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci#define usbhsh_pkt_to_ureq(p)	\
15062306a36Sopenharmony_ci	container_of((void *)p, struct usbhsh_request, pkt)
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/*
15362306a36Sopenharmony_ci *		req alloc/free
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv,
15662306a36Sopenharmony_ci					       struct urb *urb,
15762306a36Sopenharmony_ci					       gfp_t mem_flags)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct usbhsh_request *ureq;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags);
16262306a36Sopenharmony_ci	if (!ureq)
16362306a36Sopenharmony_ci		return NULL;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	usbhs_pkt_init(&ureq->pkt);
16662306a36Sopenharmony_ci	ureq->urb = urb;
16762306a36Sopenharmony_ci	usbhsh_urb_to_ureq(urb) = ureq;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return ureq;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
17362306a36Sopenharmony_ci			    struct usbhsh_request *ureq)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	usbhsh_urb_to_ureq(ureq->urb) = NULL;
17662306a36Sopenharmony_ci	ureq->urb = NULL;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	kfree(ureq);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/*
18262306a36Sopenharmony_ci *		status
18362306a36Sopenharmony_ci */
18462306a36Sopenharmony_cistatic int usbhsh_is_running(struct usbhsh_hpriv *hpriv)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	/*
18762306a36Sopenharmony_ci	 * we can decide some device is attached or not
18862306a36Sopenharmony_ci	 * by checking mod.irq_attch
18962306a36Sopenharmony_ci	 * see
19062306a36Sopenharmony_ci	 *	usbhsh_irq_attch()
19162306a36Sopenharmony_ci	 *	usbhsh_irq_dtch()
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	return (hpriv->mod.irq_attch == NULL);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/*
19762306a36Sopenharmony_ci *		pipe control
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistatic void usbhsh_endpoint_sequence_save(struct usbhsh_hpriv *hpriv,
20062306a36Sopenharmony_ci					  struct urb *urb,
20162306a36Sopenharmony_ci					  struct usbhs_pkt *pkt)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int len = urb->actual_length;
20462306a36Sopenharmony_ci	int maxp = usb_endpoint_maxp(&urb->ep->desc);
20562306a36Sopenharmony_ci	int t = 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* DCP is out of sequence control */
20862306a36Sopenharmony_ci	if (usb_pipecontrol(urb->pipe))
20962306a36Sopenharmony_ci		return;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * renesas_usbhs pipe has a limitation in a number.
21362306a36Sopenharmony_ci	 * So, driver should re-use the limited pipe for each device/endpoint.
21462306a36Sopenharmony_ci	 * DATA0/1 sequence should be saved for it.
21562306a36Sopenharmony_ci	 * see [image of mod_host]
21662306a36Sopenharmony_ci	 *     [HARDWARE LIMITATION]
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/*
22062306a36Sopenharmony_ci	 * next sequence depends on actual_length
22162306a36Sopenharmony_ci	 *
22262306a36Sopenharmony_ci	 * ex) actual_length = 1147, maxp = 512
22362306a36Sopenharmony_ci	 * data0 : 512
22462306a36Sopenharmony_ci	 * data1 : 512
22562306a36Sopenharmony_ci	 * data0 : 123
22662306a36Sopenharmony_ci	 * data1 is the next sequence
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	t = len / maxp;
22962306a36Sopenharmony_ci	if (len % maxp)
23062306a36Sopenharmony_ci		t++;
23162306a36Sopenharmony_ci	if (pkt->zero)
23262306a36Sopenharmony_ci		t++;
23362306a36Sopenharmony_ci	t %= 2;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (t)
23662306a36Sopenharmony_ci		usb_dotoggle(urb->dev,
23762306a36Sopenharmony_ci			     usb_pipeendpoint(urb->pipe),
23862306a36Sopenharmony_ci			     usb_pipeout(urb->pipe));
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
24262306a36Sopenharmony_ci					       struct urb *urb);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv,
24562306a36Sopenharmony_ci			      struct urb *urb)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
24862306a36Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
24962306a36Sopenharmony_ci	struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
25062306a36Sopenharmony_ci	struct usbhs_pipe *pipe;
25162306a36Sopenharmony_ci	struct usb_endpoint_descriptor *desc = &urb->ep->desc;
25262306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
25362306a36Sopenharmony_ci	unsigned long flags;
25462306a36Sopenharmony_ci	int dir_in_req = !!usb_pipein(urb->pipe);
25562306a36Sopenharmony_ci	int is_dcp = usb_endpoint_xfer_control(desc);
25662306a36Sopenharmony_ci	int i, dir_in;
25762306a36Sopenharmony_ci	int ret = -EBUSY;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/********************  spin lock ********************/
26062306a36Sopenharmony_ci	usbhs_lock(priv, flags);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/*
26362306a36Sopenharmony_ci	 * if uep has been attached to pipe,
26462306a36Sopenharmony_ci	 * reuse it
26562306a36Sopenharmony_ci	 */
26662306a36Sopenharmony_ci	if (usbhsh_uep_to_pipe(uep)) {
26762306a36Sopenharmony_ci		ret = 0;
26862306a36Sopenharmony_ci		goto usbhsh_pipe_attach_done;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		/* check pipe type */
27462306a36Sopenharmony_ci		if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
27562306a36Sopenharmony_ci			continue;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		/* check pipe direction if normal pipe */
27862306a36Sopenharmony_ci		if (!is_dcp) {
27962306a36Sopenharmony_ci			dir_in = !!usbhs_pipe_is_dir_in(pipe);
28062306a36Sopenharmony_ci			if (0 != (dir_in - dir_in_req))
28162306a36Sopenharmony_ci				continue;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		/* check pipe is free */
28562306a36Sopenharmony_ci		if (usbhsh_pipe_to_uep(pipe))
28662306a36Sopenharmony_ci			continue;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		/*
28962306a36Sopenharmony_ci		 * attach pipe to uep
29062306a36Sopenharmony_ci		 *
29162306a36Sopenharmony_ci		 * usbhs_pipe_config_update() should be called after
29262306a36Sopenharmony_ci		 * usbhs_set_device_config()
29362306a36Sopenharmony_ci		 * see
29462306a36Sopenharmony_ci		 *  DCPMAXP/PIPEMAXP
29562306a36Sopenharmony_ci		 */
29662306a36Sopenharmony_ci		usbhsh_uep_to_pipe(uep)		= pipe;
29762306a36Sopenharmony_ci		usbhsh_pipe_to_uep(pipe)	= uep;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		usbhs_pipe_config_update(pipe,
30062306a36Sopenharmony_ci					 usbhsh_device_number(hpriv, udev),
30162306a36Sopenharmony_ci					 usb_endpoint_num(desc),
30262306a36Sopenharmony_ci					 usb_endpoint_maxp(desc));
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		dev_dbg(dev, "%s [%d-%d(%s:%s)]\n", __func__,
30562306a36Sopenharmony_ci			usbhsh_device_number(hpriv, udev),
30662306a36Sopenharmony_ci			usb_endpoint_num(desc),
30762306a36Sopenharmony_ci			usbhs_pipe_name(pipe),
30862306a36Sopenharmony_ci			dir_in_req ? "in" : "out");
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		ret = 0;
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ciusbhsh_pipe_attach_done:
31562306a36Sopenharmony_ci	if (0 == ret)
31662306a36Sopenharmony_ci		uep->counter++;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
31962306a36Sopenharmony_ci	/********************  spin unlock ******************/
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return ret;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void usbhsh_pipe_detach(struct usbhsh_hpriv *hpriv,
32562306a36Sopenharmony_ci			       struct usbhsh_ep *uep)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
32862306a36Sopenharmony_ci	struct usbhs_pipe *pipe;
32962306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
33062306a36Sopenharmony_ci	unsigned long flags;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (unlikely(!uep)) {
33362306a36Sopenharmony_ci		dev_err(dev, "no uep\n");
33462306a36Sopenharmony_ci		return;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/********************  spin lock ********************/
33862306a36Sopenharmony_ci	usbhs_lock(priv, flags);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	pipe = usbhsh_uep_to_pipe(uep);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (unlikely(!pipe)) {
34362306a36Sopenharmony_ci		dev_err(dev, "uep doesn't have pipe\n");
34462306a36Sopenharmony_ci	} else if (1 == uep->counter--) { /* last user */
34562306a36Sopenharmony_ci		struct usb_host_endpoint *ep = usbhsh_uep_to_ep(uep);
34662306a36Sopenharmony_ci		struct usbhsh_device *udev = usbhsh_uep_to_udev(uep);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		/* detach pipe from uep */
34962306a36Sopenharmony_ci		usbhsh_uep_to_pipe(uep)		= NULL;
35062306a36Sopenharmony_ci		usbhsh_pipe_to_uep(pipe)	= NULL;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		dev_dbg(dev, "%s [%d-%d(%s)]\n", __func__,
35362306a36Sopenharmony_ci			usbhsh_device_number(hpriv, udev),
35462306a36Sopenharmony_ci			usb_endpoint_num(&ep->desc),
35562306a36Sopenharmony_ci			usbhs_pipe_name(pipe));
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
35962306a36Sopenharmony_ci	/********************  spin unlock ******************/
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*
36362306a36Sopenharmony_ci *		endpoint control
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
36662306a36Sopenharmony_ci				  struct urb *urb,
36762306a36Sopenharmony_ci				  gfp_t mem_flags)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
37062306a36Sopenharmony_ci	struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
37162306a36Sopenharmony_ci	struct usb_host_endpoint *ep = urb->ep;
37262306a36Sopenharmony_ci	struct usbhsh_ep *uep;
37362306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
37462306a36Sopenharmony_ci	struct usb_endpoint_descriptor *desc = &ep->desc;
37562306a36Sopenharmony_ci	unsigned long flags;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
37862306a36Sopenharmony_ci	if (!uep)
37962306a36Sopenharmony_ci		return -ENOMEM;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/********************  spin lock ********************/
38262306a36Sopenharmony_ci	usbhs_lock(priv, flags);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/*
38562306a36Sopenharmony_ci	 * init endpoint
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	uep->counter = 0;
38862306a36Sopenharmony_ci	INIT_LIST_HEAD(&uep->ep_list);
38962306a36Sopenharmony_ci	list_add_tail(&uep->ep_list, &udev->ep_list_head);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	usbhsh_uep_to_udev(uep)	= udev;
39262306a36Sopenharmony_ci	usbhsh_uep_to_ep(uep)	= ep;
39362306a36Sopenharmony_ci	usbhsh_ep_to_uep(ep)	= uep;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
39662306a36Sopenharmony_ci	/********************  spin unlock ******************/
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	dev_dbg(dev, "%s [%d-%d]\n", __func__,
39962306a36Sopenharmony_ci		usbhsh_device_number(hpriv, udev),
40062306a36Sopenharmony_ci		usb_endpoint_num(desc));
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic void usbhsh_endpoint_detach(struct usbhsh_hpriv *hpriv,
40662306a36Sopenharmony_ci				   struct usb_host_endpoint *ep)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
40962306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
41062306a36Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
41162306a36Sopenharmony_ci	unsigned long flags;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (!uep)
41462306a36Sopenharmony_ci		return;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	dev_dbg(dev, "%s [%d-%d]\n", __func__,
41762306a36Sopenharmony_ci		usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
41862306a36Sopenharmony_ci		usb_endpoint_num(&ep->desc));
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (usbhsh_uep_to_pipe(uep))
42162306a36Sopenharmony_ci		usbhsh_pipe_detach(hpriv, uep);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/********************  spin lock ********************/
42462306a36Sopenharmony_ci	usbhs_lock(priv, flags);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* remove this endpoint from udev */
42762306a36Sopenharmony_ci	list_del_init(&uep->ep_list);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	usbhsh_uep_to_udev(uep)	= NULL;
43062306a36Sopenharmony_ci	usbhsh_uep_to_ep(uep)	= NULL;
43162306a36Sopenharmony_ci	usbhsh_ep_to_uep(ep)	= NULL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
43462306a36Sopenharmony_ci	/********************  spin unlock ******************/
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	kfree(uep);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void usbhsh_endpoint_detach_all(struct usbhsh_hpriv *hpriv,
44062306a36Sopenharmony_ci				       struct usbhsh_device *udev)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct usbhsh_ep *uep, *next;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	list_for_each_entry_safe(uep, next, &udev->ep_list_head, ep_list)
44562306a36Sopenharmony_ci		usbhsh_endpoint_detach(hpriv, usbhsh_uep_to_ep(uep));
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/*
44962306a36Sopenharmony_ci *		device control
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic int usbhsh_connected_to_rhdev(struct usb_hcd *hcd,
45262306a36Sopenharmony_ci				     struct usbhsh_device *udev)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return hcd->self.root_hub == usbv->parent;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic int usbhsh_device_has_endpoint(struct usbhsh_device *udev)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	return !list_empty(&udev->ep_list_head);
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
46562306a36Sopenharmony_ci					       struct urb *urb)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
46862306a36Sopenharmony_ci	struct usbhsh_device *udev = usbhsh_usbv_to_udev(usbv);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* usbhsh_device_attach() is still not called */
47162306a36Sopenharmony_ci	if (!udev)
47262306a36Sopenharmony_ci		return NULL;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* if it is device0, return it */
47562306a36Sopenharmony_ci	if (0 == usb_pipedevice(urb->pipe))
47662306a36Sopenharmony_ci		return usbhsh_device0(hpriv);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* return attached device */
47962306a36Sopenharmony_ci	return udev;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic struct usbhsh_device *usbhsh_device_attach(struct usbhsh_hpriv *hpriv,
48362306a36Sopenharmony_ci						 struct urb *urb)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct usbhsh_device *udev = NULL;
48662306a36Sopenharmony_ci	struct usbhsh_device *udev0 = usbhsh_device0(hpriv);
48762306a36Sopenharmony_ci	struct usbhsh_device *pos;
48862306a36Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
48962306a36Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
49062306a36Sopenharmony_ci	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
49162306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
49262306a36Sopenharmony_ci	unsigned long flags;
49362306a36Sopenharmony_ci	u16 upphub, hubport;
49462306a36Sopenharmony_ci	int i;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * This function should be called only while urb is pointing to device0.
49862306a36Sopenharmony_ci	 * It will attach unused usbhsh_device to urb (usbv),
49962306a36Sopenharmony_ci	 * and initialize device0.
50062306a36Sopenharmony_ci	 * You can use usbhsh_device_get() to get "current" udev,
50162306a36Sopenharmony_ci	 * and usbhsh_usbv_to_udev() is for "attached" udev.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	if (0 != usb_pipedevice(urb->pipe)) {
50462306a36Sopenharmony_ci		dev_err(dev, "%s fail: urb isn't pointing device0\n", __func__);
50562306a36Sopenharmony_ci		return NULL;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/********************  spin lock ********************/
50962306a36Sopenharmony_ci	usbhs_lock(priv, flags);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/*
51262306a36Sopenharmony_ci	 * find unused device
51362306a36Sopenharmony_ci	 */
51462306a36Sopenharmony_ci	usbhsh_for_each_udev(pos, hpriv, i) {
51562306a36Sopenharmony_ci		if (usbhsh_udev_is_used(pos))
51662306a36Sopenharmony_ci			continue;
51762306a36Sopenharmony_ci		udev = pos;
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (udev) {
52262306a36Sopenharmony_ci		/*
52362306a36Sopenharmony_ci		 * usbhsh_usbv_to_udev()
52462306a36Sopenharmony_ci		 * usbhsh_udev_to_usbv()
52562306a36Sopenharmony_ci		 * will be enable
52662306a36Sopenharmony_ci		 */
52762306a36Sopenharmony_ci		dev_set_drvdata(&usbv->dev, udev);
52862306a36Sopenharmony_ci		udev->usbv = usbv;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
53262306a36Sopenharmony_ci	/********************  spin unlock ******************/
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (!udev) {
53562306a36Sopenharmony_ci		dev_err(dev, "no free usbhsh_device\n");
53662306a36Sopenharmony_ci		return NULL;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (usbhsh_device_has_endpoint(udev)) {
54062306a36Sopenharmony_ci		dev_warn(dev, "udev have old endpoint\n");
54162306a36Sopenharmony_ci		usbhsh_endpoint_detach_all(hpriv, udev);
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (usbhsh_device_has_endpoint(udev0)) {
54562306a36Sopenharmony_ci		dev_warn(dev, "udev0 have old endpoint\n");
54662306a36Sopenharmony_ci		usbhsh_endpoint_detach_all(hpriv, udev0);
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* uep will be attached */
55062306a36Sopenharmony_ci	INIT_LIST_HEAD(&udev0->ep_list_head);
55162306a36Sopenharmony_ci	INIT_LIST_HEAD(&udev->ep_list_head);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/*
55462306a36Sopenharmony_ci	 * set device0 config
55562306a36Sopenharmony_ci	 */
55662306a36Sopenharmony_ci	usbhs_set_device_config(priv,
55762306a36Sopenharmony_ci				0, 0, 0, usbv->speed);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/*
56062306a36Sopenharmony_ci	 * set new device config
56162306a36Sopenharmony_ci	 */
56262306a36Sopenharmony_ci	upphub	= 0;
56362306a36Sopenharmony_ci	hubport	= 0;
56462306a36Sopenharmony_ci	if (!usbhsh_connected_to_rhdev(hcd, udev)) {
56562306a36Sopenharmony_ci		/* if udev is not connected to rhdev, it means parent is Hub */
56662306a36Sopenharmony_ci		struct usbhsh_device *parent = usbhsh_device_parent(udev);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		upphub	= usbhsh_device_number(hpriv, parent);
56962306a36Sopenharmony_ci		hubport	= usbhsh_device_hubport(udev);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		dev_dbg(dev, "%s connected to Hub [%d:%d](%p)\n", __func__,
57262306a36Sopenharmony_ci			upphub, hubport, parent);
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	usbhs_set_device_config(priv,
57662306a36Sopenharmony_ci			       usbhsh_device_number(hpriv, udev),
57762306a36Sopenharmony_ci			       upphub, hubport, usbv->speed);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	dev_dbg(dev, "%s [%d](%p)\n", __func__,
58062306a36Sopenharmony_ci		usbhsh_device_number(hpriv, udev), udev);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return udev;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic void usbhsh_device_detach(struct usbhsh_hpriv *hpriv,
58662306a36Sopenharmony_ci			       struct usbhsh_device *udev)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
58962306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
59062306a36Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
59162306a36Sopenharmony_ci	struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
59262306a36Sopenharmony_ci	unsigned long flags;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	dev_dbg(dev, "%s [%d](%p)\n", __func__,
59562306a36Sopenharmony_ci		usbhsh_device_number(hpriv, udev), udev);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (usbhsh_device_has_endpoint(udev)) {
59862306a36Sopenharmony_ci		dev_warn(dev, "udev still have endpoint\n");
59962306a36Sopenharmony_ci		usbhsh_endpoint_detach_all(hpriv, udev);
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/*
60362306a36Sopenharmony_ci	 * There is nothing to do if it is device0.
60462306a36Sopenharmony_ci	 * see
60562306a36Sopenharmony_ci	 *  usbhsh_device_attach()
60662306a36Sopenharmony_ci	 *  usbhsh_device_get()
60762306a36Sopenharmony_ci	 */
60862306a36Sopenharmony_ci	if (0 == usbhsh_device_number(hpriv, udev))
60962306a36Sopenharmony_ci		return;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/********************  spin lock ********************/
61262306a36Sopenharmony_ci	usbhs_lock(priv, flags);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/*
61562306a36Sopenharmony_ci	 * usbhsh_usbv_to_udev()
61662306a36Sopenharmony_ci	 * usbhsh_udev_to_usbv()
61762306a36Sopenharmony_ci	 * will be disable
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	dev_set_drvdata(&usbv->dev, NULL);
62062306a36Sopenharmony_ci	udev->usbv = NULL;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
62362306a36Sopenharmony_ci	/********************  spin unlock ******************/
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/*
62762306a36Sopenharmony_ci *		queue push/pop
62862306a36Sopenharmony_ci */
62962306a36Sopenharmony_cistatic void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
63262306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
63362306a36Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
63462306a36Sopenharmony_ci	struct urb *urb = ureq->urb;
63562306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
63662306a36Sopenharmony_ci	int status = 0;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (!urb) {
64162306a36Sopenharmony_ci		dev_warn(dev, "pkt doesn't have urb\n");
64262306a36Sopenharmony_ci		return;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (!usbhsh_is_running(hpriv))
64662306a36Sopenharmony_ci		status = -ESHUTDOWN;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	urb->actual_length = pkt->actual;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	usbhsh_endpoint_sequence_save(hpriv, urb, pkt);
65162306a36Sopenharmony_ci	usbhsh_ureq_free(hpriv, ureq);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep));
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	usb_hcd_unlink_urb_from_ep(hcd, urb);
65662306a36Sopenharmony_ci	usb_hcd_giveback_urb(hcd, urb, status);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int usbhsh_queue_push(struct usb_hcd *hcd,
66062306a36Sopenharmony_ci			     struct urb *urb,
66162306a36Sopenharmony_ci			     gfp_t mem_flags)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
66462306a36Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
66562306a36Sopenharmony_ci	struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep);
66662306a36Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
66762306a36Sopenharmony_ci	struct usbhsh_request *ureq;
66862306a36Sopenharmony_ci	void *buf;
66962306a36Sopenharmony_ci	int len, sequence;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (usb_pipeisoc(urb->pipe)) {
67262306a36Sopenharmony_ci		dev_err(dev, "pipe iso is not supported now\n");
67362306a36Sopenharmony_ci		return -EIO;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* this ureq will be freed on usbhsh_queue_done() */
67762306a36Sopenharmony_ci	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
67862306a36Sopenharmony_ci	if (unlikely(!ureq)) {
67962306a36Sopenharmony_ci		dev_err(dev, "ureq alloc fail\n");
68062306a36Sopenharmony_ci		return -ENOMEM;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (usb_pipein(urb->pipe))
68462306a36Sopenharmony_ci		pipe->handler = &usbhs_fifo_dma_pop_handler;
68562306a36Sopenharmony_ci	else
68662306a36Sopenharmony_ci		pipe->handler = &usbhs_fifo_dma_push_handler;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	buf = (void *)(urb->transfer_buffer + urb->actual_length);
68962306a36Sopenharmony_ci	len = urb->transfer_buffer_length - urb->actual_length;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	sequence = usb_gettoggle(urb->dev,
69262306a36Sopenharmony_ci				 usb_pipeendpoint(urb->pipe),
69362306a36Sopenharmony_ci				 usb_pipeout(urb->pipe));
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
69662306a36Sopenharmony_ci	usbhs_pkt_push(pipe, &ureq->pkt, usbhsh_queue_done,
69762306a36Sopenharmony_ci		       buf, len, (urb->transfer_flags & URB_ZERO_PACKET),
69862306a36Sopenharmony_ci		       sequence);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	usbhs_pkt_start(pipe);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void usbhsh_queue_force_pop(struct usbhs_priv *priv,
70662306a36Sopenharmony_ci				   struct usbhs_pipe *pipe)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct usbhs_pkt *pkt;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	while (1) {
71162306a36Sopenharmony_ci		pkt = usbhs_pkt_pop(pipe, NULL);
71262306a36Sopenharmony_ci		if (!pkt)
71362306a36Sopenharmony_ci			break;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		/*
71662306a36Sopenharmony_ci		 * if all packet are gone, usbhsh_endpoint_disable()
71762306a36Sopenharmony_ci		 * will be called.
71862306a36Sopenharmony_ci		 * then, attached device/endpoint/pipe will be detached
71962306a36Sopenharmony_ci		 */
72062306a36Sopenharmony_ci		usbhsh_queue_done(priv, pkt);
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic void usbhsh_queue_force_pop_all(struct usbhs_priv *priv)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct usbhs_pipe *pos;
72762306a36Sopenharmony_ci	int i;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	usbhs_for_each_pipe_with_dcp(pos, priv, i)
73062306a36Sopenharmony_ci		usbhsh_queue_force_pop(priv, pos);
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci/*
73462306a36Sopenharmony_ci *		DCP setup stage
73562306a36Sopenharmony_ci */
73662306a36Sopenharmony_cistatic int usbhsh_is_request_address(struct urb *urb)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct usb_ctrlrequest *req;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	req = (struct usb_ctrlrequest *)urb->setup_packet;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if ((DeviceOutRequest    == req->bRequestType << 8) &&
74362306a36Sopenharmony_ci	    (USB_REQ_SET_ADDRESS == req->bRequest))
74462306a36Sopenharmony_ci		return 1;
74562306a36Sopenharmony_ci	else
74662306a36Sopenharmony_ci		return 0;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv,
75062306a36Sopenharmony_ci					   struct urb *urb,
75162306a36Sopenharmony_ci					   struct usbhs_pipe *pipe)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
75462306a36Sopenharmony_ci	struct usb_ctrlrequest req;
75562306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/*
75862306a36Sopenharmony_ci	 * wait setup packet ACK
75962306a36Sopenharmony_ci	 * see
76062306a36Sopenharmony_ci	 *	usbhsh_irq_setup_ack()
76162306a36Sopenharmony_ci	 *	usbhsh_irq_setup_err()
76262306a36Sopenharmony_ci	 */
76362306a36Sopenharmony_ci	init_completion(&hpriv->setup_ack_done);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	/* copy original request */
76662306a36Sopenharmony_ci	memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest));
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/*
76962306a36Sopenharmony_ci	 * renesas_usbhs can not use original usb address.
77062306a36Sopenharmony_ci	 * see HARDWARE LIMITATION.
77162306a36Sopenharmony_ci	 * modify usb address here to use attached device.
77262306a36Sopenharmony_ci	 * see usbhsh_device_attach()
77362306a36Sopenharmony_ci	 */
77462306a36Sopenharmony_ci	if (usbhsh_is_request_address(urb)) {
77562306a36Sopenharmony_ci		struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
77662306a36Sopenharmony_ci		struct usbhsh_device *udev = usbhsh_usbv_to_udev(usbv);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		/* udev is a attached device */
77962306a36Sopenharmony_ci		req.wValue = usbhsh_device_number(hpriv, udev);
78062306a36Sopenharmony_ci		dev_dbg(dev, "create new address - %d\n", req.wValue);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* set request */
78462306a36Sopenharmony_ci	usbhs_usbreq_set_val(priv, &req);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/*
78762306a36Sopenharmony_ci	 * wait setup packet ACK
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	wait_for_completion(&hpriv->setup_ack_done);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	dev_dbg(dev, "%s done\n", __func__);
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/*
79562306a36Sopenharmony_ci *		DCP data stage
79662306a36Sopenharmony_ci */
79762306a36Sopenharmony_cistatic void usbhsh_data_stage_packet_done(struct usbhs_priv *priv,
79862306a36Sopenharmony_ci					  struct usbhs_pkt *pkt)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
80162306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* this ureq was connected to urb when usbhsh_urb_enqueue()  */
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	usbhsh_ureq_free(hpriv, ureq);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv,
80962306a36Sopenharmony_ci					 struct urb *urb,
81062306a36Sopenharmony_ci					 struct usbhs_pipe *pipe,
81162306a36Sopenharmony_ci					 gfp_t mem_flags)
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct usbhsh_request *ureq;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* this ureq will be freed on usbhsh_data_stage_packet_done() */
81762306a36Sopenharmony_ci	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
81862306a36Sopenharmony_ci	if (unlikely(!ureq))
81962306a36Sopenharmony_ci		return -ENOMEM;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (usb_pipein(urb->pipe))
82262306a36Sopenharmony_ci		pipe->handler = &usbhs_dcp_data_stage_in_handler;
82362306a36Sopenharmony_ci	else
82462306a36Sopenharmony_ci		pipe->handler = &usbhs_dcp_data_stage_out_handler;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	usbhs_pkt_push(pipe, &ureq->pkt,
82762306a36Sopenharmony_ci		       usbhsh_data_stage_packet_done,
82862306a36Sopenharmony_ci		       urb->transfer_buffer,
82962306a36Sopenharmony_ci		       urb->transfer_buffer_length,
83062306a36Sopenharmony_ci		       (urb->transfer_flags & URB_ZERO_PACKET),
83162306a36Sopenharmony_ci		       -1);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return 0;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci/*
83762306a36Sopenharmony_ci *		DCP status stage
83862306a36Sopenharmony_ci */
83962306a36Sopenharmony_cistatic int usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv,
84062306a36Sopenharmony_ci					    struct urb *urb,
84162306a36Sopenharmony_ci					    struct usbhs_pipe *pipe,
84262306a36Sopenharmony_ci					    gfp_t mem_flags)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct usbhsh_request *ureq;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* This ureq will be freed on usbhsh_queue_done() */
84762306a36Sopenharmony_ci	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
84862306a36Sopenharmony_ci	if (unlikely(!ureq))
84962306a36Sopenharmony_ci		return -ENOMEM;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (usb_pipein(urb->pipe))
85262306a36Sopenharmony_ci		pipe->handler = &usbhs_dcp_status_stage_in_handler;
85362306a36Sopenharmony_ci	else
85462306a36Sopenharmony_ci		pipe->handler = &usbhs_dcp_status_stage_out_handler;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	usbhs_pkt_push(pipe, &ureq->pkt,
85762306a36Sopenharmony_ci		       usbhsh_queue_done,
85862306a36Sopenharmony_ci		       NULL,
85962306a36Sopenharmony_ci		       urb->transfer_buffer_length,
86062306a36Sopenharmony_ci		       0, -1);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	return 0;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic int usbhsh_dcp_queue_push(struct usb_hcd *hcd,
86662306a36Sopenharmony_ci				 struct urb *urb,
86762306a36Sopenharmony_ci				 gfp_t mflags)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
87062306a36Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
87162306a36Sopenharmony_ci	struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep);
87262306a36Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
87362306a36Sopenharmony_ci	int ret;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/*
87862306a36Sopenharmony_ci	 * setup stage
87962306a36Sopenharmony_ci	 *
88062306a36Sopenharmony_ci	 * usbhsh_send_setup_stage_packet() wait SACK/SIGN
88162306a36Sopenharmony_ci	 */
88262306a36Sopenharmony_ci	usbhsh_setup_stage_packet_push(hpriv, urb, pipe);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	/*
88562306a36Sopenharmony_ci	 * data stage
88662306a36Sopenharmony_ci	 *
88762306a36Sopenharmony_ci	 * It is pushed only when urb has buffer.
88862306a36Sopenharmony_ci	 */
88962306a36Sopenharmony_ci	if (urb->transfer_buffer_length) {
89062306a36Sopenharmony_ci		ret = usbhsh_data_stage_packet_push(hpriv, urb, pipe, mflags);
89162306a36Sopenharmony_ci		if (ret < 0) {
89262306a36Sopenharmony_ci			dev_err(dev, "data stage failed\n");
89362306a36Sopenharmony_ci			return ret;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/*
89862306a36Sopenharmony_ci	 * status stage
89962306a36Sopenharmony_ci	 */
90062306a36Sopenharmony_ci	ret = usbhsh_status_stage_packet_push(hpriv, urb, pipe, mflags);
90162306a36Sopenharmony_ci	if (ret < 0) {
90262306a36Sopenharmony_ci		dev_err(dev, "status stage failed\n");
90362306a36Sopenharmony_ci		return ret;
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/*
90762306a36Sopenharmony_ci	 * start pushed packets
90862306a36Sopenharmony_ci	 */
90962306a36Sopenharmony_ci	usbhs_pkt_start(pipe);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return 0;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci/*
91562306a36Sopenharmony_ci *		dma map functions
91662306a36Sopenharmony_ci */
91762306a36Sopenharmony_cistatic int usbhsh_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt,
91862306a36Sopenharmony_ci			       int map)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	if (map) {
92162306a36Sopenharmony_ci		struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
92262306a36Sopenharmony_ci		struct urb *urb = ureq->urb;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		/* it can not use scatter/gather */
92562306a36Sopenharmony_ci		if (urb->num_sgs)
92662306a36Sopenharmony_ci			return -EINVAL;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		pkt->dma = urb->transfer_dma;
92962306a36Sopenharmony_ci		if (!pkt->dma)
93062306a36Sopenharmony_ci			return -EINVAL;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	return 0;
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci/*
93762306a36Sopenharmony_ci *		for hc_driver
93862306a36Sopenharmony_ci */
93962306a36Sopenharmony_cistatic int usbhsh_host_start(struct usb_hcd *hcd)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	return 0;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic void usbhsh_host_stop(struct usb_hcd *hcd)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic int usbhsh_urb_enqueue(struct usb_hcd *hcd,
94962306a36Sopenharmony_ci			      struct urb *urb,
95062306a36Sopenharmony_ci			      gfp_t mem_flags)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
95362306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
95462306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
95562306a36Sopenharmony_ci	struct usb_host_endpoint *ep = urb->ep;
95662306a36Sopenharmony_ci	struct usbhsh_device *new_udev = NULL;
95762306a36Sopenharmony_ci	int is_dir_in = usb_pipein(urb->pipe);
95862306a36Sopenharmony_ci	int ret;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	dev_dbg(dev, "%s (%s)\n", __func__, is_dir_in ? "in" : "out");
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (!usbhsh_is_running(hpriv)) {
96362306a36Sopenharmony_ci		ret = -EIO;
96462306a36Sopenharmony_ci		dev_err(dev, "host is not running\n");
96562306a36Sopenharmony_ci		goto usbhsh_urb_enqueue_error_not_linked;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	ret = usb_hcd_link_urb_to_ep(hcd, urb);
96962306a36Sopenharmony_ci	if (ret) {
97062306a36Sopenharmony_ci		dev_err(dev, "urb link failed\n");
97162306a36Sopenharmony_ci		goto usbhsh_urb_enqueue_error_not_linked;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	/*
97562306a36Sopenharmony_ci	 * attach udev if needed
97662306a36Sopenharmony_ci	 * see [image of mod_host]
97762306a36Sopenharmony_ci	 */
97862306a36Sopenharmony_ci	if (!usbhsh_device_get(hpriv, urb)) {
97962306a36Sopenharmony_ci		new_udev = usbhsh_device_attach(hpriv, urb);
98062306a36Sopenharmony_ci		if (!new_udev) {
98162306a36Sopenharmony_ci			ret = -EIO;
98262306a36Sopenharmony_ci			dev_err(dev, "device attach failed\n");
98362306a36Sopenharmony_ci			goto usbhsh_urb_enqueue_error_not_linked;
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/*
98862306a36Sopenharmony_ci	 * attach endpoint if needed
98962306a36Sopenharmony_ci	 * see [image of mod_host]
99062306a36Sopenharmony_ci	 */
99162306a36Sopenharmony_ci	if (!usbhsh_ep_to_uep(ep)) {
99262306a36Sopenharmony_ci		ret = usbhsh_endpoint_attach(hpriv, urb, mem_flags);
99362306a36Sopenharmony_ci		if (ret < 0) {
99462306a36Sopenharmony_ci			dev_err(dev, "endpoint attach failed\n");
99562306a36Sopenharmony_ci			goto usbhsh_urb_enqueue_error_free_device;
99662306a36Sopenharmony_ci		}
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/*
100062306a36Sopenharmony_ci	 * attach pipe to endpoint
100162306a36Sopenharmony_ci	 * see [image of mod_host]
100262306a36Sopenharmony_ci	 */
100362306a36Sopenharmony_ci	ret = usbhsh_pipe_attach(hpriv, urb);
100462306a36Sopenharmony_ci	if (ret < 0) {
100562306a36Sopenharmony_ci		dev_err(dev, "pipe attach failed\n");
100662306a36Sopenharmony_ci		goto usbhsh_urb_enqueue_error_free_endpoint;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/*
101062306a36Sopenharmony_ci	 * push packet
101162306a36Sopenharmony_ci	 */
101262306a36Sopenharmony_ci	if (usb_pipecontrol(urb->pipe))
101362306a36Sopenharmony_ci		ret = usbhsh_dcp_queue_push(hcd, urb, mem_flags);
101462306a36Sopenharmony_ci	else
101562306a36Sopenharmony_ci		ret = usbhsh_queue_push(hcd, urb, mem_flags);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	return ret;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ciusbhsh_urb_enqueue_error_free_endpoint:
102062306a36Sopenharmony_ci	usbhsh_endpoint_detach(hpriv, ep);
102162306a36Sopenharmony_ciusbhsh_urb_enqueue_error_free_device:
102262306a36Sopenharmony_ci	if (new_udev)
102362306a36Sopenharmony_ci		usbhsh_device_detach(hpriv, new_udev);
102462306a36Sopenharmony_ciusbhsh_urb_enqueue_error_not_linked:
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	dev_dbg(dev, "%s error\n", __func__);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	return ret;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
103462306a36Sopenharmony_ci	struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (ureq) {
103762306a36Sopenharmony_ci		struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
103862306a36Sopenharmony_ci		struct usbhs_pkt *pkt = &ureq->pkt;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		usbhs_pkt_pop(pkt->pipe, pkt);
104162306a36Sopenharmony_ci		usbhsh_queue_done(priv, pkt);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return 0;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic void usbhsh_endpoint_disable(struct usb_hcd *hcd,
104862306a36Sopenharmony_ci				    struct usb_host_endpoint *ep)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
105162306a36Sopenharmony_ci	struct usbhsh_device *udev;
105262306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/*
105562306a36Sopenharmony_ci	 * this function might be called manytimes by same hcd/ep
105662306a36Sopenharmony_ci	 * in-endpoint == out-endpoint if ep == dcp.
105762306a36Sopenharmony_ci	 */
105862306a36Sopenharmony_ci	if (!uep)
105962306a36Sopenharmony_ci		return;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	udev	= usbhsh_uep_to_udev(uep);
106262306a36Sopenharmony_ci	hpriv	= usbhsh_hcd_to_hpriv(hcd);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	usbhsh_endpoint_detach(hpriv, ep);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/*
106762306a36Sopenharmony_ci	 * if there is no endpoint,
106862306a36Sopenharmony_ci	 * free device
106962306a36Sopenharmony_ci	 */
107062306a36Sopenharmony_ci	if (!usbhsh_device_has_endpoint(udev))
107162306a36Sopenharmony_ci		usbhsh_device_detach(hpriv, udev);
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
107762306a36Sopenharmony_ci	int roothub_id = 1; /* only 1 root hub */
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/*
108062306a36Sopenharmony_ci	 * does port stat was changed ?
108162306a36Sopenharmony_ci	 * check USB_PORT_STAT_C_xxx << 16
108262306a36Sopenharmony_ci	 */
108362306a36Sopenharmony_ci	if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000)
108462306a36Sopenharmony_ci		*buf = (1 << roothub_id);
108562306a36Sopenharmony_ci	else
108662306a36Sopenharmony_ci		*buf = 0;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return !!(*buf);
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv,
109262306a36Sopenharmony_ci				    u16 typeReq, u16 wValue,
109362306a36Sopenharmony_ci				    u16 wIndex, char *buf, u16 wLength)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
109662306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	switch (wValue) {
109962306a36Sopenharmony_ci	case C_HUB_OVER_CURRENT:
110062306a36Sopenharmony_ci	case C_HUB_LOCAL_POWER:
110162306a36Sopenharmony_ci		dev_dbg(dev, "%s :: C_HUB_xx\n", __func__);
110262306a36Sopenharmony_ci		return 0;
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	return -EPIPE;
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_cistatic int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv,
110962306a36Sopenharmony_ci				     u16 typeReq, u16 wValue,
111062306a36Sopenharmony_ci				     u16 wIndex, char *buf, u16 wLength)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
111362306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
111462306a36Sopenharmony_ci	int enable = (typeReq == SetPortFeature);
111562306a36Sopenharmony_ci	int speed, i, timeout = 128;
111662306a36Sopenharmony_ci	int roothub_id = 1; /* only 1 root hub */
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	/* common error */
111962306a36Sopenharmony_ci	if (wIndex > roothub_id || wLength != 0)
112062306a36Sopenharmony_ci		return -EPIPE;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	/* check wValue */
112362306a36Sopenharmony_ci	switch (wValue) {
112462306a36Sopenharmony_ci	case USB_PORT_FEAT_POWER:
112562306a36Sopenharmony_ci		usbhs_vbus_ctrl(priv, enable);
112662306a36Sopenharmony_ci		dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__);
112762306a36Sopenharmony_ci		break;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	case USB_PORT_FEAT_ENABLE:
113062306a36Sopenharmony_ci	case USB_PORT_FEAT_SUSPEND:
113162306a36Sopenharmony_ci	case USB_PORT_FEAT_C_ENABLE:
113262306a36Sopenharmony_ci	case USB_PORT_FEAT_C_SUSPEND:
113362306a36Sopenharmony_ci	case USB_PORT_FEAT_C_CONNECTION:
113462306a36Sopenharmony_ci	case USB_PORT_FEAT_C_OVER_CURRENT:
113562306a36Sopenharmony_ci	case USB_PORT_FEAT_C_RESET:
113662306a36Sopenharmony_ci		dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__);
113762306a36Sopenharmony_ci		break;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	case USB_PORT_FEAT_RESET:
114062306a36Sopenharmony_ci		if (!enable)
114162306a36Sopenharmony_ci			break;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci		usbhsh_port_stat_clear(hpriv,
114462306a36Sopenharmony_ci				       USB_PORT_STAT_HIGH_SPEED |
114562306a36Sopenharmony_ci				       USB_PORT_STAT_LOW_SPEED);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		usbhsh_queue_force_pop_all(priv);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		usbhs_bus_send_reset(priv);
115062306a36Sopenharmony_ci		msleep(20);
115162306a36Sopenharmony_ci		usbhs_bus_send_sof_enable(priv);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		for (i = 0; i < timeout ; i++) {
115462306a36Sopenharmony_ci			switch (usbhs_bus_get_speed(priv)) {
115562306a36Sopenharmony_ci			case USB_SPEED_LOW:
115662306a36Sopenharmony_ci				speed = USB_PORT_STAT_LOW_SPEED;
115762306a36Sopenharmony_ci				goto got_usb_bus_speed;
115862306a36Sopenharmony_ci			case USB_SPEED_HIGH:
115962306a36Sopenharmony_ci				speed = USB_PORT_STAT_HIGH_SPEED;
116062306a36Sopenharmony_ci				goto got_usb_bus_speed;
116162306a36Sopenharmony_ci			case USB_SPEED_FULL:
116262306a36Sopenharmony_ci				speed = 0;
116362306a36Sopenharmony_ci				goto got_usb_bus_speed;
116462306a36Sopenharmony_ci			}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci			msleep(20);
116762306a36Sopenharmony_ci		}
116862306a36Sopenharmony_ci		return -EPIPE;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cigot_usb_bus_speed:
117162306a36Sopenharmony_ci		usbhsh_port_stat_set(hpriv, speed);
117262306a36Sopenharmony_ci		usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n",
117562306a36Sopenharmony_ci			__func__, speed);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		/* status change is not needed */
117862306a36Sopenharmony_ci		return 0;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	default:
118162306a36Sopenharmony_ci		return -EPIPE;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* set/clear status */
118562306a36Sopenharmony_ci	if (enable)
118662306a36Sopenharmony_ci		usbhsh_port_stat_set(hpriv, (1 << wValue));
118762306a36Sopenharmony_ci	else
118862306a36Sopenharmony_ci		usbhsh_port_stat_clear(hpriv, (1 << wValue));
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return 0;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv,
119462306a36Sopenharmony_ci				   u16 typeReq, u16 wValue,
119562306a36Sopenharmony_ci				   u16 wIndex, char *buf, u16 wLength)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
119862306a36Sopenharmony_ci	struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf;
119962306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
120062306a36Sopenharmony_ci	int roothub_id = 1; /* only 1 root hub */
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	switch (typeReq) {
120362306a36Sopenharmony_ci	case GetHubStatus:
120462306a36Sopenharmony_ci		dev_dbg(dev, "%s :: GetHubStatus\n", __func__);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		*buf = 0x00;
120762306a36Sopenharmony_ci		break;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	case GetPortStatus:
121062306a36Sopenharmony_ci		if (wIndex != roothub_id)
121162306a36Sopenharmony_ci			return -EPIPE;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		dev_dbg(dev, "%s :: GetPortStatus\n", __func__);
121462306a36Sopenharmony_ci		*(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv));
121562306a36Sopenharmony_ci		break;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	case GetHubDescriptor:
121862306a36Sopenharmony_ci		desc->bDescriptorType		= USB_DT_HUB;
121962306a36Sopenharmony_ci		desc->bHubContrCurrent		= 0;
122062306a36Sopenharmony_ci		desc->bNbrPorts			= roothub_id;
122162306a36Sopenharmony_ci		desc->bDescLength		= 9;
122262306a36Sopenharmony_ci		desc->bPwrOn2PwrGood		= 0;
122362306a36Sopenharmony_ci		desc->wHubCharacteristics	=
122462306a36Sopenharmony_ci			cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM);
122562306a36Sopenharmony_ci		desc->u.hs.DeviceRemovable[0]	= (roothub_id << 1);
122662306a36Sopenharmony_ci		desc->u.hs.DeviceRemovable[1]	= ~0;
122762306a36Sopenharmony_ci		dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__);
122862306a36Sopenharmony_ci		break;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	return 0;
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
123562306a36Sopenharmony_ci			      u16 wIndex, char *buf, u16 wLength)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
123862306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
123962306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
124062306a36Sopenharmony_ci	int ret = -EPIPE;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	switch (typeReq) {
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* Hub Feature */
124562306a36Sopenharmony_ci	case ClearHubFeature:
124662306a36Sopenharmony_ci	case SetHubFeature:
124762306a36Sopenharmony_ci		ret = __usbhsh_hub_hub_feature(hpriv, typeReq,
124862306a36Sopenharmony_ci					       wValue, wIndex, buf, wLength);
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	/* Port Feature */
125262306a36Sopenharmony_ci	case SetPortFeature:
125362306a36Sopenharmony_ci	case ClearPortFeature:
125462306a36Sopenharmony_ci		ret = __usbhsh_hub_port_feature(hpriv, typeReq,
125562306a36Sopenharmony_ci						wValue, wIndex, buf, wLength);
125662306a36Sopenharmony_ci		break;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	/* Get status */
125962306a36Sopenharmony_ci	case GetHubStatus:
126062306a36Sopenharmony_ci	case GetPortStatus:
126162306a36Sopenharmony_ci	case GetHubDescriptor:
126262306a36Sopenharmony_ci		ret = __usbhsh_hub_get_status(hpriv, typeReq,
126362306a36Sopenharmony_ci					      wValue, wIndex, buf, wLength);
126462306a36Sopenharmony_ci		break;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n",
126862306a36Sopenharmony_ci		typeReq, ret, usbhsh_port_stat_get(hpriv));
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return ret;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic int usbhsh_bus_nop(struct usb_hcd *hcd)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	/* nothing to do */
127662306a36Sopenharmony_ci	return 0;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_cistatic const struct hc_driver usbhsh_driver = {
128062306a36Sopenharmony_ci	.description =		usbhsh_hcd_name,
128162306a36Sopenharmony_ci	.hcd_priv_size =	sizeof(struct usbhsh_hpriv),
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/*
128462306a36Sopenharmony_ci	 * generic hardware linkage
128562306a36Sopenharmony_ci	 */
128662306a36Sopenharmony_ci	.flags =		HCD_DMA | HCD_USB2,
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	.start =		usbhsh_host_start,
128962306a36Sopenharmony_ci	.stop =			usbhsh_host_stop,
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	/*
129262306a36Sopenharmony_ci	 * managing i/o requests and associated device resources
129362306a36Sopenharmony_ci	 */
129462306a36Sopenharmony_ci	.urb_enqueue =		usbhsh_urb_enqueue,
129562306a36Sopenharmony_ci	.urb_dequeue =		usbhsh_urb_dequeue,
129662306a36Sopenharmony_ci	.endpoint_disable =	usbhsh_endpoint_disable,
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/*
129962306a36Sopenharmony_ci	 * root hub
130062306a36Sopenharmony_ci	 */
130162306a36Sopenharmony_ci	.hub_status_data =	usbhsh_hub_status_data,
130262306a36Sopenharmony_ci	.hub_control =		usbhsh_hub_control,
130362306a36Sopenharmony_ci	.bus_suspend =		usbhsh_bus_nop,
130462306a36Sopenharmony_ci	.bus_resume =		usbhsh_bus_nop,
130562306a36Sopenharmony_ci};
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci/*
130862306a36Sopenharmony_ci *		interrupt functions
130962306a36Sopenharmony_ci */
131062306a36Sopenharmony_cistatic int usbhsh_irq_attch(struct usbhs_priv *priv,
131162306a36Sopenharmony_ci			    struct usbhs_irq_state *irq_state)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
131462306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	dev_dbg(dev, "device attached\n");
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION);
131962306a36Sopenharmony_ci	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	/*
132262306a36Sopenharmony_ci	 * attch interrupt might happen infinitely on some device
132362306a36Sopenharmony_ci	 * (on self power USB hub ?)
132462306a36Sopenharmony_ci	 * disable it here.
132562306a36Sopenharmony_ci	 *
132662306a36Sopenharmony_ci	 * usbhsh_is_running() becomes effective
132762306a36Sopenharmony_ci	 * according to this process.
132862306a36Sopenharmony_ci	 * see
132962306a36Sopenharmony_ci	 *	usbhsh_is_running()
133062306a36Sopenharmony_ci	 *	usbhsh_urb_enqueue()
133162306a36Sopenharmony_ci	 */
133262306a36Sopenharmony_ci	hpriv->mod.irq_attch = NULL;
133362306a36Sopenharmony_ci	usbhs_irq_callback_update(priv, &hpriv->mod);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	return 0;
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic int usbhsh_irq_dtch(struct usbhs_priv *priv,
133962306a36Sopenharmony_ci			   struct usbhs_irq_state *irq_state)
134062306a36Sopenharmony_ci{
134162306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
134262306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	dev_dbg(dev, "device detached\n");
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION);
134762306a36Sopenharmony_ci	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	/*
135062306a36Sopenharmony_ci	 * enable attch interrupt again
135162306a36Sopenharmony_ci	 *
135262306a36Sopenharmony_ci	 * usbhsh_is_running() becomes invalid
135362306a36Sopenharmony_ci	 * according to this process.
135462306a36Sopenharmony_ci	 * see
135562306a36Sopenharmony_ci	 *	usbhsh_is_running()
135662306a36Sopenharmony_ci	 *	usbhsh_urb_enqueue()
135762306a36Sopenharmony_ci	 */
135862306a36Sopenharmony_ci	hpriv->mod.irq_attch = usbhsh_irq_attch;
135962306a36Sopenharmony_ci	usbhs_irq_callback_update(priv, &hpriv->mod);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/*
136262306a36Sopenharmony_ci	 * usbhsh_queue_force_pop_all() should be called
136362306a36Sopenharmony_ci	 * after usbhsh_is_running() becomes invalid.
136462306a36Sopenharmony_ci	 */
136562306a36Sopenharmony_ci	usbhsh_queue_force_pop_all(priv);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	return 0;
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic int usbhsh_irq_setup_ack(struct usbhs_priv *priv,
137162306a36Sopenharmony_ci				struct usbhs_irq_state *irq_state)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
137462306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	dev_dbg(dev, "setup packet OK\n");
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	complete(&hpriv->setup_ack_done); /* see usbhsh_urb_enqueue() */
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	return 0;
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic int usbhsh_irq_setup_err(struct usbhs_priv *priv,
138462306a36Sopenharmony_ci				struct usbhs_irq_state *irq_state)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
138762306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	dev_dbg(dev, "setup packet Err\n");
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	complete(&hpriv->setup_ack_done); /* see usbhsh_urb_enqueue() */
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	return 0;
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci/*
139762306a36Sopenharmony_ci *		module start/stop
139862306a36Sopenharmony_ci */
139962306a36Sopenharmony_cistatic void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
140262306a36Sopenharmony_ci	struct usbhs_pipe *pipe;
140362306a36Sopenharmony_ci	struct renesas_usbhs_driver_pipe_config *pipe_configs =
140462306a36Sopenharmony_ci					usbhs_get_dparam(priv, pipe_configs);
140562306a36Sopenharmony_ci	int pipe_size = usbhs_get_dparam(priv, pipe_size);
140662306a36Sopenharmony_ci	int old_type, dir_in, i;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	/* init all pipe */
140962306a36Sopenharmony_ci	old_type = USB_ENDPOINT_XFER_CONTROL;
141062306a36Sopenharmony_ci	for (i = 0; i < pipe_size; i++) {
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		/*
141362306a36Sopenharmony_ci		 * data "output" will be finished as soon as possible,
141462306a36Sopenharmony_ci		 * but there is no guaranty at data "input" case.
141562306a36Sopenharmony_ci		 *
141662306a36Sopenharmony_ci		 * "input" needs "standby" pipe.
141762306a36Sopenharmony_ci		 * So, "input" direction pipe > "output" direction pipe
141862306a36Sopenharmony_ci		 * is good idea.
141962306a36Sopenharmony_ci		 *
142062306a36Sopenharmony_ci		 * 1st USB_ENDPOINT_XFER_xxx will be output direction,
142162306a36Sopenharmony_ci		 * and the other will be input direction here.
142262306a36Sopenharmony_ci		 *
142362306a36Sopenharmony_ci		 * ex)
142462306a36Sopenharmony_ci		 * ...
142562306a36Sopenharmony_ci		 * USB_ENDPOINT_XFER_ISOC -> dir out
142662306a36Sopenharmony_ci		 * USB_ENDPOINT_XFER_ISOC -> dir in
142762306a36Sopenharmony_ci		 * USB_ENDPOINT_XFER_BULK -> dir out
142862306a36Sopenharmony_ci		 * USB_ENDPOINT_XFER_BULK -> dir in
142962306a36Sopenharmony_ci		 * USB_ENDPOINT_XFER_BULK -> dir in
143062306a36Sopenharmony_ci		 * ...
143162306a36Sopenharmony_ci		 */
143262306a36Sopenharmony_ci		dir_in = (pipe_configs[i].type == old_type);
143362306a36Sopenharmony_ci		old_type = pipe_configs[i].type;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci		if (USB_ENDPOINT_XFER_CONTROL == pipe_configs[i].type) {
143662306a36Sopenharmony_ci			pipe = usbhs_dcp_malloc(priv);
143762306a36Sopenharmony_ci			usbhsh_hpriv_to_dcp(hpriv) = pipe;
143862306a36Sopenharmony_ci		} else {
143962306a36Sopenharmony_ci			pipe = usbhs_pipe_malloc(priv,
144062306a36Sopenharmony_ci						 pipe_configs[i].type,
144162306a36Sopenharmony_ci						 dir_in);
144262306a36Sopenharmony_ci		}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci		pipe->mod_private = NULL;
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic int usbhsh_start(struct usbhs_priv *priv)
144962306a36Sopenharmony_ci{
145062306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
145162306a36Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
145262306a36Sopenharmony_ci	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
145362306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
145462306a36Sopenharmony_ci	int ret;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* add hcd */
145762306a36Sopenharmony_ci	ret = usb_add_hcd(hcd, 0, 0);
145862306a36Sopenharmony_ci	if (ret < 0)
145962306a36Sopenharmony_ci		return 0;
146062306a36Sopenharmony_ci	device_wakeup_enable(hcd->self.controller);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/*
146362306a36Sopenharmony_ci	 * pipe initialize and enable DCP
146462306a36Sopenharmony_ci	 */
146562306a36Sopenharmony_ci	usbhs_fifo_init(priv);
146662306a36Sopenharmony_ci	usbhs_pipe_init(priv,
146762306a36Sopenharmony_ci			usbhsh_dma_map_ctrl);
146862306a36Sopenharmony_ci	usbhsh_pipe_init_for_host(priv);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/*
147162306a36Sopenharmony_ci	 * system config enble
147262306a36Sopenharmony_ci	 * - HI speed
147362306a36Sopenharmony_ci	 * - host
147462306a36Sopenharmony_ci	 * - usb module
147562306a36Sopenharmony_ci	 */
147662306a36Sopenharmony_ci	usbhs_sys_host_ctrl(priv, 1);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/*
147962306a36Sopenharmony_ci	 * enable irq callback
148062306a36Sopenharmony_ci	 */
148162306a36Sopenharmony_ci	mod->irq_attch		= usbhsh_irq_attch;
148262306a36Sopenharmony_ci	mod->irq_dtch		= usbhsh_irq_dtch;
148362306a36Sopenharmony_ci	mod->irq_sack		= usbhsh_irq_setup_ack;
148462306a36Sopenharmony_ci	mod->irq_sign		= usbhsh_irq_setup_err;
148562306a36Sopenharmony_ci	usbhs_irq_callback_update(priv, mod);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	dev_dbg(dev, "start host\n");
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return ret;
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic int usbhsh_stop(struct usbhs_priv *priv)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
149562306a36Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
149662306a36Sopenharmony_ci	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
149762306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	/*
150062306a36Sopenharmony_ci	 * disable irq callback
150162306a36Sopenharmony_ci	 */
150262306a36Sopenharmony_ci	mod->irq_attch	= NULL;
150362306a36Sopenharmony_ci	mod->irq_dtch	= NULL;
150462306a36Sopenharmony_ci	mod->irq_sack	= NULL;
150562306a36Sopenharmony_ci	mod->irq_sign	= NULL;
150662306a36Sopenharmony_ci	usbhs_irq_callback_update(priv, mod);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	usb_remove_hcd(hcd);
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	/* disable sys */
151162306a36Sopenharmony_ci	usbhs_sys_host_ctrl(priv, 0);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	dev_dbg(dev, "quit host\n");
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return 0;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ciint usbhs_mod_host_probe(struct usbhs_priv *priv)
151962306a36Sopenharmony_ci{
152062306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv;
152162306a36Sopenharmony_ci	struct usb_hcd *hcd;
152262306a36Sopenharmony_ci	struct usbhsh_device *udev;
152362306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
152462306a36Sopenharmony_ci	int i;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	/* initialize hcd */
152762306a36Sopenharmony_ci	hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name);
152862306a36Sopenharmony_ci	if (!hcd) {
152962306a36Sopenharmony_ci		dev_err(dev, "Failed to create hcd\n");
153062306a36Sopenharmony_ci		return -ENOMEM;
153162306a36Sopenharmony_ci	}
153262306a36Sopenharmony_ci	hcd->has_tt = 1; /* for low/full speed */
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	/*
153562306a36Sopenharmony_ci	 * CAUTION
153662306a36Sopenharmony_ci	 *
153762306a36Sopenharmony_ci	 * There is no guarantee that it is possible to access usb module here.
153862306a36Sopenharmony_ci	 * Don't accesses to it.
153962306a36Sopenharmony_ci	 * The accesse will be enable after "usbhsh_start"
154062306a36Sopenharmony_ci	 */
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	hpriv = usbhsh_hcd_to_hpriv(hcd);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	/*
154562306a36Sopenharmony_ci	 * register itself
154662306a36Sopenharmony_ci	 */
154762306a36Sopenharmony_ci	usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* init hpriv */
155062306a36Sopenharmony_ci	hpriv->mod.name		= "host";
155162306a36Sopenharmony_ci	hpriv->mod.start	= usbhsh_start;
155262306a36Sopenharmony_ci	hpriv->mod.stop		= usbhsh_stop;
155362306a36Sopenharmony_ci	usbhsh_port_stat_init(hpriv);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/* init all device */
155662306a36Sopenharmony_ci	usbhsh_for_each_udev_with_dev0(udev, hpriv, i) {
155762306a36Sopenharmony_ci		udev->usbv	= NULL;
155862306a36Sopenharmony_ci		INIT_LIST_HEAD(&udev->ep_list_head);
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	dev_info(dev, "host probed\n");
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	return 0;
156462306a36Sopenharmony_ci}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ciint usbhs_mod_host_remove(struct usbhs_priv *priv)
156762306a36Sopenharmony_ci{
156862306a36Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
156962306a36Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	usb_put_hcd(hcd);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	return 0;
157462306a36Sopenharmony_ci}
1575