mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			977 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			977 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Diff
		
	
	
| --- linux-4.9.37/drivers/usb/dwc3/gadget.c	2017-07-12 16:42:41.000000000 +0300
 | |
| +++ linux-4.9.y/drivers/usb/dwc3/gadget.c	2021-06-07 13:01:34.000000000 +0300
 | |
| @@ -35,6 +35,15 @@
 | |
|  #include "gadget.h"
 | |
|  #include "io.h"
 | |
|  
 | |
| +static void dwc3_gadget_sync_connected_status(struct dwc3 *dwc);
 | |
| +static int __dwc3_gadget_get_frame(struct dwc3 *dwc);
 | |
| +static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
 | |
| +		const struct dwc3_event_depevt *event);
 | |
| +static bool __dwc3_gadget_target_frame_elapsed(struct dwc3_ep *dep);
 | |
| +
 | |
| +#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
 | |
| +		& ~((d)->interval - 1))
 | |
| +
 | |
|  /**
 | |
|   * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
 | |
|   * @dwc: pointer to our context structure
 | |
| @@ -139,9 +148,6 @@
 | |
|  		udelay(5);
 | |
|  	}
 | |
|  
 | |
| -	dwc3_trace(trace_dwc3_gadget,
 | |
| -			"link state change request timed out");
 | |
| -
 | |
|  	return -ETIMEDOUT;
 | |
|  }
 | |
|  
 | |
| @@ -153,7 +159,7 @@
 | |
|   * if it is point to the link TRB, wrap around to the beginning. The
 | |
|   * link TRB is always at the last TRB entry.
 | |
|   */
 | |
| -static void dwc3_ep_inc_trb(u8 *index)
 | |
| +static void dwc3_ep_inc_trb(u32 *index)
 | |
|  {
 | |
|  	(*index)++;
 | |
|  	if (*index == (DWC3_TRB_NUM - 1))
 | |
| @@ -247,7 +253,7 @@
 | |
|  		struct dwc3_gadget_ep_cmd_params *params)
 | |
|  {
 | |
|  	struct dwc3		*dwc = dep->dwc;
 | |
| -	u32			timeout = 500;
 | |
| +	u32			timeout = 5000;
 | |
|  	u32			reg;
 | |
|  
 | |
|  	int			cmd_status = 0;
 | |
| @@ -563,8 +569,6 @@
 | |
|  	u32			reg;
 | |
|  	int			ret;
 | |
|  
 | |
| -	dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
 | |
| -
 | |
|  	if (!(dep->flags & DWC3_EP_ENABLED)) {
 | |
|  		ret = dwc3_gadget_start_config(dwc, dep);
 | |
|  		if (ret)
 | |
| @@ -590,7 +594,7 @@
 | |
|  		dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
 | |
|  
 | |
|  		if (usb_endpoint_xfer_control(desc))
 | |
| -			return 0;
 | |
| +			goto out;
 | |
|  
 | |
|  		/* Initialize the TRB ring */
 | |
|  		dep->trb_dequeue = 0;
 | |
| @@ -608,15 +612,19 @@
 | |
|  		trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
 | |
|  	}
 | |
|  
 | |
| +
 | |
| +out:
 | |
| +	trace_dwc3_gadget_ep_enable(dep);
 | |
| +
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
 | |
| +static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force);
 | |
|  static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
 | |
|  {
 | |
|  	struct dwc3_request		*req;
 | |
|  
 | |
| -	dwc3_stop_active_transfer(dwc, dep->number, true);
 | |
| +	dwc3_stop_active_transfer(dep, true);
 | |
|  
 | |
|  	/* - giveback all requests to gadget driver */
 | |
|  	while (!list_empty(&dep->started_list)) {
 | |
| @@ -645,7 +653,7 @@
 | |
|  	struct dwc3		*dwc = dep->dwc;
 | |
|  	u32			reg;
 | |
|  
 | |
| -	dwc3_trace(trace_dwc3_gadget, "Disabling %s", dep->name);
 | |
| +	trace_dwc3_gadget_ep_disable(dep);
 | |
|  
 | |
|  	dwc3_remove_requests(dwc, dep);
 | |
|  
 | |
| @@ -787,10 +795,7 @@
 | |
|  	struct dwc3		*dwc = dep->dwc;
 | |
|  	struct usb_gadget	*gadget = &dwc->gadget;
 | |
|  	enum usb_device_speed	speed = gadget->speed;
 | |
| -
 | |
| -	dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
 | |
| -			dep->name, req, (unsigned long long) dma,
 | |
| -			length, chain ? " chain" : "");
 | |
| +	unsigned int chain_skip = 0;
 | |
|  
 | |
|  	trb = &dep->trb_pool[dep->trb_enqueue];
 | |
|  
 | |
| @@ -816,12 +821,55 @@
 | |
|  	case USB_ENDPOINT_XFER_ISOC:
 | |
|  		if (!node) {
 | |
|  			trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
 | |
| -
 | |
| +			/*
 | |
| +			 * USB Specification 2.0 Section 5.9.2 states that: "If
 | |
| +			 * there is only a single transaction in the microframe,
 | |
| +			 * only a DATA0 data packet PID is used.  If there are
 | |
| +			 * two transactions per microframe, DATA1 is used for
 | |
| +			 * the first transaction data packet and DATA0 is used
 | |
| +			 * for the second transaction data packet.  If there are
 | |
| +			 * three transactions per microframe, DATA2 is used for
 | |
| +			 * the first transaction data packet, DATA1 is used for
 | |
| +			 * the second, and DATA0 is used for the third."
 | |
| +			 *
 | |
| +			 * IOW, we should satisfy the following cases:
 | |
| +			 *
 | |
| +			 * 1) length <= maxpacket
 | |
| +			 *	- DATA0
 | |
| +			 *
 | |
| +			 * 2) maxpacket < length <= (2 * maxpacket)
 | |
| +			 *	- DATA1, DATA0
 | |
| +			 *
 | |
| +			 * 3) (2 * maxpacket) < length <= (3 * maxpacket)
 | |
| +			 *	- DATA2, DATA1, DATA0
 | |
| +			 */
 | |
|  			if (speed == USB_SPEED_HIGH) {
 | |
|  				struct usb_ep *ep = &dep->endpoint;
 | |
| -				trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
 | |
| +				//unsigned int mult = ep->mult - 1;
 | |
| +				unsigned int mult = 2;  //backport commit 	ec5bb87e4e2a1d3a35563a7bcfac9febf67aba9d
 | |
| +				unsigned int maxp = usb_endpoint_maxp(ep->desc);
 | |
| +
 | |
| +				if (length <= (2 * maxp))
 | |
| +					mult--;
 | |
| +
 | |
| +				if (length <= maxp)
 | |
| +					mult--;
 | |
| +
 | |
| +				trb->size |= DWC3_TRB_SIZE_PCM1(mult);
 | |
| +
 | |
| +				/*
 | |
| +				 * If there are three transactions per mframe,
 | |
| +				 * and each transcation length = 1024B, no any
 | |
| +				 * chain trb needed, so skip it.
 | |
| +				 */
 | |
| +				if (length == (3 * maxp))
 | |
| +					chain_skip = 1;
 | |
|  			}
 | |
| +
 | |
| +			if (speed == USB_SPEED_SUPER)
 | |
| +				chain_skip = 1;
 | |
|  		} else {
 | |
| +			chain_skip = 1;
 | |
|  			trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
 | |
|  		}
 | |
|  
 | |
| @@ -848,7 +896,7 @@
 | |
|  			(dwc3_calc_trbs_left(dep) == 0))
 | |
|  		trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
 | |
|  
 | |
| -	if (chain)
 | |
| +	if ((!chain_skip) && chain)
 | |
|  		trb->ctrl |= DWC3_TRB_CTRL_CHN;
 | |
|  
 | |
|  	if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
 | |
| @@ -868,9 +916,9 @@
 | |
|   * index is 0, we will wrap backwards, skip the link TRB, and return
 | |
|   * the one just before that.
 | |
|   */
 | |
| -static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
 | |
| +static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u32 index)
 | |
|  {
 | |
| -	u8 tmp = index;
 | |
| +	u32 tmp = index;
 | |
|  
 | |
|  	if (!tmp)
 | |
|  		tmp = DWC3_TRB_NUM - 1;
 | |
| @@ -881,7 +929,7 @@
 | |
|  static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
 | |
|  {
 | |
|  	struct dwc3_trb		*tmp;
 | |
| -	u8			trbs_left;
 | |
| +	u32			trbs_left;
 | |
|  
 | |
|  	/*
 | |
|  	 * If enqueue & dequeue are equal than it is either full or empty.
 | |
| @@ -910,11 +958,13 @@
 | |
|  static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
 | |
|  		struct dwc3_request *req)
 | |
|  {
 | |
| +	struct dwc3		*dwc = dep->dwc;
 | |
| +	struct usb_gadget	*gadget = &dwc->gadget;
 | |
| +	enum usb_device_speed   speed = gadget->speed;
 | |
|  	struct scatterlist *sg = req->sg;
 | |
|  	struct scatterlist *s;
 | |
| -	unsigned int	length;
 | |
| +	unsigned int	length, i;
 | |
|  	dma_addr_t	dma;
 | |
| -	int		i;
 | |
|  
 | |
|  	for_each_sg(sg, s, req->num_pending_sgs, i) {
 | |
|  		unsigned chain = true;
 | |
| @@ -925,8 +975,11 @@
 | |
|  		if (sg_is_last(s))
 | |
|  			chain = false;
 | |
|  
 | |
| -		dwc3_prepare_one_trb(dep, req, dma, length,
 | |
| -				chain, i);
 | |
| +		if ((speed == USB_SPEED_HIGH) &&
 | |
| +			usb_endpoint_xfer_isoc(dep->endpoint.desc))
 | |
| +			dwc3_prepare_one_trb(dep, req, dma, length, chain, 0);
 | |
| +		else
 | |
| +			dwc3_prepare_one_trb(dep, req, dma, length, chain, i);
 | |
|  
 | |
|  		if (!dwc3_calc_trbs_left(dep))
 | |
|  			break;
 | |
| @@ -994,11 +1047,22 @@
 | |
|  
 | |
|  	memset(¶ms, 0, sizeof(params));
 | |
|  
 | |
| -	if (starting) {
 | |
| +	if (starting && !(dep->flags&DWC3_EP_UPDATE)) {
 | |
|  		params.param0 = upper_32_bits(req->trb_dma);
 | |
|  		params.param1 = lower_32_bits(req->trb_dma);
 | |
| -		cmd = DWC3_DEPCMD_STARTTRANSFER |
 | |
| -			DWC3_DEPCMD_PARAM(cmd_param);
 | |
| +
 | |
| +		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 | |
| +			while (__dwc3_gadget_target_frame_elapsed(dep))
 | |
| +				dep->frame_number = DWC3_ALIGN_FRAME(dep);
 | |
| +
 | |
| +			dep->frame_number = DWC3_ALIGN_FRAME(dep);
 | |
| +			cmd_param = dep->frame_number;
 | |
| +		}
 | |
| +
 | |
| +		cmd = DWC3_DEPCMD_STARTTRANSFER | DWC3_DEPCMD_PARAM(cmd_param);
 | |
| +
 | |
| +		if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
 | |
| +			dep->flags |= DWC3_EP_UPDATE;
 | |
|  	} else {
 | |
|  		cmd = DWC3_DEPCMD_UPDATETRANSFER |
 | |
|  			DWC3_DEPCMD_PARAM(dep->resource_index);
 | |
| @@ -1027,34 +1091,36 @@
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| -static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
 | |
| -		struct dwc3_ep *dep, u32 cur_uf)
 | |
| -{
 | |
| -	u32 uf;
 | |
| +static bool __dwc3_gadget_target_frame_elapsed(struct dwc3_ep *dep) {
 | |
| +	u16 cframe =  __dwc3_gadget_get_frame(dep->dwc);
 | |
| +	u16 eframe = dep->frame_number & DWC3_EVENT_PRAM_SOFFN_MASK;
 | |
| +
 | |
| +	if (eframe == cframe)
 | |
| +		return true;
 | |
| +
 | |
| +	return (((eframe - cframe) & DWC3_EVENT_PRAM_SOFFN_MASK)
 | |
| +		> DWC3_EVENT_PRAM_MAX_SOFFN / 2);
 | |
| +}
 | |
|  
 | |
| +static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
 | |
| +{
 | |
|  	if (list_empty(&dep->pending_list)) {
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"ISOC ep %s run out for requests",
 | |
| -				dep->name);
 | |
|  		dep->flags |= DWC3_EP_PENDING_REQUEST;
 | |
|  		return;
 | |
|  	}
 | |
|  
 | |
| -	/* 4 micro frames in the future */
 | |
| -	uf = cur_uf + dep->interval * 4;
 | |
| +	while (__dwc3_gadget_target_frame_elapsed(dep))
 | |
| +		dep->frame_number = DWC3_ALIGN_FRAME(dep);
 | |
|  
 | |
| -	__dwc3_gadget_kick_transfer(dep, uf);
 | |
| +	dep->frame_number = DWC3_ALIGN_FRAME(dep);
 | |
| +	__dwc3_gadget_kick_transfer(dep, dep->frame_number);
 | |
|  }
 | |
|  
 | |
| -static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
 | |
| -		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
 | |
| +static void dwc3_gadget_start_isoc(struct dwc3_ep *dep,
 | |
| +		const struct dwc3_event_depevt *event)
 | |
|  {
 | |
| -	u32 cur_uf, mask;
 | |
| -
 | |
| -	mask = ~(dep->interval - 1);
 | |
| -	cur_uf = event->parameters & mask;
 | |
| -
 | |
| -	__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
 | |
| +	dwc3_gadget_endpoint_frame_from_event(dep, event);
 | |
| +	__dwc3_gadget_start_isoc(dep);
 | |
|  }
 | |
|  
 | |
|  static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 | |
| @@ -1063,16 +1129,15 @@
 | |
|  	int			ret;
 | |
|  
 | |
|  	if (!dep->endpoint.desc) {
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"trying to queue request %p to disabled %s",
 | |
| -				&req->request, dep->endpoint.name);
 | |
| +		dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
 | |
| +				dep->name);
 | |
|  		return -ESHUTDOWN;
 | |
|  	}
 | |
|  
 | |
|  	if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
 | |
|  				&req->request, req->dep->name)) {
 | |
| -		dwc3_trace(trace_dwc3_gadget, "request %pK belongs to '%s'",
 | |
| -				&req->request, req->dep->name);
 | |
| +		dev_err(dwc->dev, "%s: request %p belongs to '%s'\n",
 | |
| +				dep->name, &req->request, req->dep->name);
 | |
|  		return -EINVAL;
 | |
|  	}
 | |
|  
 | |
| @@ -1106,7 +1171,7 @@
 | |
|  	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 | |
|  		if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
 | |
|  				list_empty(&dep->started_list)) {
 | |
| -			dwc3_stop_active_transfer(dwc, dep->number, true);
 | |
| +			dwc3_stop_active_transfer(dep, true);
 | |
|  			dep->flags = DWC3_EP_ENABLED;
 | |
|  		}
 | |
|  		return 0;
 | |
| @@ -1116,10 +1181,6 @@
 | |
|  		return 0;
 | |
|  
 | |
|  	ret = __dwc3_gadget_kick_transfer(dep, 0);
 | |
| -	if (ret && ret != -EBUSY)
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"%s: failed to kick transfers",
 | |
| -				dep->name);
 | |
|  	if (ret == -EBUSY)
 | |
|  		ret = 0;
 | |
|  
 | |
| @@ -1138,7 +1199,6 @@
 | |
|  	struct usb_request		*request;
 | |
|  	struct usb_ep			*ep = &dep->endpoint;
 | |
|  
 | |
| -	dwc3_trace(trace_dwc3_gadget, "queueing ZLP");
 | |
|  	request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
 | |
|  	if (!request)
 | |
|  		return -ENOMEM;
 | |
| @@ -1197,6 +1257,10 @@
 | |
|  
 | |
|  	spin_lock_irqsave(&dwc->lock, flags);
 | |
|  
 | |
| +
 | |
| +    if (list_empty(&dep->pending_list) && list_empty(&dep->started_list) )
 | |
| +		goto out0 ;
 | |
| +
 | |
|  	list_for_each_entry(r, &dep->pending_list, list) {
 | |
|  		if (r == req)
 | |
|  			break;
 | |
| @@ -1209,7 +1273,7 @@
 | |
|  		}
 | |
|  		if (r == req) {
 | |
|  			/* wait until it is processed */
 | |
| -			dwc3_stop_active_transfer(dwc, dep->number, true);
 | |
| +			dwc3_stop_active_transfer(dep, true);
 | |
|  			goto out1;
 | |
|  		}
 | |
|  		dev_err(dwc->dev, "request %pK was not queued to %s\n",
 | |
| @@ -1247,9 +1311,6 @@
 | |
|  		unsigned transfer_in_flight;
 | |
|  		unsigned started;
 | |
|  
 | |
| -		if (dep->flags & DWC3_EP_STALL)
 | |
| -			return 0;
 | |
| -
 | |
|  		if (dep->number > 1)
 | |
|  			trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
 | |
|  		else
 | |
| @@ -1260,9 +1321,6 @@
 | |
|  
 | |
|  		if (!protocol && ((dep->direction && transfer_in_flight) ||
 | |
|  				(!dep->direction && started))) {
 | |
| -			dwc3_trace(trace_dwc3_gadget,
 | |
| -					"%s: pending request, cannot halt",
 | |
| -					dep->name);
 | |
|  			return -EAGAIN;
 | |
|  		}
 | |
|  
 | |
| @@ -1274,8 +1332,6 @@
 | |
|  		else
 | |
|  			dep->flags |= DWC3_EP_STALL;
 | |
|  	} else {
 | |
| -		if (!(dep->flags & DWC3_EP_STALL))
 | |
| -			return 0;
 | |
|  
 | |
|  		ret = dwc3_send_clear_stall_ep_cmd(dep);
 | |
|  		if (ret)
 | |
| @@ -1355,15 +1411,21 @@
 | |
|  
 | |
|  /* -------------------------------------------------------------------------- */
 | |
|  
 | |
| -static int dwc3_gadget_get_frame(struct usb_gadget *g)
 | |
| +static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
 | |
|  {
 | |
| -	struct dwc3		*dwc = gadget_to_dwc(g);
 | |
|  	u32			reg;
 | |
|  
 | |
|  	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 | |
|  	return DWC3_DSTS_SOFFN(reg);
 | |
|  }
 | |
|  
 | |
| +static int dwc3_gadget_get_frame(struct usb_gadget *g)
 | |
| +{
 | |
| +	struct dwc3		*dwc = gadget_to_dwc(g);
 | |
| +
 | |
| +	return __dwc3_gadget_get_frame(dwc);
 | |
| +}
 | |
| +
 | |
|  static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
 | |
|  {
 | |
|  	int			retries;
 | |
| @@ -1384,10 +1446,8 @@
 | |
|  
 | |
|  	speed = reg & DWC3_DSTS_CONNECTSPD;
 | |
|  	if ((speed == DWC3_DSTS_SUPERSPEED) ||
 | |
| -	    (speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
 | |
| -		dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed");
 | |
| +	    (speed == DWC3_DSTS_SUPERSPEED_PLUS))
 | |
|  		return 0;
 | |
| -	}
 | |
|  
 | |
|  	link_state = DWC3_DSTS_USBLNKST(reg);
 | |
|  
 | |
| @@ -1396,9 +1456,6 @@
 | |
|  	case DWC3_LINK_STATE_U3:	/* in HS, means SUSPEND */
 | |
|  		break;
 | |
|  	default:
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"can't wakeup from '%s'",
 | |
| -				dwc3_gadget_link_string(link_state));
 | |
|  		return -EINVAL;
 | |
|  	}
 | |
|  
 | |
| @@ -1503,11 +1560,6 @@
 | |
|  	if (!timeout)
 | |
|  		return -ETIMEDOUT;
 | |
|  
 | |
| -	dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
 | |
| -			dwc->gadget_driver
 | |
| -			? dwc->gadget_driver->function : "no-function",
 | |
| -			is_on ? "connect" : "disconnect");
 | |
| -
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| @@ -1677,6 +1729,7 @@
 | |
|  
 | |
|  	/* begin to receive SETUP packets */
 | |
|  	dwc->ep0state = EP0_SETUP_PHASE;
 | |
| +	dwc->link_state = DWC3_LINK_STATE_SS_DIS;
 | |
|  	dwc3_ep0_out_start(dwc);
 | |
|  
 | |
|  	dwc3_gadget_enable_irq(dwc);
 | |
| @@ -1767,6 +1820,81 @@
 | |
|  	.udc_stop		= dwc3_gadget_stop,
 | |
|  };
 | |
|  
 | |
| +static int dwc3_gadget_init_hw_all_endpoints(struct dwc3 *dwc)
 | |
| +{
 | |
| +	struct dwc3_ep			*dep;
 | |
| +	struct dwc3_hwparams    *parms = &dwc->hwparams;
 | |
| +	u32 direction = dwc->eps_directions;
 | |
| +	u8 num_eps = DWC3_NUM_EPS(parms);
 | |
| +	u8 num_in_eps = 0;
 | |
| +	u8 num_out_eps = 0;
 | |
| +	u8 epnum = 0;
 | |
| +	u8 i;
 | |
| +
 | |
| +	if (!direction)
 | |
| +		direction = DWC3_EPS_DEFAULT_DIRECTIONS;
 | |
| +
 | |
| +	for (i = 0; i < num_eps; i++) {
 | |
| +		if (direction & 0x1)
 | |
| +			epnum = (num_in_eps++ << 1) + 1;
 | |
| +		else
 | |
| +			epnum = (num_out_eps++ << 1);
 | |
| +
 | |
| +		dep = kzalloc(sizeof(*dep), GFP_KERNEL);
 | |
| +		if (!dep)
 | |
| +			return -ENOMEM;
 | |
| +
 | |
| +		dep->dwc = dwc;
 | |
| +		dep->number = epnum;
 | |
| +		dep->direction = !!(direction & 0x1);
 | |
| +		dep->regs = dwc->regs + DWC3_DEP_BASE(i);
 | |
| +		dwc->eps[i] = dep;
 | |
| +
 | |
| +		snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
 | |
| +				(epnum & 1) ? "in" : "out");
 | |
| +
 | |
| +		dep->endpoint.name = dep->name;
 | |
| +		spin_lock_init(&dep->lock);
 | |
| +
 | |
| +		if (epnum == 0 || epnum == 1) {
 | |
| +			usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
 | |
| +			dep->endpoint.maxburst = 1;
 | |
| +			dep->endpoint.ops = &dwc3_gadget_ep0_ops;
 | |
| +			if (!epnum)
 | |
| +				dwc->gadget.ep0 = &dep->endpoint;
 | |
| +		} else {
 | |
| +			int		ret;
 | |
| +
 | |
| +			usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
 | |
| +			dep->endpoint.max_streams = 15;
 | |
| +			dep->endpoint.ops = &dwc3_gadget_ep_ops;
 | |
| +			list_add_tail(&dep->endpoint.ep_list,
 | |
| +					&dwc->gadget.ep_list);
 | |
| +
 | |
| +			ret = dwc3_alloc_trb_pool(dep);
 | |
| +			if (ret)
 | |
| +				return ret;
 | |
| +		}
 | |
| +
 | |
| +		if (epnum == 0 || epnum == 1) {
 | |
| +			dep->endpoint.caps.type_control = true;
 | |
| +		} else {
 | |
| +			dep->endpoint.caps.type_iso = true;
 | |
| +			dep->endpoint.caps.type_bulk = true;
 | |
| +			dep->endpoint.caps.type_int = true;
 | |
| +		}
 | |
| +
 | |
| +		dep->endpoint.caps.dir_in = !!(direction & 0x1);
 | |
| +		dep->endpoint.caps.dir_out = !(direction & 0x1);
 | |
| +		direction = (direction >> 1);
 | |
| +
 | |
| +		INIT_LIST_HEAD(&dep->pending_list);
 | |
| +		INIT_LIST_HEAD(&dep->started_list);
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  /* -------------------------------------------------------------------------- */
 | |
|  
 | |
|  static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
 | |
| @@ -1794,8 +1922,6 @@
 | |
|  		dep->endpoint.name = dep->name;
 | |
|  		spin_lock_init(&dep->lock);
 | |
|  
 | |
| -		dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
 | |
| -
 | |
|  		if (epnum == 0 || epnum == 1) {
 | |
|  			usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
 | |
|  			dep->endpoint.maxburst = 1;
 | |
| @@ -1840,17 +1966,24 @@
 | |
|  
 | |
|  	INIT_LIST_HEAD(&dwc->gadget.ep_list);
 | |
|  
 | |
| +	if (dwc->eps_new_init) {
 | |
| +		ret = dwc3_gadget_init_hw_all_endpoints(dwc);
 | |
| +		if (ret < 0) {
 | |
| +			dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
 | |
| +			return ret;
 | |
| +		}
 | |
| +		return 0;
 | |
| +	}
 | |
| +
 | |
|  	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
 | |
|  	if (ret < 0) {
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"failed to allocate OUT endpoints");
 | |
| +		dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
 | |
|  		return ret;
 | |
|  	}
 | |
|  
 | |
|  	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
 | |
|  	if (ret < 0) {
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"failed to allocate IN endpoints");
 | |
| +		dev_err(dwc->dev, "failed to initialize IN endpoints\n");
 | |
|  		return ret;
 | |
|  	}
 | |
|  
 | |
| @@ -1886,14 +2019,12 @@
 | |
|  
 | |
|  /* -------------------------------------------------------------------------- */
 | |
|  
 | |
| -static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
 | |
| -		struct dwc3_request *req, struct dwc3_trb *trb,
 | |
| -		const struct dwc3_event_depevt *event, int status,
 | |
| -		int chain)
 | |
| +static int __dwc3_cleanup_done_trbs(struct dwc3_ep *dep, struct dwc3_request *req,
 | |
| +		struct dwc3_trb *trb, const struct dwc3_event_depevt *event,
 | |
| +		int status, int chain)
 | |
|  {
 | |
|  	unsigned int		count;
 | |
|  	unsigned int		s_pkt = 0;
 | |
| -	unsigned int		trb_status;
 | |
|  
 | |
|  	dwc3_ep_inc_deq(dep);
 | |
|  
 | |
| @@ -1922,37 +2053,6 @@
 | |
|  	req->request.actual += count;
 | |
|  
 | |
|  	if (dep->direction) {
 | |
| -		if (count) {
 | |
| -			trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
 | |
| -			if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
 | |
| -				dwc3_trace(trace_dwc3_gadget,
 | |
| -						"%s: incomplete IN transfer",
 | |
| -						dep->name);
 | |
| -				/*
 | |
| -				 * If missed isoc occurred and there is
 | |
| -				 * no request queued then issue END
 | |
| -				 * TRANSFER, so that core generates
 | |
| -				 * next xfernotready and we will issue
 | |
| -				 * a fresh START TRANSFER.
 | |
| -				 * If there are still queued request
 | |
| -				 * then wait, do not issue either END
 | |
| -				 * or UPDATE TRANSFER, just attach next
 | |
| -				 * request in pending_list during
 | |
| -				 * giveback.If any future queued request
 | |
| -				 * is successfully transferred then we
 | |
| -				 * will issue UPDATE TRANSFER for all
 | |
| -				 * request in the pending_list.
 | |
| -				 */
 | |
| -				dep->flags |= DWC3_EP_MISSED_ISOC;
 | |
| -			} else {
 | |
| -				dev_err(dwc->dev, "incomplete IN transfer %s\n",
 | |
| -						dep->name);
 | |
| -				status = -ECONNRESET;
 | |
| -			}
 | |
| -		} else {
 | |
| -			dep->flags &= ~DWC3_EP_MISSED_ISOC;
 | |
| -		}
 | |
| -	} else {
 | |
|  		if (count && (event->status & DEPEVT_STATUS_SHORT))
 | |
|  			s_pkt = 1;
 | |
|  	}
 | |
| @@ -1967,7 +2067,7 @@
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| -static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
 | |
| +static int dwc3_cleanup_done_reqs(struct dwc3_ep *dep,
 | |
|  		const struct dwc3_event_depevt *event, int status)
 | |
|  {
 | |
|  	struct dwc3_request	*req, *n;
 | |
| @@ -1994,14 +2094,14 @@
 | |
|  				req->sg = sg_next(s);
 | |
|  				req->num_pending_sgs--;
 | |
|  
 | |
| -				ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
 | |
| +				ret = __dwc3_cleanup_done_trbs(dep, req, trb,
 | |
|  						event, status, chain);
 | |
|  				if (ret)
 | |
|  					break;
 | |
|  			}
 | |
|  		} else {
 | |
|  			trb = &dep->trb_pool[dep->trb_dequeue];
 | |
| -			ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
 | |
| +			ret = __dwc3_cleanup_done_trbs(dep, req, trb,
 | |
|  					event, status, chain);
 | |
|  		}
 | |
|  
 | |
| @@ -2045,9 +2145,9 @@
 | |
|  			 * flag, so that END TRANSFER is issued when an
 | |
|  			 * entry is added into request list.
 | |
|  			 */
 | |
| -			dep->flags = DWC3_EP_PENDING_REQUEST;
 | |
| +			dep->flags |= DWC3_EP_PENDING_REQUEST;
 | |
|  		} else {
 | |
| -			dwc3_stop_active_transfer(dwc, dep->number, true);
 | |
| +			dwc3_stop_active_transfer(dep, true);
 | |
|  			dep->flags = DWC3_EP_ENABLED;
 | |
|  		}
 | |
|  		return 1;
 | |
| @@ -2059,19 +2159,28 @@
 | |
|  	return 1;
 | |
|  }
 | |
|  
 | |
| -static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
 | |
| -		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
 | |
| +static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
 | |
| +		const struct dwc3_event_depevt *event)
 | |
| +{
 | |
| +	dep->frame_number = event->parameters;
 | |
| +}
 | |
| +
 | |
| +static void dwc3_endpoint_transfer_complete(struct dwc3_ep *dep,
 | |
| +		const struct dwc3_event_depevt *event)
 | |
|  {
 | |
| +	struct dwc3     *dwc = dep->dwc;
 | |
|  	unsigned		status = 0;
 | |
|  	int			clean_busy;
 | |
|  	u32			is_xfer_complete;
 | |
|  
 | |
| +	dwc3_gadget_endpoint_frame_from_event(dep, event);
 | |
| +
 | |
|  	is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
 | |
|  
 | |
|  	if (event->status & DEPEVT_STATUS_BUSERR)
 | |
|  		status = -ECONNRESET;
 | |
|  
 | |
| -	clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
 | |
| +	clean_busy = dwc3_cleanup_done_reqs(dep, event, status);
 | |
|  	if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
 | |
|  				usb_endpoint_xfer_isoc(dep->endpoint.desc)))
 | |
|  		dep->flags &= ~DWC3_EP_BUSY;
 | |
| @@ -2109,12 +2218,16 @@
 | |
|  	if (!dep->endpoint.desc)
 | |
|  		return;
 | |
|  
 | |
| -	if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 | |
| +	if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) || (dep->flags&DWC3_EP_ENABLED)) {
 | |
|  		int ret;
 | |
| -
 | |
| -		ret = __dwc3_gadget_kick_transfer(dep, 0);
 | |
| -		if (!ret || ret == -EBUSY)
 | |
| -			return;
 | |
| +		if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
 | |
| +			(!(dep->flags & (DWC3_EP_BUSY | DWC3_EP_UPDATE)))) {
 | |
| +			__dwc3_gadget_start_isoc(dep);
 | |
| +		} else {
 | |
| +			ret = __dwc3_gadget_kick_transfer(dep, 0);
 | |
| +			if (!ret || ret == -EBUSY)
 | |
| +				return;
 | |
| +		}
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| @@ -2139,37 +2252,24 @@
 | |
|  		dep->resource_index = 0;
 | |
|  
 | |
|  		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 | |
| -			dwc3_trace(trace_dwc3_gadget,
 | |
| -					"%s is an Isochronous endpoint",
 | |
| -					dep->name);
 | |
| +			dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
 | |
|  			return;
 | |
|  		}
 | |
|  
 | |
| -		dwc3_endpoint_transfer_complete(dwc, dep, event);
 | |
| +		dwc3_endpoint_transfer_complete(dep, event);
 | |
|  		break;
 | |
|  	case DWC3_DEPEVT_XFERINPROGRESS:
 | |
| -		dwc3_endpoint_transfer_complete(dwc, dep, event);
 | |
| +		dwc3_endpoint_transfer_complete(dep, event);
 | |
|  		break;
 | |
|  	case DWC3_DEPEVT_XFERNOTREADY:
 | |
|  		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 | |
| -			dwc3_gadget_start_isoc(dwc, dep, event);
 | |
| +			dwc3_gadget_start_isoc(dep, event);
 | |
|  		} else {
 | |
| -			int active;
 | |
|  			int ret;
 | |
|  
 | |
| -			active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
 | |
| -
 | |
| -			dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
 | |
| -					dep->name, active ? "Transfer Active"
 | |
| -					: "Transfer Not Active");
 | |
| -
 | |
|  			ret = __dwc3_gadget_kick_transfer(dep, 0);
 | |
|  			if (!ret || ret == -EBUSY)
 | |
|  				return;
 | |
| -
 | |
| -			dwc3_trace(trace_dwc3_gadget,
 | |
| -					"%s: failed to kick transfers",
 | |
| -					dep->name);
 | |
|  		}
 | |
|  
 | |
|  		break;
 | |
| @@ -2179,26 +2279,9 @@
 | |
|  					dep->name);
 | |
|  			return;
 | |
|  		}
 | |
| -
 | |
| -		switch (event->status) {
 | |
| -		case DEPEVT_STREAMEVT_FOUND:
 | |
| -			dwc3_trace(trace_dwc3_gadget,
 | |
| -					"Stream %d found and started",
 | |
| -					event->parameters);
 | |
| -
 | |
| -			break;
 | |
| -		case DEPEVT_STREAMEVT_NOTFOUND:
 | |
| -			/* FALLTHROUGH */
 | |
| -		default:
 | |
| -			dwc3_trace(trace_dwc3_gadget,
 | |
| -					"unable to find suitable stream");
 | |
| -		}
 | |
|  		break;
 | |
|  	case DWC3_DEPEVT_RXTXFIFOEVT:
 | |
| -		dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name);
 | |
| -		break;
 | |
|  	case DWC3_DEPEVT_EPCMDCMPLT:
 | |
| -		dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
 | |
|  		break;
 | |
|  	}
 | |
|  }
 | |
| @@ -2242,15 +2325,13 @@
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
 | |
| +static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
 | |
|  {
 | |
| -	struct dwc3_ep *dep;
 | |
| +	struct dwc3 *dwc = dep->dwc;
 | |
|  	struct dwc3_gadget_ep_cmd_params params;
 | |
|  	u32 cmd;
 | |
|  	int ret;
 | |
|  
 | |
| -	dep = dwc->eps[epnum];
 | |
| -
 | |
|  	if (!dep->resource_index)
 | |
|  		return;
 | |
|  
 | |
| @@ -2596,8 +2677,6 @@
 | |
|  			(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
 | |
|  		if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
 | |
|  				(next == DWC3_LINK_STATE_RESUME)) {
 | |
| -			dwc3_trace(trace_dwc3_gadget,
 | |
| -					"ignoring transition U3 -> Resume");
 | |
|  			return;
 | |
|  		}
 | |
|  	}
 | |
| @@ -2731,11 +2810,7 @@
 | |
|  		break;
 | |
|  	case DWC3_DEVICE_EVENT_EOPF:
 | |
|  		/* It changed to be suspend event for version 2.30a and above */
 | |
| -		if (dwc->revision < DWC3_REVISION_230A) {
 | |
| -			dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
 | |
| -		} else {
 | |
| -			dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
 | |
| -
 | |
| +		if (dwc->revision >= DWC3_REVISION_230A) {
 | |
|  			/*
 | |
|  			 * Ignore suspend event until the gadget enters into
 | |
|  			 * USB_STATE_CONFIGURED state.
 | |
| @@ -2746,26 +2821,21 @@
 | |
|  		}
 | |
|  		break;
 | |
|  	case DWC3_DEVICE_EVENT_SOF:
 | |
| -		dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
 | |
| -		break;
 | |
|  	case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
 | |
| -		dwc3_trace(trace_dwc3_gadget, "Erratic Error");
 | |
| -		break;
 | |
|  	case DWC3_DEVICE_EVENT_CMD_CMPL:
 | |
| -		dwc3_trace(trace_dwc3_gadget, "Command Complete");
 | |
| -		break;
 | |
|  	case DWC3_DEVICE_EVENT_OVERFLOW:
 | |
| -		dwc3_trace(trace_dwc3_gadget, "Overflow");
 | |
|  		break;
 | |
| +
 | |
|  	default:
 | |
|  		dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
 | |
|  	}
 | |
| +	dwc3_gadget_sync_connected_status(dwc);
 | |
|  }
 | |
|  
 | |
|  static void dwc3_process_event_entry(struct dwc3 *dwc,
 | |
|  		const union dwc3_event *event)
 | |
|  {
 | |
| -	trace_dwc3_event(event->raw);
 | |
| +	trace_dwc3_event(event->raw, dwc);
 | |
|  
 | |
|  	/* Endpoint IRQ, handle it and return early */
 | |
|  	if (event->type.is_devspec == 0) {
 | |
| @@ -2984,8 +3054,7 @@
 | |
|  	 * composite.c that we are USB 2.0 + LPM ECN.
 | |
|  	 */
 | |
|  	if (dwc->revision < DWC3_REVISION_220A)
 | |
| -		dwc3_trace(trace_dwc3_gadget,
 | |
| -				"Changing max_speed on rev %08x",
 | |
| +		dev_info(dwc->dev, "changing max_speed on rev %08x\n",
 | |
|  				dwc->revision);
 | |
|  
 | |
|  	dwc->gadget.max_speed		= dwc->maximum_speed;
 | |
| @@ -3005,6 +3074,8 @@
 | |
|  	if (ret)
 | |
|  		goto err5;
 | |
|  
 | |
| +	dwc3_proc_init(dwc);
 | |
| +
 | |
|  	ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
 | |
|  	if (ret) {
 | |
|  		dev_err(dwc->dev, "failed to register udc\n");
 | |
| @@ -3042,6 +3113,8 @@
 | |
|  {
 | |
|  	usb_del_gadget_udc(&dwc->gadget);
 | |
|  
 | |
| +	dwc3_proc_shutdown(dwc);
 | |
| +
 | |
|  	dwc3_gadget_free_endpoints(dwc);
 | |
|  
 | |
|  	dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
 | |
| @@ -3106,3 +3179,70 @@
 | |
|  		enable_irq(dwc->irq_gadget);
 | |
|  	}
 | |
|  }
 | |
| +
 | |
| +/*
 | |
| + * dwc3_gadget_sync_connected_status() function just for
 | |
| + * user space get udc connected status. and this function
 | |
| + * just report three status: Disconnected, Connected host,
 | |
| + * Connected charger.
 | |
| + *
 | |
| + * After some tests, report connected udc connected status by
 | |
| + * DSTS register, [21:18]USB Link Status and [2:0] Connect Speed.
 | |
| + *
 | |
| + * How to identify whitch status is the UDC connected?
 | |
| + * 1. Host connected:
 | |
| + * Host would reset udc, so dwc3 core get reset event interrupt,
 | |
| + * dwc3_gadget_reset_interrupt() set dwc->connected = true,
 | |
| + * so check connected host by dwc->connected == true
 | |
| + *
 | |
| + * 2. Disconnected:
 | |
| + * When vbus detect 5V lose, dwc3 core generated a disconnect event intr.
 | |
| + * dwc3_gadget_disconnect_interrupt() set dwc->connected = false.
 | |
| + * so check disconnected by dwc->connected == false
 | |
| + *
 | |
| + * 3. Charger connected: (Fixedme)
 | |
| + * As dwc3 core size, DP keep pullup register when connected to charger.
 | |
| + * no any port reset action created, so dwc3 would entry FullSpeed mode.
 | |
| + * so chect connected charger by FullSpeed && dwc->connectd == false
 | |
| + *
 | |
| + */
 | |
| +static void dwc3_gadget_sync_connected_status(struct dwc3 *dwc)
 | |
| +{
 | |
| +	u32 reg;
 | |
| +	u8	speed;
 | |
| +	u8  state;
 | |
| +	static int prev = UDC_DISCONNECTED;
 | |
| +
 | |
| +	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 | |
| +	speed = reg & DWC3_DSTS_CONNECTSPD;
 | |
| +	state = DWC3_DSTS_USBLNKST(reg);
 | |
| +
 | |
| +	/*
 | |
| +	 * step1 check is connected host?
 | |
| +	 */
 | |
| +	if (dwc->connected == true) {
 | |
| +		if (prev != UDC_CONNECT_HOST)
 | |
| +		dwc->udc_connect_status = UDC_CONNECT_HOST;
 | |
| +		goto out;
 | |
| +	}
 | |
| +
 | |
| +	/*
 | |
| +	 * step2 disconectd status && fullspeed mode,
 | |
| +	 * as connected charger.
 | |
| +	 */
 | |
| +	if ((speed == DWC3_DSTS_FULLSPEED) && (state != DWC3_LINK_STATE_SS_DIS)) {
 | |
| +		if (prev != UDC_CONNECT_CHARGER)
 | |
| +		dwc->udc_connect_status = UDC_CONNECT_CHARGER;
 | |
| +		goto out;
 | |
| +	}
 | |
| +
 | |
| +	/*
 | |
| +	 * step3 not host and charger connected, so just
 | |
| +	 * disconnectd.
 | |
| +	 */
 | |
| +	if (prev != UDC_DISCONNECTED)
 | |
| +	dwc->udc_connect_status = UDC_DISCONNECTED;
 | |
| +
 | |
| +out:
 | |
| +	prev = dwc->udc_connect_status;
 | |
| +}
 |