Commit b4c53b4a authored by Minas Harutyunyan's avatar Minas Harutyunyan Committed by Felipe Balbi
Browse files

usb: dwc2: Delayed status support



Added delayed status support for Control transfers.

Tested in all 3 modes: Slave, BDMA and DDMA.
Performed tests: USB CV (Ch9 and MSC), Control Read/Write tests
using Synopsys USB test environment function driver.

Signed-off-by: default avatarMinas Harutyunyan <hminas@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent c67d4262
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -993,6 +993,7 @@ struct dwc2_hregs_backup {
 * @ctrl_buff:          Buffer for EP0 control requests.
 * @ctrl_req:           Request for EP0 control packets.
 * @ep0_state:          EP0 control transfers state
 * @delayed_status:		true when gadget driver asks for delayed status
 * @test_mode:          USB test mode requested by the host
 * @remote_wakeup_allowed: True if device is allowed to wake-up host by
 *                      remote-wakeup signalling
@@ -1175,6 +1176,7 @@ struct dwc2_hsotg {
	void *ep0_buff;
	void *ctrl_buff;
	enum dwc2_ep0_state ep0_state;
	unsigned delayed_status : 1;
	u8 test_mode;

	dma_addr_t setup_desc_dma[2];
+27 −4
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/phy.h>
#include <linux/usb/composite.h>


#include "core.h"
#include "hw.h"
@@ -1446,6 +1448,11 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
		return 0;
	}

	/* Change EP direction if status phase request is after data out */
	if (!hs_ep->index && !req->length && !hs_ep->dir_in &&
	    hs->ep0_state == DWC2_EP0_DATA_OUT)
		hs_ep->dir_in = 1;

	if (first) {
		if (!hs_ep->isochronous) {
			dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
@@ -1938,6 +1945,10 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
			dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret);
	}

	hsotg->delayed_status = false;
	if (ret == USB_GADGET_DELAYED_STATUS)
		hsotg->delayed_status = true;

	/*
	 * the request is either unhandlable, or is not formatted correctly
	 * so respond with a STALL for the status stage to indicate failure.
@@ -2387,8 +2398,8 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
	if (!using_desc_dma(hsotg) && epnum == 0 &&
	    hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
		/* Move to STATUS IN */
		if (!hsotg->delayed_status)
			dwc2_hsotg_ep0_zlp(hsotg, true);
		return;
	}

	/*
@@ -3053,8 +3064,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
		/* Safety check EP0 state when STSPHSERCVD asserted */
		if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
			/* Move to STATUS IN for DDMA */
			if (using_desc_dma(hsotg))
			if (using_desc_dma(hsotg)) {
				if (!hsotg->delayed_status)
					dwc2_hsotg_ep0_zlp(hsotg, true);
				else
				/* In case of 3 stage Control Write with delayed
				 * status, when Status IN transfer started
				 * before STSPHSERCVD asserted, NAKSTS bit not
				 * cleared by CNAK in dwc2_hsotg_start_req()
				 * function. Clear now NAKSTS to allow complete
				 * transfer.
				 */
					dwc2_set_bit(hsotg, DIEPCTL(0),
						     DXEPCTL_CNAK);
			}
		}

	}