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

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
...@@ -993,6 +993,7 @@ struct dwc2_hregs_backup { ...@@ -993,6 +993,7 @@ struct dwc2_hregs_backup {
* @ctrl_buff: Buffer for EP0 control requests. * @ctrl_buff: Buffer for EP0 control requests.
* @ctrl_req: Request for EP0 control packets. * @ctrl_req: Request for EP0 control packets.
* @ep0_state: EP0 control transfers state * @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 * @test_mode: USB test mode requested by the host
* @remote_wakeup_allowed: True if device is allowed to wake-up host by * @remote_wakeup_allowed: True if device is allowed to wake-up host by
* remote-wakeup signalling * remote-wakeup signalling
...@@ -1175,6 +1176,7 @@ struct dwc2_hsotg { ...@@ -1175,6 +1176,7 @@ struct dwc2_hsotg {
void *ep0_buff; void *ep0_buff;
void *ctrl_buff; void *ctrl_buff;
enum dwc2_ep0_state ep0_state; enum dwc2_ep0_state ep0_state;
unsigned delayed_status : 1;
u8 test_mode; u8 test_mode;
dma_addr_t setup_desc_dma[2]; dma_addr_t setup_desc_dma[2];
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/phy.h> #include <linux/usb/phy.h>
#include <linux/usb/composite.h>
#include "core.h" #include "core.h"
#include "hw.h" #include "hw.h"
...@@ -1446,6 +1448,11 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ...@@ -1446,6 +1448,11 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
return 0; 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 (first) {
if (!hs_ep->isochronous) { if (!hs_ep->isochronous) {
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
...@@ -1938,6 +1945,10 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg, ...@@ -1938,6 +1945,10 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); 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 * the request is either unhandlable, or is not formatted correctly
* so respond with a STALL for the status stage to indicate failure. * 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) ...@@ -2387,8 +2398,8 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
if (!using_desc_dma(hsotg) && epnum == 0 && if (!using_desc_dma(hsotg) && epnum == 0 &&
hsotg->ep0_state == DWC2_EP0_DATA_OUT) { hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
/* Move to STATUS IN */ /* Move to STATUS IN */
dwc2_hsotg_ep0_zlp(hsotg, true); if (!hsotg->delayed_status)
return; dwc2_hsotg_ep0_zlp(hsotg, true);
} }
/* /*
...@@ -3053,8 +3064,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, ...@@ -3053,8 +3064,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
/* Safety check EP0 state when STSPHSERCVD asserted */ /* Safety check EP0 state when STSPHSERCVD asserted */
if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) { if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
/* Move to STATUS IN for DDMA */ /* Move to STATUS IN for DDMA */
if (using_desc_dma(hsotg)) if (using_desc_dma(hsotg)) {
dwc2_hsotg_ep0_zlp(hsotg, true); 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);
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment