diff options
author | Minas Harutyunyan <minas.harutyunyan@synopsys.com> | 2019-03-12 10:45:12 +0300 |
---|---|---|
committer | Felipe Balbi <felipe.balbi@linux.intel.com> | 2019-05-03 09:13:48 +0300 |
commit | b4c53b4ac66a75a93672abf08aafac64dfb08d00 (patch) | |
tree | f9935ef234f199c72bab94df876d4ccacd213f15 /drivers/usb/dwc2 | |
parent | c67d4262f61733cb4fb81954de53e34712962993 (diff) | |
download | linux-b4c53b4ac66a75a93672abf08aafac64dfb08d00.tar.xz |
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: Minas Harutyunyan <hminas@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r-- | drivers/usb/dwc2/core.h | 2 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 31 |
2 files changed, 29 insertions, 4 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 9f3fc8e18277..152ac41dfb2d 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -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]; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2e2f9cbf6a3d..5f314a10a116 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -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 */ - dwc2_hsotg_ep0_zlp(hsotg, true); - return; + if (!hsotg->delayed_status) + dwc2_hsotg_ep0_zlp(hsotg, true); } /* @@ -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)) - dwc2_hsotg_ep0_zlp(hsotg, true); + 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); + } } } |