diff options
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 120 | 
1 files changed, 71 insertions, 49 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 58fdfad96b4..c9e729a4bf6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -263,8 +263,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,  	if (req->request.status == -EINPROGRESS)  		req->request.status = status; -	usb_gadget_unmap_request(&dwc->gadget, &req->request, -			req->direction); +	if (dwc->ep0_bounced && dep->number == 0) +		dwc->ep0_bounced = false; +	else +		usb_gadget_unmap_request(&dwc->gadget, &req->request, +				req->direction);  	dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",  			req, dep->name, req->request.actual, @@ -431,15 +434,25 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)  static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,  		const struct usb_endpoint_descriptor *desc, -		const struct usb_ss_ep_comp_descriptor *comp_desc) +		const struct usb_ss_ep_comp_descriptor *comp_desc, +		bool ignore)  {  	struct dwc3_gadget_ep_cmd_params params;  	memset(¶ms, 0x00, sizeof(params));  	params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) -		| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) -		| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1); +		| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); + +	/* Burst size is only needed in SuperSpeed mode */ +	if (dwc->gadget.speed == USB_SPEED_SUPER) { +		u32 burst = dep->endpoint.maxburst - 1; + +		params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst); +	} + +	if (ignore) +		params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;  	params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN  		| DWC3_DEPCFG_XFER_NOT_READY_EN; @@ -498,7 +511,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)   */  static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,  		const struct usb_endpoint_descriptor *desc, -		const struct usb_ss_ep_comp_descriptor *comp_desc) +		const struct usb_ss_ep_comp_descriptor *comp_desc, +		bool ignore)  {  	struct dwc3		*dwc = dep->dwc;  	u32			reg; @@ -510,7 +524,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,  			return ret;  	} -	ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc); +	ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);  	if (ret)  		return ret; @@ -558,27 +572,7 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)  	if (!list_empty(&dep->req_queued)) {  		dwc3_stop_active_transfer(dwc, dep->number); -		/* -		 * NOTICE: We are violating what the Databook says about the -		 * EndTransfer command. Ideally we would _always_ wait for the -		 * EndTransfer Command Completion IRQ, but that's causing too -		 * much trouble synchronizing between us and gadget driver. -		 * -		 * We have discussed this with the IP Provider and it was -		 * suggested to giveback all requests here, but give HW some -		 * extra time to synchronize with the interconnect. We're using -		 * an arbitraty 100us delay for that. -		 * -		 * Note also that a similar handling was tested by Synopsys -		 * (thanks a lot Paul) and nothing bad has come out of it. -		 * In short, what we're doing is: -		 * -		 * - Issue EndTransfer WITH CMDIOC bit set -		 * - Wait 100us -		 * - giveback all requests to gadget driver -		 */ -		udelay(100); - +		/* - giveback all requests to gadget driver */  		while (!list_empty(&dep->req_queued)) {  			req = next_request(&dep->req_queued); @@ -657,6 +651,12 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,  	dep = to_dwc3_ep(ep);  	dwc = dep->dwc; +	if (dep->flags & DWC3_EP_ENABLED) { +		dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", +				dep->name); +		return 0; +	} +  	switch (usb_endpoint_type(desc)) {  	case USB_ENDPOINT_XFER_CONTROL:  		strlcat(dep->name, "-control", sizeof(dep->name)); @@ -674,16 +674,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,  		dev_err(dwc->dev, "invalid endpoint transfer type\n");  	} -	if (dep->flags & DWC3_EP_ENABLED) { -		dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", -				dep->name); -		return 0; -	} -  	dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);  	spin_lock_irqsave(&dwc->lock, flags); -	ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc); +	ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);  	spin_unlock_irqrestore(&dwc->lock, flags);  	return ret; @@ -1026,6 +1020,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,  	if (list_empty(&dep->request_list)) {  		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",  			dep->name); +		dep->flags |= DWC3_EP_PENDING_REQUEST;  		return;  	} @@ -1089,13 +1084,21 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)  	if (dep->flags & DWC3_EP_PENDING_REQUEST) {  		int	ret; -		ret = __dwc3_gadget_kick_transfer(dep, 0, true); -		if (ret && ret != -EBUSY) { -			struct dwc3	*dwc = dep->dwc; +		/* +		 * If xfernotready is already elapsed and it is a case +		 * of isoc transfer, then issue END TRANSFER, so that +		 * you can receive xfernotready again and can have +		 * notion of current microframe. +		 */ +		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { +			dwc3_stop_active_transfer(dwc, dep->number); +			return 0; +		} +		ret = __dwc3_gadget_kick_transfer(dep, 0, true); +		if (ret && ret != -EBUSY)  			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",  					dep->name); -		}  	}  	/* @@ -1104,16 +1107,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)  	 *    core may not see the modified TRB(s).  	 */  	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && -			(dep->flags & DWC3_EP_BUSY)) { +			(dep->flags & DWC3_EP_BUSY) && +			!(dep->flags & DWC3_EP_MISSED_ISOC)) {  		WARN_ON_ONCE(!dep->resource_index);  		ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,  				false); -		if (ret && ret != -EBUSY) { -			struct dwc3	*dwc = dep->dwc; - +		if (ret && ret != -EBUSY)  			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",  					dep->name); -		}  	}  	/* @@ -1518,14 +1519,14 @@ static int dwc3_gadget_start(struct usb_gadget *g,  	dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);  	dep = dwc->eps[0]; -	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); +	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);  	if (ret) {  		dev_err(dwc->dev, "failed to enable %s\n", dep->name);  		goto err0;  	}  	dep = dwc->eps[1]; -	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); +	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);  	if (ret) {  		dev_err(dwc->dev, "failed to enable %s\n", dep->name);  		goto err1; @@ -1750,7 +1751,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,  		int		i;  		for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { -			struct dwc3_ep	*dep = dwc->eps[i]; +			dep = dwc->eps[i];  			if (!(dep->flags & DWC3_EP_ENABLED))  				continue; @@ -1877,6 +1878,25 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)  	if (!dep->resource_index)  		return; +	/* +	 * NOTICE: We are violating what the Databook says about the +	 * EndTransfer command. Ideally we would _always_ wait for the +	 * EndTransfer Command Completion IRQ, but that's causing too +	 * much trouble synchronizing between us and gadget driver. +	 * +	 * We have discussed this with the IP Provider and it was +	 * suggested to giveback all requests here, but give HW some +	 * extra time to synchronize with the interconnect. We're using +	 * an arbitraty 100us delay for that. +	 * +	 * Note also that a similar handling was tested by Synopsys +	 * (thanks a lot Paul) and nothing bad has come out of it. +	 * In short, what we're doing is: +	 * +	 * - Issue EndTransfer WITH CMDIOC bit set +	 * - Wait 100us +	 */ +  	cmd = DWC3_DEPCMD_ENDTRANSFER;  	cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;  	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); @@ -1884,6 +1904,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)  	ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);  	WARN_ON_ONCE(ret);  	dep->resource_index = 0; + +	udelay(100);  }  static void dwc3_stop_active_transfers(struct dwc3 *dwc) @@ -2141,14 +2163,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)  	}  	dep = dwc->eps[0]; -	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); +	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);  	if (ret) {  		dev_err(dwc->dev, "failed to enable %s\n", dep->name);  		return;  	}  	dep = dwc->eps[1]; -	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); +	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);  	if (ret) {  		dev_err(dwc->dev, "failed to enable %s\n", dep->name);  		return;  |