18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Renesas USB driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Renesas Solutions Corp.
68c2ecf20Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/list.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/usb.h>
148c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h>
158c2ecf20Sopenharmony_ci#include "common.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci *** HARDWARE LIMITATION ***
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * 1) renesas_usbhs has a limited number of controllable devices.
218c2ecf20Sopenharmony_ci *    it can control only 9 devices in generally.
228c2ecf20Sopenharmony_ci *	see DEVADDn / DCPMAXP / PIPEMAXP.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * 2) renesas_usbhs pipe number is limited.
258c2ecf20Sopenharmony_ci *    the pipe will be re-used for each devices.
268c2ecf20Sopenharmony_ci *    so, software should control DATA0/1 sequence of each devices.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci *		image of mod_host
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * +--------+
348c2ecf20Sopenharmony_ci * | udev 0 | --> it is used when set address
358c2ecf20Sopenharmony_ci * +--------+
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * +--------+					pipes are reused for each uep.
388c2ecf20Sopenharmony_ci * | udev 1 |-+- [uep 0 (dcp) ] --+		pipe will be switched when
398c2ecf20Sopenharmony_ci * +--------+ |			  |		other device requested
408c2ecf20Sopenharmony_ci *	      +- [uep 1 (bulk)]	--|---+		   +--------------+
418c2ecf20Sopenharmony_ci *	      |			  +--------------> | pipe0 (dcp)  |
428c2ecf20Sopenharmony_ci *	      +- [uep 2 (bulk)]	-@    |		   +--------------+
438c2ecf20Sopenharmony_ci *				      |		   | pipe1 (isoc) |
448c2ecf20Sopenharmony_ci * +--------+			      |		   +--------------+
458c2ecf20Sopenharmony_ci * | udev 2 |-+- [uep 0 (dcp) ]	-@    +----------> | pipe2 (bulk) |
468c2ecf20Sopenharmony_ci * +--------+ |					   +--------------+
478c2ecf20Sopenharmony_ci *	      +- [uep 1 (int) ]	----+	  +------> | pipe3 (bulk) |
488c2ecf20Sopenharmony_ci *				    |	  |	   +--------------+
498c2ecf20Sopenharmony_ci * +--------+			    +-----|------> | pipe4 (int)  |
508c2ecf20Sopenharmony_ci * | udev 3 |-+- [uep 0 (dcp) ]	-@	  |	   +--------------+
518c2ecf20Sopenharmony_ci * +--------+ |				  |	   | ....	  |
528c2ecf20Sopenharmony_ci *	      +- [uep 1 (bulk)]	-@	  |	   | ....	  |
538c2ecf20Sopenharmony_ci *	      |				  |
548c2ecf20Sopenharmony_ci *	      +- [uep 2 (bulk)]-----------+
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * @ :	uep requested free pipe, but all have been used.
578c2ecf20Sopenharmony_ci *	now it is waiting for free pipe
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci *		struct
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistruct usbhsh_request {
658c2ecf20Sopenharmony_ci	struct urb		*urb;
668c2ecf20Sopenharmony_ci	struct usbhs_pkt	pkt;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct usbhsh_device {
708c2ecf20Sopenharmony_ci	struct usb_device	*usbv;
718c2ecf20Sopenharmony_ci	struct list_head	ep_list_head; /* list of usbhsh_ep */
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct usbhsh_ep {
758c2ecf20Sopenharmony_ci	struct usbhs_pipe	*pipe;   /* attached pipe */
768c2ecf20Sopenharmony_ci	struct usbhsh_device	*udev;   /* attached udev */
778c2ecf20Sopenharmony_ci	struct usb_host_endpoint *ep;
788c2ecf20Sopenharmony_ci	struct list_head	ep_list; /* list to usbhsh_device */
798c2ecf20Sopenharmony_ci	unsigned int		counter; /* pipe attach counter */
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define USBHSH_DEVICE_MAX	10 /* see DEVADDn / DCPMAXP / PIPEMAXP */
838c2ecf20Sopenharmony_ci#define USBHSH_PORT_MAX		 7 /* see DEVADDn :: HUBPORT */
848c2ecf20Sopenharmony_cistruct usbhsh_hpriv {
858c2ecf20Sopenharmony_ci	struct usbhs_mod	mod;
868c2ecf20Sopenharmony_ci	struct usbhs_pipe	*dcp;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	struct usbhsh_device	udev[USBHSH_DEVICE_MAX];
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	u32	port_stat;	/* USB_PORT_STAT_xxx */
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	struct completion	setup_ack_done;
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic const char usbhsh_hcd_name[] = "renesas_usbhs host";
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci *		macro
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci#define usbhsh_priv_to_hpriv(priv) \
1028c2ecf20Sopenharmony_ci	container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod)
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define __usbhsh_for_each_udev(start, pos, h, i)	\
1058c2ecf20Sopenharmony_ci	for ((i) = start;						\
1068c2ecf20Sopenharmony_ci	     ((i) < USBHSH_DEVICE_MAX) && ((pos) = (h)->udev + (i));	\
1078c2ecf20Sopenharmony_ci	     (i)++)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#define usbhsh_for_each_udev(pos, hpriv, i)	\
1108c2ecf20Sopenharmony_ci	__usbhsh_for_each_udev(1, pos, hpriv, i)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i)	\
1138c2ecf20Sopenharmony_ci	__usbhsh_for_each_udev(0, pos, hpriv, i)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define usbhsh_hcd_to_hpriv(h)	(struct usbhsh_hpriv *)((h)->hcd_priv)
1168c2ecf20Sopenharmony_ci#define usbhsh_hcd_to_dev(h)	((h)->self.controller)
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define usbhsh_hpriv_to_priv(h)	((h)->mod.priv)
1198c2ecf20Sopenharmony_ci#define usbhsh_hpriv_to_dcp(h)	((h)->dcp)
1208c2ecf20Sopenharmony_ci#define usbhsh_hpriv_to_hcd(h)	\
1218c2ecf20Sopenharmony_ci	container_of((void *)h, struct usb_hcd, hcd_priv)
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define usbhsh_ep_to_uep(u)	((u)->hcpriv)
1248c2ecf20Sopenharmony_ci#define usbhsh_uep_to_pipe(u)	((u)->pipe)
1258c2ecf20Sopenharmony_ci#define usbhsh_uep_to_udev(u)	((u)->udev)
1268c2ecf20Sopenharmony_ci#define usbhsh_uep_to_ep(u)	((u)->ep)
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#define usbhsh_urb_to_ureq(u)	((u)->hcpriv)
1298c2ecf20Sopenharmony_ci#define usbhsh_urb_to_usbv(u)	((u)->dev)
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#define usbhsh_usbv_to_udev(d)	dev_get_drvdata(&(d)->dev)
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#define usbhsh_udev_to_usbv(h)	((h)->usbv)
1348c2ecf20Sopenharmony_ci#define usbhsh_udev_is_used(h)	usbhsh_udev_to_usbv(h)
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci#define usbhsh_pipe_to_uep(p)	((p)->mod_private)
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci#define usbhsh_device_parent(d)		(usbhsh_usbv_to_udev((d)->usbv->parent))
1398c2ecf20Sopenharmony_ci#define usbhsh_device_hubport(d)	((d)->usbv->portnum)
1408c2ecf20Sopenharmony_ci#define usbhsh_device_number(h, d)	((int)((d) - (h)->udev))
1418c2ecf20Sopenharmony_ci#define usbhsh_device_nth(h, d)		((h)->udev + d)
1428c2ecf20Sopenharmony_ci#define usbhsh_device0(h)		usbhsh_device_nth(h, 0)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#define usbhsh_port_stat_init(h)	((h)->port_stat = 0)
1458c2ecf20Sopenharmony_ci#define usbhsh_port_stat_set(h, s)	((h)->port_stat |= (s))
1468c2ecf20Sopenharmony_ci#define usbhsh_port_stat_clear(h, s)	((h)->port_stat &= ~(s))
1478c2ecf20Sopenharmony_ci#define usbhsh_port_stat_get(h)		((h)->port_stat)
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci#define usbhsh_pkt_to_ureq(p)	\
1508c2ecf20Sopenharmony_ci	container_of((void *)p, struct usbhsh_request, pkt)
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci *		req alloc/free
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv,
1568c2ecf20Sopenharmony_ci					       struct urb *urb,
1578c2ecf20Sopenharmony_ci					       gfp_t mem_flags)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags);
1628c2ecf20Sopenharmony_ci	if (!ureq)
1638c2ecf20Sopenharmony_ci		return NULL;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	usbhs_pkt_init(&ureq->pkt);
1668c2ecf20Sopenharmony_ci	ureq->urb = urb;
1678c2ecf20Sopenharmony_ci	usbhsh_urb_to_ureq(urb) = ureq;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return ureq;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
1738c2ecf20Sopenharmony_ci			    struct usbhsh_request *ureq)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	usbhsh_urb_to_ureq(ureq->urb) = NULL;
1768c2ecf20Sopenharmony_ci	ureq->urb = NULL;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	kfree(ureq);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/*
1828c2ecf20Sopenharmony_ci *		status
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistatic int usbhsh_is_running(struct usbhsh_hpriv *hpriv)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	/*
1878c2ecf20Sopenharmony_ci	 * we can decide some device is attached or not
1888c2ecf20Sopenharmony_ci	 * by checking mod.irq_attch
1898c2ecf20Sopenharmony_ci	 * see
1908c2ecf20Sopenharmony_ci	 *	usbhsh_irq_attch()
1918c2ecf20Sopenharmony_ci	 *	usbhsh_irq_dtch()
1928c2ecf20Sopenharmony_ci	 */
1938c2ecf20Sopenharmony_ci	return (hpriv->mod.irq_attch == NULL);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/*
1978c2ecf20Sopenharmony_ci *		pipe control
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_cistatic void usbhsh_endpoint_sequence_save(struct usbhsh_hpriv *hpriv,
2008c2ecf20Sopenharmony_ci					  struct urb *urb,
2018c2ecf20Sopenharmony_ci					  struct usbhs_pkt *pkt)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	int len = urb->actual_length;
2048c2ecf20Sopenharmony_ci	int maxp = usb_endpoint_maxp(&urb->ep->desc);
2058c2ecf20Sopenharmony_ci	int t = 0;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* DCP is out of sequence control */
2088c2ecf20Sopenharmony_ci	if (usb_pipecontrol(urb->pipe))
2098c2ecf20Sopenharmony_ci		return;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/*
2128c2ecf20Sopenharmony_ci	 * renesas_usbhs pipe has a limitation in a number.
2138c2ecf20Sopenharmony_ci	 * So, driver should re-use the limited pipe for each device/endpoint.
2148c2ecf20Sopenharmony_ci	 * DATA0/1 sequence should be saved for it.
2158c2ecf20Sopenharmony_ci	 * see [image of mod_host]
2168c2ecf20Sopenharmony_ci	 *     [HARDWARE LIMITATION]
2178c2ecf20Sopenharmony_ci	 */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/*
2208c2ecf20Sopenharmony_ci	 * next sequence depends on actual_length
2218c2ecf20Sopenharmony_ci	 *
2228c2ecf20Sopenharmony_ci	 * ex) actual_length = 1147, maxp = 512
2238c2ecf20Sopenharmony_ci	 * data0 : 512
2248c2ecf20Sopenharmony_ci	 * data1 : 512
2258c2ecf20Sopenharmony_ci	 * data0 : 123
2268c2ecf20Sopenharmony_ci	 * data1 is the next sequence
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	t = len / maxp;
2298c2ecf20Sopenharmony_ci	if (len % maxp)
2308c2ecf20Sopenharmony_ci		t++;
2318c2ecf20Sopenharmony_ci	if (pkt->zero)
2328c2ecf20Sopenharmony_ci		t++;
2338c2ecf20Sopenharmony_ci	t %= 2;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (t)
2368c2ecf20Sopenharmony_ci		usb_dotoggle(urb->dev,
2378c2ecf20Sopenharmony_ci			     usb_pipeendpoint(urb->pipe),
2388c2ecf20Sopenharmony_ci			     usb_pipeout(urb->pipe));
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
2428c2ecf20Sopenharmony_ci					       struct urb *urb);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv,
2458c2ecf20Sopenharmony_ci			      struct urb *urb)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
2488c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
2498c2ecf20Sopenharmony_ci	struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
2508c2ecf20Sopenharmony_ci	struct usbhs_pipe *pipe;
2518c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *desc = &urb->ep->desc;
2528c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
2538c2ecf20Sopenharmony_ci	unsigned long flags;
2548c2ecf20Sopenharmony_ci	int dir_in_req = !!usb_pipein(urb->pipe);
2558c2ecf20Sopenharmony_ci	int is_dcp = usb_endpoint_xfer_control(desc);
2568c2ecf20Sopenharmony_ci	int i, dir_in;
2578c2ecf20Sopenharmony_ci	int ret = -EBUSY;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/********************  spin lock ********************/
2608c2ecf20Sopenharmony_ci	usbhs_lock(priv, flags);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * if uep has been attached to pipe,
2648c2ecf20Sopenharmony_ci	 * reuse it
2658c2ecf20Sopenharmony_ci	 */
2668c2ecf20Sopenharmony_ci	if (usbhsh_uep_to_pipe(uep)) {
2678c2ecf20Sopenharmony_ci		ret = 0;
2688c2ecf20Sopenharmony_ci		goto usbhsh_pipe_attach_done;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		/* check pipe type */
2748c2ecf20Sopenharmony_ci		if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
2758c2ecf20Sopenharmony_ci			continue;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		/* check pipe direction if normal pipe */
2788c2ecf20Sopenharmony_ci		if (!is_dcp) {
2798c2ecf20Sopenharmony_ci			dir_in = !!usbhs_pipe_is_dir_in(pipe);
2808c2ecf20Sopenharmony_ci			if (0 != (dir_in - dir_in_req))
2818c2ecf20Sopenharmony_ci				continue;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		/* check pipe is free */
2858c2ecf20Sopenharmony_ci		if (usbhsh_pipe_to_uep(pipe))
2868c2ecf20Sopenharmony_ci			continue;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		/*
2898c2ecf20Sopenharmony_ci		 * attach pipe to uep
2908c2ecf20Sopenharmony_ci		 *
2918c2ecf20Sopenharmony_ci		 * usbhs_pipe_config_update() should be called after
2928c2ecf20Sopenharmony_ci		 * usbhs_set_device_config()
2938c2ecf20Sopenharmony_ci		 * see
2948c2ecf20Sopenharmony_ci		 *  DCPMAXP/PIPEMAXP
2958c2ecf20Sopenharmony_ci		 */
2968c2ecf20Sopenharmony_ci		usbhsh_uep_to_pipe(uep)		= pipe;
2978c2ecf20Sopenharmony_ci		usbhsh_pipe_to_uep(pipe)	= uep;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		usbhs_pipe_config_update(pipe,
3008c2ecf20Sopenharmony_ci					 usbhsh_device_number(hpriv, udev),
3018c2ecf20Sopenharmony_ci					 usb_endpoint_num(desc),
3028c2ecf20Sopenharmony_ci					 usb_endpoint_maxp(desc));
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s [%d-%d(%s:%s)]\n", __func__,
3058c2ecf20Sopenharmony_ci			usbhsh_device_number(hpriv, udev),
3068c2ecf20Sopenharmony_ci			usb_endpoint_num(desc),
3078c2ecf20Sopenharmony_ci			usbhs_pipe_name(pipe),
3088c2ecf20Sopenharmony_ci			dir_in_req ? "in" : "out");
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		ret = 0;
3118c2ecf20Sopenharmony_ci		break;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ciusbhsh_pipe_attach_done:
3158c2ecf20Sopenharmony_ci	if (0 == ret)
3168c2ecf20Sopenharmony_ci		uep->counter++;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	usbhs_unlock(priv, flags);
3198c2ecf20Sopenharmony_ci	/********************  spin unlock ******************/
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return ret;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic void usbhsh_pipe_detach(struct usbhsh_hpriv *hpriv,
3258c2ecf20Sopenharmony_ci			       struct usbhsh_ep *uep)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
3288c2ecf20Sopenharmony_ci	struct usbhs_pipe *pipe;
3298c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
3308c2ecf20Sopenharmony_ci	unsigned long flags;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (unlikely(!uep)) {
3338c2ecf20Sopenharmony_ci		dev_err(dev, "no uep\n");
3348c2ecf20Sopenharmony_ci		return;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/********************  spin lock ********************/
3388c2ecf20Sopenharmony_ci	usbhs_lock(priv, flags);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	pipe = usbhsh_uep_to_pipe(uep);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (unlikely(!pipe)) {
3438c2ecf20Sopenharmony_ci		dev_err(dev, "uep doesn't have pipe\n");
3448c2ecf20Sopenharmony_ci	} else if (1 == uep->counter--) { /* last user */
3458c2ecf20Sopenharmony_ci		struct usb_host_endpoint *ep = usbhsh_uep_to_ep(uep);
3468c2ecf20Sopenharmony_ci		struct usbhsh_device *udev = usbhsh_uep_to_udev(uep);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		/* detach pipe from uep */
3498c2ecf20Sopenharmony_ci		usbhsh_uep_to_pipe(uep)		= NULL;
3508c2ecf20Sopenharmony_ci		usbhsh_pipe_to_uep(pipe)	= NULL;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s [%d-%d(%s)]\n", __func__,
3538c2ecf20Sopenharmony_ci			usbhsh_device_number(hpriv, udev),
3548c2ecf20Sopenharmony_ci			usb_endpoint_num(&ep->desc),
3558c2ecf20Sopenharmony_ci			usbhs_pipe_name(pipe));
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	usbhs_unlock(priv, flags);
3598c2ecf20Sopenharmony_ci	/********************  spin unlock ******************/
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/*
3638c2ecf20Sopenharmony_ci *		endpoint control
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_cistatic int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
3668c2ecf20Sopenharmony_ci				  struct urb *urb,
3678c2ecf20Sopenharmony_ci				  gfp_t mem_flags)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
3708c2ecf20Sopenharmony_ci	struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
3718c2ecf20Sopenharmony_ci	struct usb_host_endpoint *ep = urb->ep;
3728c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep;
3738c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
3748c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *desc = &ep->desc;
3758c2ecf20Sopenharmony_ci	unsigned long flags;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
3788c2ecf20Sopenharmony_ci	if (!uep)
3798c2ecf20Sopenharmony_ci		return -ENOMEM;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/********************  spin lock ********************/
3828c2ecf20Sopenharmony_ci	usbhs_lock(priv, flags);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/*
3858c2ecf20Sopenharmony_ci	 * init endpoint
3868c2ecf20Sopenharmony_ci	 */
3878c2ecf20Sopenharmony_ci	uep->counter = 0;
3888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&uep->ep_list);
3898c2ecf20Sopenharmony_ci	list_add_tail(&uep->ep_list, &udev->ep_list_head);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	usbhsh_uep_to_udev(uep)	= udev;
3928c2ecf20Sopenharmony_ci	usbhsh_uep_to_ep(uep)	= ep;
3938c2ecf20Sopenharmony_ci	usbhsh_ep_to_uep(ep)	= uep;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	usbhs_unlock(priv, flags);
3968c2ecf20Sopenharmony_ci	/********************  spin unlock ******************/
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s [%d-%d]\n", __func__,
3998c2ecf20Sopenharmony_ci		usbhsh_device_number(hpriv, udev),
4008c2ecf20Sopenharmony_ci		usb_endpoint_num(desc));
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return 0;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic void usbhsh_endpoint_detach(struct usbhsh_hpriv *hpriv,
4068c2ecf20Sopenharmony_ci				   struct usb_host_endpoint *ep)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
4098c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
4108c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
4118c2ecf20Sopenharmony_ci	unsigned long flags;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (!uep)
4148c2ecf20Sopenharmony_ci		return;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s [%d-%d]\n", __func__,
4178c2ecf20Sopenharmony_ci		usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
4188c2ecf20Sopenharmony_ci		usb_endpoint_num(&ep->desc));
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (usbhsh_uep_to_pipe(uep))
4218c2ecf20Sopenharmony_ci		usbhsh_pipe_detach(hpriv, uep);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/********************  spin lock ********************/
4248c2ecf20Sopenharmony_ci	usbhs_lock(priv, flags);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* remove this endpoint from udev */
4278c2ecf20Sopenharmony_ci	list_del_init(&uep->ep_list);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	usbhsh_uep_to_udev(uep)	= NULL;
4308c2ecf20Sopenharmony_ci	usbhsh_uep_to_ep(uep)	= NULL;
4318c2ecf20Sopenharmony_ci	usbhsh_ep_to_uep(ep)	= NULL;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	usbhs_unlock(priv, flags);
4348c2ecf20Sopenharmony_ci	/********************  spin unlock ******************/
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	kfree(uep);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic void usbhsh_endpoint_detach_all(struct usbhsh_hpriv *hpriv,
4408c2ecf20Sopenharmony_ci				       struct usbhsh_device *udev)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep, *next;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	list_for_each_entry_safe(uep, next, &udev->ep_list_head, ep_list)
4458c2ecf20Sopenharmony_ci		usbhsh_endpoint_detach(hpriv, usbhsh_uep_to_ep(uep));
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci/*
4498c2ecf20Sopenharmony_ci *		device control
4508c2ecf20Sopenharmony_ci */
4518c2ecf20Sopenharmony_cistatic int usbhsh_connected_to_rhdev(struct usb_hcd *hcd,
4528c2ecf20Sopenharmony_ci				     struct usbhsh_device *udev)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return hcd->self.root_hub == usbv->parent;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic int usbhsh_device_has_endpoint(struct usbhsh_device *udev)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	return !list_empty(&udev->ep_list_head);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
4658c2ecf20Sopenharmony_ci					       struct urb *urb)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
4688c2ecf20Sopenharmony_ci	struct usbhsh_device *udev = usbhsh_usbv_to_udev(usbv);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* usbhsh_device_attach() is still not called */
4718c2ecf20Sopenharmony_ci	if (!udev)
4728c2ecf20Sopenharmony_ci		return NULL;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* if it is device0, return it */
4758c2ecf20Sopenharmony_ci	if (0 == usb_pipedevice(urb->pipe))
4768c2ecf20Sopenharmony_ci		return usbhsh_device0(hpriv);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* return attached device */
4798c2ecf20Sopenharmony_ci	return udev;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic struct usbhsh_device *usbhsh_device_attach(struct usbhsh_hpriv *hpriv,
4838c2ecf20Sopenharmony_ci						 struct urb *urb)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct usbhsh_device *udev = NULL;
4868c2ecf20Sopenharmony_ci	struct usbhsh_device *udev0 = usbhsh_device0(hpriv);
4878c2ecf20Sopenharmony_ci	struct usbhsh_device *pos;
4888c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
4898c2ecf20Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
4908c2ecf20Sopenharmony_ci	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
4918c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
4928c2ecf20Sopenharmony_ci	unsigned long flags;
4938c2ecf20Sopenharmony_ci	u16 upphub, hubport;
4948c2ecf20Sopenharmony_ci	int i;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/*
4978c2ecf20Sopenharmony_ci	 * This function should be called only while urb is pointing to device0.
4988c2ecf20Sopenharmony_ci	 * It will attach unused usbhsh_device to urb (usbv),
4998c2ecf20Sopenharmony_ci	 * and initialize device0.
5008c2ecf20Sopenharmony_ci	 * You can use usbhsh_device_get() to get "current" udev,
5018c2ecf20Sopenharmony_ci	 * and usbhsh_usbv_to_udev() is for "attached" udev.
5028c2ecf20Sopenharmony_ci	 */
5038c2ecf20Sopenharmony_ci	if (0 != usb_pipedevice(urb->pipe)) {
5048c2ecf20Sopenharmony_ci		dev_err(dev, "%s fail: urb isn't pointing device0\n", __func__);
5058c2ecf20Sopenharmony_ci		return NULL;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/********************  spin lock ********************/
5098c2ecf20Sopenharmony_ci	usbhs_lock(priv, flags);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/*
5128c2ecf20Sopenharmony_ci	 * find unused device
5138c2ecf20Sopenharmony_ci	 */
5148c2ecf20Sopenharmony_ci	usbhsh_for_each_udev(pos, hpriv, i) {
5158c2ecf20Sopenharmony_ci		if (usbhsh_udev_is_used(pos))
5168c2ecf20Sopenharmony_ci			continue;
5178c2ecf20Sopenharmony_ci		udev = pos;
5188c2ecf20Sopenharmony_ci		break;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (udev) {
5228c2ecf20Sopenharmony_ci		/*
5238c2ecf20Sopenharmony_ci		 * usbhsh_usbv_to_udev()
5248c2ecf20Sopenharmony_ci		 * usbhsh_udev_to_usbv()
5258c2ecf20Sopenharmony_ci		 * will be enable
5268c2ecf20Sopenharmony_ci		 */
5278c2ecf20Sopenharmony_ci		dev_set_drvdata(&usbv->dev, udev);
5288c2ecf20Sopenharmony_ci		udev->usbv = usbv;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	usbhs_unlock(priv, flags);
5328c2ecf20Sopenharmony_ci	/********************  spin unlock ******************/
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (!udev) {
5358c2ecf20Sopenharmony_ci		dev_err(dev, "no free usbhsh_device\n");
5368c2ecf20Sopenharmony_ci		return NULL;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (usbhsh_device_has_endpoint(udev)) {
5408c2ecf20Sopenharmony_ci		dev_warn(dev, "udev have old endpoint\n");
5418c2ecf20Sopenharmony_ci		usbhsh_endpoint_detach_all(hpriv, udev);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (usbhsh_device_has_endpoint(udev0)) {
5458c2ecf20Sopenharmony_ci		dev_warn(dev, "udev0 have old endpoint\n");
5468c2ecf20Sopenharmony_ci		usbhsh_endpoint_detach_all(hpriv, udev0);
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/* uep will be attached */
5508c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&udev0->ep_list_head);
5518c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&udev->ep_list_head);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	/*
5548c2ecf20Sopenharmony_ci	 * set device0 config
5558c2ecf20Sopenharmony_ci	 */
5568c2ecf20Sopenharmony_ci	usbhs_set_device_config(priv,
5578c2ecf20Sopenharmony_ci				0, 0, 0, usbv->speed);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/*
5608c2ecf20Sopenharmony_ci	 * set new device config
5618c2ecf20Sopenharmony_ci	 */
5628c2ecf20Sopenharmony_ci	upphub	= 0;
5638c2ecf20Sopenharmony_ci	hubport	= 0;
5648c2ecf20Sopenharmony_ci	if (!usbhsh_connected_to_rhdev(hcd, udev)) {
5658c2ecf20Sopenharmony_ci		/* if udev is not connected to rhdev, it means parent is Hub */
5668c2ecf20Sopenharmony_ci		struct usbhsh_device *parent = usbhsh_device_parent(udev);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		upphub	= usbhsh_device_number(hpriv, parent);
5698c2ecf20Sopenharmony_ci		hubport	= usbhsh_device_hubport(udev);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s connected to Hub [%d:%d](%p)\n", __func__,
5728c2ecf20Sopenharmony_ci			upphub, hubport, parent);
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	usbhs_set_device_config(priv,
5768c2ecf20Sopenharmony_ci			       usbhsh_device_number(hpriv, udev),
5778c2ecf20Sopenharmony_ci			       upphub, hubport, usbv->speed);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s [%d](%p)\n", __func__,
5808c2ecf20Sopenharmony_ci		usbhsh_device_number(hpriv, udev), udev);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	return udev;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic void usbhsh_device_detach(struct usbhsh_hpriv *hpriv,
5868c2ecf20Sopenharmony_ci			       struct usbhsh_device *udev)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
5898c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
5908c2ecf20Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
5918c2ecf20Sopenharmony_ci	struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
5928c2ecf20Sopenharmony_ci	unsigned long flags;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s [%d](%p)\n", __func__,
5958c2ecf20Sopenharmony_ci		usbhsh_device_number(hpriv, udev), udev);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (usbhsh_device_has_endpoint(udev)) {
5988c2ecf20Sopenharmony_ci		dev_warn(dev, "udev still have endpoint\n");
5998c2ecf20Sopenharmony_ci		usbhsh_endpoint_detach_all(hpriv, udev);
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/*
6038c2ecf20Sopenharmony_ci	 * There is nothing to do if it is device0.
6048c2ecf20Sopenharmony_ci	 * see
6058c2ecf20Sopenharmony_ci	 *  usbhsh_device_attach()
6068c2ecf20Sopenharmony_ci	 *  usbhsh_device_get()
6078c2ecf20Sopenharmony_ci	 */
6088c2ecf20Sopenharmony_ci	if (0 == usbhsh_device_number(hpriv, udev))
6098c2ecf20Sopenharmony_ci		return;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/********************  spin lock ********************/
6128c2ecf20Sopenharmony_ci	usbhs_lock(priv, flags);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/*
6158c2ecf20Sopenharmony_ci	 * usbhsh_usbv_to_udev()
6168c2ecf20Sopenharmony_ci	 * usbhsh_udev_to_usbv()
6178c2ecf20Sopenharmony_ci	 * will be disable
6188c2ecf20Sopenharmony_ci	 */
6198c2ecf20Sopenharmony_ci	dev_set_drvdata(&usbv->dev, NULL);
6208c2ecf20Sopenharmony_ci	udev->usbv = NULL;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	usbhs_unlock(priv, flags);
6238c2ecf20Sopenharmony_ci	/********************  spin unlock ******************/
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci/*
6278c2ecf20Sopenharmony_ci *		queue push/pop
6288c2ecf20Sopenharmony_ci */
6298c2ecf20Sopenharmony_cistatic void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
6328c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
6338c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
6348c2ecf20Sopenharmony_ci	struct urb *urb = ureq->urb;
6358c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
6368c2ecf20Sopenharmony_ci	int status = 0;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (!urb) {
6418c2ecf20Sopenharmony_ci		dev_warn(dev, "pkt doesn't have urb\n");
6428c2ecf20Sopenharmony_ci		return;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (!usbhsh_is_running(hpriv))
6468c2ecf20Sopenharmony_ci		status = -ESHUTDOWN;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	urb->actual_length = pkt->actual;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	usbhsh_endpoint_sequence_save(hpriv, urb, pkt);
6518c2ecf20Sopenharmony_ci	usbhsh_ureq_free(hpriv, ureq);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep));
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	usb_hcd_unlink_urb_from_ep(hcd, urb);
6568c2ecf20Sopenharmony_ci	usb_hcd_giveback_urb(hcd, urb, status);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int usbhsh_queue_push(struct usb_hcd *hcd,
6608c2ecf20Sopenharmony_ci			     struct urb *urb,
6618c2ecf20Sopenharmony_ci			     gfp_t mem_flags)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
6648c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
6658c2ecf20Sopenharmony_ci	struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep);
6668c2ecf20Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
6678c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq;
6688c2ecf20Sopenharmony_ci	void *buf;
6698c2ecf20Sopenharmony_ci	int len, sequence;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (usb_pipeisoc(urb->pipe)) {
6728c2ecf20Sopenharmony_ci		dev_err(dev, "pipe iso is not supported now\n");
6738c2ecf20Sopenharmony_ci		return -EIO;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* this ureq will be freed on usbhsh_queue_done() */
6778c2ecf20Sopenharmony_ci	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
6788c2ecf20Sopenharmony_ci	if (unlikely(!ureq)) {
6798c2ecf20Sopenharmony_ci		dev_err(dev, "ureq alloc fail\n");
6808c2ecf20Sopenharmony_ci		return -ENOMEM;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (usb_pipein(urb->pipe))
6848c2ecf20Sopenharmony_ci		pipe->handler = &usbhs_fifo_dma_pop_handler;
6858c2ecf20Sopenharmony_ci	else
6868c2ecf20Sopenharmony_ci		pipe->handler = &usbhs_fifo_dma_push_handler;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	buf = (void *)(urb->transfer_buffer + urb->actual_length);
6898c2ecf20Sopenharmony_ci	len = urb->transfer_buffer_length - urb->actual_length;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	sequence = usb_gettoggle(urb->dev,
6928c2ecf20Sopenharmony_ci				 usb_pipeendpoint(urb->pipe),
6938c2ecf20Sopenharmony_ci				 usb_pipeout(urb->pipe));
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
6968c2ecf20Sopenharmony_ci	usbhs_pkt_push(pipe, &ureq->pkt, usbhsh_queue_done,
6978c2ecf20Sopenharmony_ci		       buf, len, (urb->transfer_flags & URB_ZERO_PACKET),
6988c2ecf20Sopenharmony_ci		       sequence);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	usbhs_pkt_start(pipe);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	return 0;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic void usbhsh_queue_force_pop(struct usbhs_priv *priv,
7068c2ecf20Sopenharmony_ci				   struct usbhs_pipe *pipe)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct usbhs_pkt *pkt;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	while (1) {
7118c2ecf20Sopenharmony_ci		pkt = usbhs_pkt_pop(pipe, NULL);
7128c2ecf20Sopenharmony_ci		if (!pkt)
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci		/*
7168c2ecf20Sopenharmony_ci		 * if all packet are gone, usbhsh_endpoint_disable()
7178c2ecf20Sopenharmony_ci		 * will be called.
7188c2ecf20Sopenharmony_ci		 * then, attached device/endpoint/pipe will be detached
7198c2ecf20Sopenharmony_ci		 */
7208c2ecf20Sopenharmony_ci		usbhsh_queue_done(priv, pkt);
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic void usbhsh_queue_force_pop_all(struct usbhs_priv *priv)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct usbhs_pipe *pos;
7278c2ecf20Sopenharmony_ci	int i;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	usbhs_for_each_pipe_with_dcp(pos, priv, i)
7308c2ecf20Sopenharmony_ci		usbhsh_queue_force_pop(priv, pos);
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci/*
7348c2ecf20Sopenharmony_ci *		DCP setup stage
7358c2ecf20Sopenharmony_ci */
7368c2ecf20Sopenharmony_cistatic int usbhsh_is_request_address(struct urb *urb)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct usb_ctrlrequest *req;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	req = (struct usb_ctrlrequest *)urb->setup_packet;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if ((DeviceOutRequest    == req->bRequestType << 8) &&
7438c2ecf20Sopenharmony_ci	    (USB_REQ_SET_ADDRESS == req->bRequest))
7448c2ecf20Sopenharmony_ci		return 1;
7458c2ecf20Sopenharmony_ci	else
7468c2ecf20Sopenharmony_ci		return 0;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv,
7508c2ecf20Sopenharmony_ci					   struct urb *urb,
7518c2ecf20Sopenharmony_ci					   struct usbhs_pipe *pipe)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
7548c2ecf20Sopenharmony_ci	struct usb_ctrlrequest req;
7558c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/*
7588c2ecf20Sopenharmony_ci	 * wait setup packet ACK
7598c2ecf20Sopenharmony_ci	 * see
7608c2ecf20Sopenharmony_ci	 *	usbhsh_irq_setup_ack()
7618c2ecf20Sopenharmony_ci	 *	usbhsh_irq_setup_err()
7628c2ecf20Sopenharmony_ci	 */
7638c2ecf20Sopenharmony_ci	init_completion(&hpriv->setup_ack_done);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* copy original request */
7668c2ecf20Sopenharmony_ci	memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest));
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/*
7698c2ecf20Sopenharmony_ci	 * renesas_usbhs can not use original usb address.
7708c2ecf20Sopenharmony_ci	 * see HARDWARE LIMITATION.
7718c2ecf20Sopenharmony_ci	 * modify usb address here to use attached device.
7728c2ecf20Sopenharmony_ci	 * see usbhsh_device_attach()
7738c2ecf20Sopenharmony_ci	 */
7748c2ecf20Sopenharmony_ci	if (usbhsh_is_request_address(urb)) {
7758c2ecf20Sopenharmony_ci		struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
7768c2ecf20Sopenharmony_ci		struct usbhsh_device *udev = usbhsh_usbv_to_udev(usbv);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		/* udev is a attached device */
7798c2ecf20Sopenharmony_ci		req.wValue = usbhsh_device_number(hpriv, udev);
7808c2ecf20Sopenharmony_ci		dev_dbg(dev, "create new address - %d\n", req.wValue);
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* set request */
7848c2ecf20Sopenharmony_ci	usbhs_usbreq_set_val(priv, &req);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/*
7878c2ecf20Sopenharmony_ci	 * wait setup packet ACK
7888c2ecf20Sopenharmony_ci	 */
7898c2ecf20Sopenharmony_ci	wait_for_completion(&hpriv->setup_ack_done);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s done\n", __func__);
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci/*
7958c2ecf20Sopenharmony_ci *		DCP data stage
7968c2ecf20Sopenharmony_ci */
7978c2ecf20Sopenharmony_cistatic void usbhsh_data_stage_packet_done(struct usbhs_priv *priv,
7988c2ecf20Sopenharmony_ci					  struct usbhs_pkt *pkt)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
8018c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* this ureq was connected to urb when usbhsh_urb_enqueue()  */
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	usbhsh_ureq_free(hpriv, ureq);
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic int usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv,
8098c2ecf20Sopenharmony_ci					 struct urb *urb,
8108c2ecf20Sopenharmony_ci					 struct usbhs_pipe *pipe,
8118c2ecf20Sopenharmony_ci					 gfp_t mem_flags)
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/* this ureq will be freed on usbhsh_data_stage_packet_done() */
8178c2ecf20Sopenharmony_ci	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
8188c2ecf20Sopenharmony_ci	if (unlikely(!ureq))
8198c2ecf20Sopenharmony_ci		return -ENOMEM;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (usb_pipein(urb->pipe))
8228c2ecf20Sopenharmony_ci		pipe->handler = &usbhs_dcp_data_stage_in_handler;
8238c2ecf20Sopenharmony_ci	else
8248c2ecf20Sopenharmony_ci		pipe->handler = &usbhs_dcp_data_stage_out_handler;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	usbhs_pkt_push(pipe, &ureq->pkt,
8278c2ecf20Sopenharmony_ci		       usbhsh_data_stage_packet_done,
8288c2ecf20Sopenharmony_ci		       urb->transfer_buffer,
8298c2ecf20Sopenharmony_ci		       urb->transfer_buffer_length,
8308c2ecf20Sopenharmony_ci		       (urb->transfer_flags & URB_ZERO_PACKET),
8318c2ecf20Sopenharmony_ci		       -1);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	return 0;
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci/*
8378c2ecf20Sopenharmony_ci *		DCP status stage
8388c2ecf20Sopenharmony_ci */
8398c2ecf20Sopenharmony_cistatic int usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv,
8408c2ecf20Sopenharmony_ci					    struct urb *urb,
8418c2ecf20Sopenharmony_ci					    struct usbhs_pipe *pipe,
8428c2ecf20Sopenharmony_ci					    gfp_t mem_flags)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* This ureq will be freed on usbhsh_queue_done() */
8478c2ecf20Sopenharmony_ci	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
8488c2ecf20Sopenharmony_ci	if (unlikely(!ureq))
8498c2ecf20Sopenharmony_ci		return -ENOMEM;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (usb_pipein(urb->pipe))
8528c2ecf20Sopenharmony_ci		pipe->handler = &usbhs_dcp_status_stage_in_handler;
8538c2ecf20Sopenharmony_ci	else
8548c2ecf20Sopenharmony_ci		pipe->handler = &usbhs_dcp_status_stage_out_handler;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	usbhs_pkt_push(pipe, &ureq->pkt,
8578c2ecf20Sopenharmony_ci		       usbhsh_queue_done,
8588c2ecf20Sopenharmony_ci		       NULL,
8598c2ecf20Sopenharmony_ci		       urb->transfer_buffer_length,
8608c2ecf20Sopenharmony_ci		       0, -1);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	return 0;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic int usbhsh_dcp_queue_push(struct usb_hcd *hcd,
8668c2ecf20Sopenharmony_ci				 struct urb *urb,
8678c2ecf20Sopenharmony_ci				 gfp_t mflags)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
8708c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
8718c2ecf20Sopenharmony_ci	struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep);
8728c2ecf20Sopenharmony_ci	struct device *dev = usbhsh_hcd_to_dev(hcd);
8738c2ecf20Sopenharmony_ci	int ret;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/*
8788c2ecf20Sopenharmony_ci	 * setup stage
8798c2ecf20Sopenharmony_ci	 *
8808c2ecf20Sopenharmony_ci	 * usbhsh_send_setup_stage_packet() wait SACK/SIGN
8818c2ecf20Sopenharmony_ci	 */
8828c2ecf20Sopenharmony_ci	usbhsh_setup_stage_packet_push(hpriv, urb, pipe);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/*
8858c2ecf20Sopenharmony_ci	 * data stage
8868c2ecf20Sopenharmony_ci	 *
8878c2ecf20Sopenharmony_ci	 * It is pushed only when urb has buffer.
8888c2ecf20Sopenharmony_ci	 */
8898c2ecf20Sopenharmony_ci	if (urb->transfer_buffer_length) {
8908c2ecf20Sopenharmony_ci		ret = usbhsh_data_stage_packet_push(hpriv, urb, pipe, mflags);
8918c2ecf20Sopenharmony_ci		if (ret < 0) {
8928c2ecf20Sopenharmony_ci			dev_err(dev, "data stage failed\n");
8938c2ecf20Sopenharmony_ci			return ret;
8948c2ecf20Sopenharmony_ci		}
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	/*
8988c2ecf20Sopenharmony_ci	 * status stage
8998c2ecf20Sopenharmony_ci	 */
9008c2ecf20Sopenharmony_ci	ret = usbhsh_status_stage_packet_push(hpriv, urb, pipe, mflags);
9018c2ecf20Sopenharmony_ci	if (ret < 0) {
9028c2ecf20Sopenharmony_ci		dev_err(dev, "status stage failed\n");
9038c2ecf20Sopenharmony_ci		return ret;
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	/*
9078c2ecf20Sopenharmony_ci	 * start pushed packets
9088c2ecf20Sopenharmony_ci	 */
9098c2ecf20Sopenharmony_ci	usbhs_pkt_start(pipe);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return 0;
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci/*
9158c2ecf20Sopenharmony_ci *		dma map functions
9168c2ecf20Sopenharmony_ci */
9178c2ecf20Sopenharmony_cistatic int usbhsh_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt,
9188c2ecf20Sopenharmony_ci			       int map)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	if (map) {
9218c2ecf20Sopenharmony_ci		struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
9228c2ecf20Sopenharmony_ci		struct urb *urb = ureq->urb;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		/* it can not use scatter/gather */
9258c2ecf20Sopenharmony_ci		if (urb->num_sgs)
9268c2ecf20Sopenharmony_ci			return -EINVAL;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		pkt->dma = urb->transfer_dma;
9298c2ecf20Sopenharmony_ci		if (!pkt->dma)
9308c2ecf20Sopenharmony_ci			return -EINVAL;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return 0;
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci/*
9378c2ecf20Sopenharmony_ci *		for hc_driver
9388c2ecf20Sopenharmony_ci */
9398c2ecf20Sopenharmony_cistatic int usbhsh_host_start(struct usb_hcd *hcd)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	return 0;
9428c2ecf20Sopenharmony_ci}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_cistatic void usbhsh_host_stop(struct usb_hcd *hcd)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic int usbhsh_urb_enqueue(struct usb_hcd *hcd,
9498c2ecf20Sopenharmony_ci			      struct urb *urb,
9508c2ecf20Sopenharmony_ci			      gfp_t mem_flags)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
9538c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
9548c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
9558c2ecf20Sopenharmony_ci	struct usb_host_endpoint *ep = urb->ep;
9568c2ecf20Sopenharmony_ci	struct usbhsh_device *new_udev = NULL;
9578c2ecf20Sopenharmony_ci	int is_dir_in = usb_pipein(urb->pipe);
9588c2ecf20Sopenharmony_ci	int ret;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s (%s)\n", __func__, is_dir_in ? "in" : "out");
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	if (!usbhsh_is_running(hpriv)) {
9638c2ecf20Sopenharmony_ci		ret = -EIO;
9648c2ecf20Sopenharmony_ci		dev_err(dev, "host is not running\n");
9658c2ecf20Sopenharmony_ci		goto usbhsh_urb_enqueue_error_not_linked;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	ret = usb_hcd_link_urb_to_ep(hcd, urb);
9698c2ecf20Sopenharmony_ci	if (ret) {
9708c2ecf20Sopenharmony_ci		dev_err(dev, "urb link failed\n");
9718c2ecf20Sopenharmony_ci		goto usbhsh_urb_enqueue_error_not_linked;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/*
9758c2ecf20Sopenharmony_ci	 * attach udev if needed
9768c2ecf20Sopenharmony_ci	 * see [image of mod_host]
9778c2ecf20Sopenharmony_ci	 */
9788c2ecf20Sopenharmony_ci	if (!usbhsh_device_get(hpriv, urb)) {
9798c2ecf20Sopenharmony_ci		new_udev = usbhsh_device_attach(hpriv, urb);
9808c2ecf20Sopenharmony_ci		if (!new_udev) {
9818c2ecf20Sopenharmony_ci			ret = -EIO;
9828c2ecf20Sopenharmony_ci			dev_err(dev, "device attach failed\n");
9838c2ecf20Sopenharmony_ci			goto usbhsh_urb_enqueue_error_not_linked;
9848c2ecf20Sopenharmony_ci		}
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/*
9888c2ecf20Sopenharmony_ci	 * attach endpoint if needed
9898c2ecf20Sopenharmony_ci	 * see [image of mod_host]
9908c2ecf20Sopenharmony_ci	 */
9918c2ecf20Sopenharmony_ci	if (!usbhsh_ep_to_uep(ep)) {
9928c2ecf20Sopenharmony_ci		ret = usbhsh_endpoint_attach(hpriv, urb, mem_flags);
9938c2ecf20Sopenharmony_ci		if (ret < 0) {
9948c2ecf20Sopenharmony_ci			dev_err(dev, "endpoint attach failed\n");
9958c2ecf20Sopenharmony_ci			goto usbhsh_urb_enqueue_error_free_device;
9968c2ecf20Sopenharmony_ci		}
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	/*
10008c2ecf20Sopenharmony_ci	 * attach pipe to endpoint
10018c2ecf20Sopenharmony_ci	 * see [image of mod_host]
10028c2ecf20Sopenharmony_ci	 */
10038c2ecf20Sopenharmony_ci	ret = usbhsh_pipe_attach(hpriv, urb);
10048c2ecf20Sopenharmony_ci	if (ret < 0) {
10058c2ecf20Sopenharmony_ci		dev_err(dev, "pipe attach failed\n");
10068c2ecf20Sopenharmony_ci		goto usbhsh_urb_enqueue_error_free_endpoint;
10078c2ecf20Sopenharmony_ci	}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/*
10108c2ecf20Sopenharmony_ci	 * push packet
10118c2ecf20Sopenharmony_ci	 */
10128c2ecf20Sopenharmony_ci	if (usb_pipecontrol(urb->pipe))
10138c2ecf20Sopenharmony_ci		ret = usbhsh_dcp_queue_push(hcd, urb, mem_flags);
10148c2ecf20Sopenharmony_ci	else
10158c2ecf20Sopenharmony_ci		ret = usbhsh_queue_push(hcd, urb, mem_flags);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	return ret;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ciusbhsh_urb_enqueue_error_free_endpoint:
10208c2ecf20Sopenharmony_ci	usbhsh_endpoint_detach(hpriv, ep);
10218c2ecf20Sopenharmony_ciusbhsh_urb_enqueue_error_free_device:
10228c2ecf20Sopenharmony_ci	if (new_udev)
10238c2ecf20Sopenharmony_ci		usbhsh_device_detach(hpriv, new_udev);
10248c2ecf20Sopenharmony_ciusbhsh_urb_enqueue_error_not_linked:
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s error\n", __func__);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	return ret;
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_cistatic int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
10348c2ecf20Sopenharmony_ci	struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	if (ureq) {
10378c2ecf20Sopenharmony_ci		struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
10388c2ecf20Sopenharmony_ci		struct usbhs_pkt *pkt = &ureq->pkt;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci		usbhs_pkt_pop(pkt->pipe, pkt);
10418c2ecf20Sopenharmony_ci		usbhsh_queue_done(priv, pkt);
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	return 0;
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic void usbhsh_endpoint_disable(struct usb_hcd *hcd,
10488c2ecf20Sopenharmony_ci				    struct usb_host_endpoint *ep)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
10518c2ecf20Sopenharmony_ci	struct usbhsh_device *udev;
10528c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/*
10558c2ecf20Sopenharmony_ci	 * this function might be called manytimes by same hcd/ep
10568c2ecf20Sopenharmony_ci	 * in-endpoint == out-endpoint if ep == dcp.
10578c2ecf20Sopenharmony_ci	 */
10588c2ecf20Sopenharmony_ci	if (!uep)
10598c2ecf20Sopenharmony_ci		return;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	udev	= usbhsh_uep_to_udev(uep);
10628c2ecf20Sopenharmony_ci	hpriv	= usbhsh_hcd_to_hpriv(hcd);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	usbhsh_endpoint_detach(hpriv, ep);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/*
10678c2ecf20Sopenharmony_ci	 * if there is no endpoint,
10688c2ecf20Sopenharmony_ci	 * free device
10698c2ecf20Sopenharmony_ci	 */
10708c2ecf20Sopenharmony_ci	if (!usbhsh_device_has_endpoint(udev))
10718c2ecf20Sopenharmony_ci		usbhsh_device_detach(hpriv, udev);
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
10778c2ecf20Sopenharmony_ci	int roothub_id = 1; /* only 1 root hub */
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	/*
10808c2ecf20Sopenharmony_ci	 * does port stat was changed ?
10818c2ecf20Sopenharmony_ci	 * check USB_PORT_STAT_C_xxx << 16
10828c2ecf20Sopenharmony_ci	 */
10838c2ecf20Sopenharmony_ci	if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000)
10848c2ecf20Sopenharmony_ci		*buf = (1 << roothub_id);
10858c2ecf20Sopenharmony_ci	else
10868c2ecf20Sopenharmony_ci		*buf = 0;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	return !!(*buf);
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv,
10928c2ecf20Sopenharmony_ci				    u16 typeReq, u16 wValue,
10938c2ecf20Sopenharmony_ci				    u16 wIndex, char *buf, u16 wLength)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
10968c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	switch (wValue) {
10998c2ecf20Sopenharmony_ci	case C_HUB_OVER_CURRENT:
11008c2ecf20Sopenharmony_ci	case C_HUB_LOCAL_POWER:
11018c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: C_HUB_xx\n", __func__);
11028c2ecf20Sopenharmony_ci		return 0;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	return -EPIPE;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv,
11098c2ecf20Sopenharmony_ci				     u16 typeReq, u16 wValue,
11108c2ecf20Sopenharmony_ci				     u16 wIndex, char *buf, u16 wLength)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
11138c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
11148c2ecf20Sopenharmony_ci	int enable = (typeReq == SetPortFeature);
11158c2ecf20Sopenharmony_ci	int speed, i, timeout = 128;
11168c2ecf20Sopenharmony_ci	int roothub_id = 1; /* only 1 root hub */
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	/* common error */
11198c2ecf20Sopenharmony_ci	if (wIndex > roothub_id || wLength != 0)
11208c2ecf20Sopenharmony_ci		return -EPIPE;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	/* check wValue */
11238c2ecf20Sopenharmony_ci	switch (wValue) {
11248c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_POWER:
11258c2ecf20Sopenharmony_ci		usbhs_vbus_ctrl(priv, enable);
11268c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__);
11278c2ecf20Sopenharmony_ci		break;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_ENABLE:
11308c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_SUSPEND:
11318c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_ENABLE:
11328c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_SUSPEND:
11338c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_CONNECTION:
11348c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_OVER_CURRENT:
11358c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_RESET:
11368c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__);
11378c2ecf20Sopenharmony_ci		break;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_RESET:
11408c2ecf20Sopenharmony_ci		if (!enable)
11418c2ecf20Sopenharmony_ci			break;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci		usbhsh_port_stat_clear(hpriv,
11448c2ecf20Sopenharmony_ci				       USB_PORT_STAT_HIGH_SPEED |
11458c2ecf20Sopenharmony_ci				       USB_PORT_STAT_LOW_SPEED);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		usbhsh_queue_force_pop_all(priv);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci		usbhs_bus_send_reset(priv);
11508c2ecf20Sopenharmony_ci		msleep(20);
11518c2ecf20Sopenharmony_ci		usbhs_bus_send_sof_enable(priv);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci		for (i = 0; i < timeout ; i++) {
11548c2ecf20Sopenharmony_ci			switch (usbhs_bus_get_speed(priv)) {
11558c2ecf20Sopenharmony_ci			case USB_SPEED_LOW:
11568c2ecf20Sopenharmony_ci				speed = USB_PORT_STAT_LOW_SPEED;
11578c2ecf20Sopenharmony_ci				goto got_usb_bus_speed;
11588c2ecf20Sopenharmony_ci			case USB_SPEED_HIGH:
11598c2ecf20Sopenharmony_ci				speed = USB_PORT_STAT_HIGH_SPEED;
11608c2ecf20Sopenharmony_ci				goto got_usb_bus_speed;
11618c2ecf20Sopenharmony_ci			case USB_SPEED_FULL:
11628c2ecf20Sopenharmony_ci				speed = 0;
11638c2ecf20Sopenharmony_ci				goto got_usb_bus_speed;
11648c2ecf20Sopenharmony_ci			}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci			msleep(20);
11678c2ecf20Sopenharmony_ci		}
11688c2ecf20Sopenharmony_ci		return -EPIPE;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_cigot_usb_bus_speed:
11718c2ecf20Sopenharmony_ci		usbhsh_port_stat_set(hpriv, speed);
11728c2ecf20Sopenharmony_ci		usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n",
11758c2ecf20Sopenharmony_ci			__func__, speed);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci		/* status change is not needed */
11788c2ecf20Sopenharmony_ci		return 0;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	default:
11818c2ecf20Sopenharmony_ci		return -EPIPE;
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	/* set/clear status */
11858c2ecf20Sopenharmony_ci	if (enable)
11868c2ecf20Sopenharmony_ci		usbhsh_port_stat_set(hpriv, (1 << wValue));
11878c2ecf20Sopenharmony_ci	else
11888c2ecf20Sopenharmony_ci		usbhsh_port_stat_clear(hpriv, (1 << wValue));
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	return 0;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv,
11948c2ecf20Sopenharmony_ci				   u16 typeReq, u16 wValue,
11958c2ecf20Sopenharmony_ci				   u16 wIndex, char *buf, u16 wLength)
11968c2ecf20Sopenharmony_ci{
11978c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
11988c2ecf20Sopenharmony_ci	struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf;
11998c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
12008c2ecf20Sopenharmony_ci	int roothub_id = 1; /* only 1 root hub */
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	switch (typeReq) {
12038c2ecf20Sopenharmony_ci	case GetHubStatus:
12048c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: GetHubStatus\n", __func__);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		*buf = 0x00;
12078c2ecf20Sopenharmony_ci		break;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	case GetPortStatus:
12108c2ecf20Sopenharmony_ci		if (wIndex != roothub_id)
12118c2ecf20Sopenharmony_ci			return -EPIPE;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: GetPortStatus\n", __func__);
12148c2ecf20Sopenharmony_ci		*(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv));
12158c2ecf20Sopenharmony_ci		break;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	case GetHubDescriptor:
12188c2ecf20Sopenharmony_ci		desc->bDescriptorType		= USB_DT_HUB;
12198c2ecf20Sopenharmony_ci		desc->bHubContrCurrent		= 0;
12208c2ecf20Sopenharmony_ci		desc->bNbrPorts			= roothub_id;
12218c2ecf20Sopenharmony_ci		desc->bDescLength		= 9;
12228c2ecf20Sopenharmony_ci		desc->bPwrOn2PwrGood		= 0;
12238c2ecf20Sopenharmony_ci		desc->wHubCharacteristics	=
12248c2ecf20Sopenharmony_ci			cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM);
12258c2ecf20Sopenharmony_ci		desc->u.hs.DeviceRemovable[0]	= (roothub_id << 1);
12268c2ecf20Sopenharmony_ci		desc->u.hs.DeviceRemovable[1]	= ~0;
12278c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__);
12288c2ecf20Sopenharmony_ci		break;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	return 0;
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
12358c2ecf20Sopenharmony_ci			      u16 wIndex, char *buf, u16 wLength)
12368c2ecf20Sopenharmony_ci{
12378c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
12388c2ecf20Sopenharmony_ci	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
12398c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
12408c2ecf20Sopenharmony_ci	int ret = -EPIPE;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	switch (typeReq) {
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	/* Hub Feature */
12458c2ecf20Sopenharmony_ci	case ClearHubFeature:
12468c2ecf20Sopenharmony_ci	case SetHubFeature:
12478c2ecf20Sopenharmony_ci		ret = __usbhsh_hub_hub_feature(hpriv, typeReq,
12488c2ecf20Sopenharmony_ci					       wValue, wIndex, buf, wLength);
12498c2ecf20Sopenharmony_ci		break;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	/* Port Feature */
12528c2ecf20Sopenharmony_ci	case SetPortFeature:
12538c2ecf20Sopenharmony_ci	case ClearPortFeature:
12548c2ecf20Sopenharmony_ci		ret = __usbhsh_hub_port_feature(hpriv, typeReq,
12558c2ecf20Sopenharmony_ci						wValue, wIndex, buf, wLength);
12568c2ecf20Sopenharmony_ci		break;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	/* Get status */
12598c2ecf20Sopenharmony_ci	case GetHubStatus:
12608c2ecf20Sopenharmony_ci	case GetPortStatus:
12618c2ecf20Sopenharmony_ci	case GetHubDescriptor:
12628c2ecf20Sopenharmony_ci		ret = __usbhsh_hub_get_status(hpriv, typeReq,
12638c2ecf20Sopenharmony_ci					      wValue, wIndex, buf, wLength);
12648c2ecf20Sopenharmony_ci		break;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n",
12688c2ecf20Sopenharmony_ci		typeReq, ret, usbhsh_port_stat_get(hpriv));
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	return ret;
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic int usbhsh_bus_nop(struct usb_hcd *hcd)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	/* nothing to do */
12768c2ecf20Sopenharmony_ci	return 0;
12778c2ecf20Sopenharmony_ci}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cistatic const struct hc_driver usbhsh_driver = {
12808c2ecf20Sopenharmony_ci	.description =		usbhsh_hcd_name,
12818c2ecf20Sopenharmony_ci	.hcd_priv_size =	sizeof(struct usbhsh_hpriv),
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/*
12848c2ecf20Sopenharmony_ci	 * generic hardware linkage
12858c2ecf20Sopenharmony_ci	 */
12868c2ecf20Sopenharmony_ci	.flags =		HCD_DMA | HCD_USB2,
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	.start =		usbhsh_host_start,
12898c2ecf20Sopenharmony_ci	.stop =			usbhsh_host_stop,
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	/*
12928c2ecf20Sopenharmony_ci	 * managing i/o requests and associated device resources
12938c2ecf20Sopenharmony_ci	 */
12948c2ecf20Sopenharmony_ci	.urb_enqueue =		usbhsh_urb_enqueue,
12958c2ecf20Sopenharmony_ci	.urb_dequeue =		usbhsh_urb_dequeue,
12968c2ecf20Sopenharmony_ci	.endpoint_disable =	usbhsh_endpoint_disable,
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	/*
12998c2ecf20Sopenharmony_ci	 * root hub
13008c2ecf20Sopenharmony_ci	 */
13018c2ecf20Sopenharmony_ci	.hub_status_data =	usbhsh_hub_status_data,
13028c2ecf20Sopenharmony_ci	.hub_control =		usbhsh_hub_control,
13038c2ecf20Sopenharmony_ci	.bus_suspend =		usbhsh_bus_nop,
13048c2ecf20Sopenharmony_ci	.bus_resume =		usbhsh_bus_nop,
13058c2ecf20Sopenharmony_ci};
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci/*
13088c2ecf20Sopenharmony_ci *		interrupt functions
13098c2ecf20Sopenharmony_ci */
13108c2ecf20Sopenharmony_cistatic int usbhsh_irq_attch(struct usbhs_priv *priv,
13118c2ecf20Sopenharmony_ci			    struct usbhs_irq_state *irq_state)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
13148c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	dev_dbg(dev, "device attached\n");
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION);
13198c2ecf20Sopenharmony_ci	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	/*
13228c2ecf20Sopenharmony_ci	 * attch interrupt might happen infinitely on some device
13238c2ecf20Sopenharmony_ci	 * (on self power USB hub ?)
13248c2ecf20Sopenharmony_ci	 * disable it here.
13258c2ecf20Sopenharmony_ci	 *
13268c2ecf20Sopenharmony_ci	 * usbhsh_is_running() becomes effective
13278c2ecf20Sopenharmony_ci	 * according to this process.
13288c2ecf20Sopenharmony_ci	 * see
13298c2ecf20Sopenharmony_ci	 *	usbhsh_is_running()
13308c2ecf20Sopenharmony_ci	 *	usbhsh_urb_enqueue()
13318c2ecf20Sopenharmony_ci	 */
13328c2ecf20Sopenharmony_ci	hpriv->mod.irq_attch = NULL;
13338c2ecf20Sopenharmony_ci	usbhs_irq_callback_update(priv, &hpriv->mod);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	return 0;
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_cistatic int usbhsh_irq_dtch(struct usbhs_priv *priv,
13398c2ecf20Sopenharmony_ci			   struct usbhs_irq_state *irq_state)
13408c2ecf20Sopenharmony_ci{
13418c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
13428c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	dev_dbg(dev, "device detached\n");
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION);
13478c2ecf20Sopenharmony_ci	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	/*
13508c2ecf20Sopenharmony_ci	 * enable attch interrupt again
13518c2ecf20Sopenharmony_ci	 *
13528c2ecf20Sopenharmony_ci	 * usbhsh_is_running() becomes invalid
13538c2ecf20Sopenharmony_ci	 * according to this process.
13548c2ecf20Sopenharmony_ci	 * see
13558c2ecf20Sopenharmony_ci	 *	usbhsh_is_running()
13568c2ecf20Sopenharmony_ci	 *	usbhsh_urb_enqueue()
13578c2ecf20Sopenharmony_ci	 */
13588c2ecf20Sopenharmony_ci	hpriv->mod.irq_attch = usbhsh_irq_attch;
13598c2ecf20Sopenharmony_ci	usbhs_irq_callback_update(priv, &hpriv->mod);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	/*
13628c2ecf20Sopenharmony_ci	 * usbhsh_queue_force_pop_all() should be called
13638c2ecf20Sopenharmony_ci	 * after usbhsh_is_running() becomes invalid.
13648c2ecf20Sopenharmony_ci	 */
13658c2ecf20Sopenharmony_ci	usbhsh_queue_force_pop_all(priv);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	return 0;
13688c2ecf20Sopenharmony_ci}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_cistatic int usbhsh_irq_setup_ack(struct usbhs_priv *priv,
13718c2ecf20Sopenharmony_ci				struct usbhs_irq_state *irq_state)
13728c2ecf20Sopenharmony_ci{
13738c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
13748c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	dev_dbg(dev, "setup packet OK\n");
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	complete(&hpriv->setup_ack_done); /* see usbhsh_urb_enqueue() */
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int usbhsh_irq_setup_err(struct usbhs_priv *priv,
13848c2ecf20Sopenharmony_ci				struct usbhs_irq_state *irq_state)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
13878c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	dev_dbg(dev, "setup packet Err\n");
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	complete(&hpriv->setup_ack_done); /* see usbhsh_urb_enqueue() */
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	return 0;
13948c2ecf20Sopenharmony_ci}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci/*
13978c2ecf20Sopenharmony_ci *		module start/stop
13988c2ecf20Sopenharmony_ci */
13998c2ecf20Sopenharmony_cistatic void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
14028c2ecf20Sopenharmony_ci	struct usbhs_pipe *pipe;
14038c2ecf20Sopenharmony_ci	struct renesas_usbhs_driver_pipe_config *pipe_configs =
14048c2ecf20Sopenharmony_ci					usbhs_get_dparam(priv, pipe_configs);
14058c2ecf20Sopenharmony_ci	int pipe_size = usbhs_get_dparam(priv, pipe_size);
14068c2ecf20Sopenharmony_ci	int old_type, dir_in, i;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	/* init all pipe */
14098c2ecf20Sopenharmony_ci	old_type = USB_ENDPOINT_XFER_CONTROL;
14108c2ecf20Sopenharmony_ci	for (i = 0; i < pipe_size; i++) {
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci		/*
14138c2ecf20Sopenharmony_ci		 * data "output" will be finished as soon as possible,
14148c2ecf20Sopenharmony_ci		 * but there is no guaranty at data "input" case.
14158c2ecf20Sopenharmony_ci		 *
14168c2ecf20Sopenharmony_ci		 * "input" needs "standby" pipe.
14178c2ecf20Sopenharmony_ci		 * So, "input" direction pipe > "output" direction pipe
14188c2ecf20Sopenharmony_ci		 * is good idea.
14198c2ecf20Sopenharmony_ci		 *
14208c2ecf20Sopenharmony_ci		 * 1st USB_ENDPOINT_XFER_xxx will be output direction,
14218c2ecf20Sopenharmony_ci		 * and the other will be input direction here.
14228c2ecf20Sopenharmony_ci		 *
14238c2ecf20Sopenharmony_ci		 * ex)
14248c2ecf20Sopenharmony_ci		 * ...
14258c2ecf20Sopenharmony_ci		 * USB_ENDPOINT_XFER_ISOC -> dir out
14268c2ecf20Sopenharmony_ci		 * USB_ENDPOINT_XFER_ISOC -> dir in
14278c2ecf20Sopenharmony_ci		 * USB_ENDPOINT_XFER_BULK -> dir out
14288c2ecf20Sopenharmony_ci		 * USB_ENDPOINT_XFER_BULK -> dir in
14298c2ecf20Sopenharmony_ci		 * USB_ENDPOINT_XFER_BULK -> dir in
14308c2ecf20Sopenharmony_ci		 * ...
14318c2ecf20Sopenharmony_ci		 */
14328c2ecf20Sopenharmony_ci		dir_in = (pipe_configs[i].type == old_type);
14338c2ecf20Sopenharmony_ci		old_type = pipe_configs[i].type;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci		if (USB_ENDPOINT_XFER_CONTROL == pipe_configs[i].type) {
14368c2ecf20Sopenharmony_ci			pipe = usbhs_dcp_malloc(priv);
14378c2ecf20Sopenharmony_ci			usbhsh_hpriv_to_dcp(hpriv) = pipe;
14388c2ecf20Sopenharmony_ci		} else {
14398c2ecf20Sopenharmony_ci			pipe = usbhs_pipe_malloc(priv,
14408c2ecf20Sopenharmony_ci						 pipe_configs[i].type,
14418c2ecf20Sopenharmony_ci						 dir_in);
14428c2ecf20Sopenharmony_ci		}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci		pipe->mod_private = NULL;
14458c2ecf20Sopenharmony_ci	}
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_cistatic int usbhsh_start(struct usbhs_priv *priv)
14498c2ecf20Sopenharmony_ci{
14508c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
14518c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
14528c2ecf20Sopenharmony_ci	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
14538c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
14548c2ecf20Sopenharmony_ci	int ret;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* add hcd */
14578c2ecf20Sopenharmony_ci	ret = usb_add_hcd(hcd, 0, 0);
14588c2ecf20Sopenharmony_ci	if (ret < 0)
14598c2ecf20Sopenharmony_ci		return 0;
14608c2ecf20Sopenharmony_ci	device_wakeup_enable(hcd->self.controller);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	/*
14638c2ecf20Sopenharmony_ci	 * pipe initialize and enable DCP
14648c2ecf20Sopenharmony_ci	 */
14658c2ecf20Sopenharmony_ci	usbhs_fifo_init(priv);
14668c2ecf20Sopenharmony_ci	usbhs_pipe_init(priv,
14678c2ecf20Sopenharmony_ci			usbhsh_dma_map_ctrl);
14688c2ecf20Sopenharmony_ci	usbhsh_pipe_init_for_host(priv);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	/*
14718c2ecf20Sopenharmony_ci	 * system config enble
14728c2ecf20Sopenharmony_ci	 * - HI speed
14738c2ecf20Sopenharmony_ci	 * - host
14748c2ecf20Sopenharmony_ci	 * - usb module
14758c2ecf20Sopenharmony_ci	 */
14768c2ecf20Sopenharmony_ci	usbhs_sys_host_ctrl(priv, 1);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	/*
14798c2ecf20Sopenharmony_ci	 * enable irq callback
14808c2ecf20Sopenharmony_ci	 */
14818c2ecf20Sopenharmony_ci	mod->irq_attch		= usbhsh_irq_attch;
14828c2ecf20Sopenharmony_ci	mod->irq_dtch		= usbhsh_irq_dtch;
14838c2ecf20Sopenharmony_ci	mod->irq_sack		= usbhsh_irq_setup_ack;
14848c2ecf20Sopenharmony_ci	mod->irq_sign		= usbhsh_irq_setup_err;
14858c2ecf20Sopenharmony_ci	usbhs_irq_callback_update(priv, mod);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	dev_dbg(dev, "start host\n");
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	return ret;
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_cistatic int usbhsh_stop(struct usbhs_priv *priv)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
14958c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
14968c2ecf20Sopenharmony_ci	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
14978c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	/*
15008c2ecf20Sopenharmony_ci	 * disable irq callback
15018c2ecf20Sopenharmony_ci	 */
15028c2ecf20Sopenharmony_ci	mod->irq_attch	= NULL;
15038c2ecf20Sopenharmony_ci	mod->irq_dtch	= NULL;
15048c2ecf20Sopenharmony_ci	mod->irq_sack	= NULL;
15058c2ecf20Sopenharmony_ci	mod->irq_sign	= NULL;
15068c2ecf20Sopenharmony_ci	usbhs_irq_callback_update(priv, mod);
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	usb_remove_hcd(hcd);
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	/* disable sys */
15118c2ecf20Sopenharmony_ci	usbhs_sys_host_ctrl(priv, 0);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	dev_dbg(dev, "quit host\n");
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	return 0;
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ciint usbhs_mod_host_probe(struct usbhs_priv *priv)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv;
15218c2ecf20Sopenharmony_ci	struct usb_hcd *hcd;
15228c2ecf20Sopenharmony_ci	struct usbhsh_device *udev;
15238c2ecf20Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
15248c2ecf20Sopenharmony_ci	int i;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	/* initialize hcd */
15278c2ecf20Sopenharmony_ci	hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name);
15288c2ecf20Sopenharmony_ci	if (!hcd) {
15298c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to create hcd\n");
15308c2ecf20Sopenharmony_ci		return -ENOMEM;
15318c2ecf20Sopenharmony_ci	}
15328c2ecf20Sopenharmony_ci	hcd->has_tt = 1; /* for low/full speed */
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	/*
15358c2ecf20Sopenharmony_ci	 * CAUTION
15368c2ecf20Sopenharmony_ci	 *
15378c2ecf20Sopenharmony_ci	 * There is no guarantee that it is possible to access usb module here.
15388c2ecf20Sopenharmony_ci	 * Don't accesses to it.
15398c2ecf20Sopenharmony_ci	 * The accesse will be enable after "usbhsh_start"
15408c2ecf20Sopenharmony_ci	 */
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	hpriv = usbhsh_hcd_to_hpriv(hcd);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	/*
15458c2ecf20Sopenharmony_ci	 * register itself
15468c2ecf20Sopenharmony_ci	 */
15478c2ecf20Sopenharmony_ci	usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	/* init hpriv */
15508c2ecf20Sopenharmony_ci	hpriv->mod.name		= "host";
15518c2ecf20Sopenharmony_ci	hpriv->mod.start	= usbhsh_start;
15528c2ecf20Sopenharmony_ci	hpriv->mod.stop		= usbhsh_stop;
15538c2ecf20Sopenharmony_ci	usbhsh_port_stat_init(hpriv);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	/* init all device */
15568c2ecf20Sopenharmony_ci	usbhsh_for_each_udev_with_dev0(udev, hpriv, i) {
15578c2ecf20Sopenharmony_ci		udev->usbv	= NULL;
15588c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&udev->ep_list_head);
15598c2ecf20Sopenharmony_ci	}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	dev_info(dev, "host probed\n");
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	return 0;
15648c2ecf20Sopenharmony_ci}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ciint usbhs_mod_host_remove(struct usbhs_priv *priv)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
15698c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	usb_put_hcd(hcd);
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	return 0;
15748c2ecf20Sopenharmony_ci}
1575