Commit b311048c authored by Thinh Nguyen's avatar Thinh Nguyen Committed by Greg Kroah-Hartman

usb: dwc3: gadget: Rewrite endpoint allocation flow

The driver dwc3 deviates from the programming guide in regard to
endpoint configuration. It does this command sequence:

DEPSTARTCFG -> DEPXFERCFG -> DEPCFG

Instead of the suggested flow:

DEPSTARTCFG -> DEPCFG -> DEPXFERCFG

The reasons for this deviation were as follow, quoted:

	1) The databook says to do %DWC3_DEPCMD_DEPSTARTCFG for every
	   %USB_REQ_SET_CONFIGURATION and %USB_REQ_SET_INTERFACE
	   (8.1.5). This is incorrect in the scenario of multiple
	   interfaces.

	2) The databook does not mention doing more
	   %DWC3_DEPCMD_DEPXFERCFG for new endpoint on alt setting
	   (8.1.6).

Regarding 1), DEPSTARTCFG resets the endpoints' resource and can be a
problem if used with SET_INTERFACE request of a multiple interface
configuration. But we can still satisfy the programming guide
requirement by assigning the endpoint resource as part of
usb_ep_enable(). We will only reset endpoint resources on controller
initialization and SET_CONFIGURATION request.

Regarding 2), the later versions of the programming guide were updated
to clarify this flow (see "Alternate Initialization on SetInterface
Request" of the programming guide). As long as the platform has enough
physical endpoints, we can assign resource to a new endpoint.

The order of the command sequence will not be a problem to most
platforms for the current implementation of the dwc3 driver. However,
this order is required in different scenarios (such as initialization
during controller's hibernation restore). Let's keep the flow consistent
and follow the programming guide.
Signed-off-by: default avatarThinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/c143583a5afb087deb8c3aa5eb227ee23515f272.1706754219.git.Thinh.Nguyen@synopsys.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d4718eff
...@@ -756,6 +756,7 @@ struct dwc3_ep { ...@@ -756,6 +756,7 @@ struct dwc3_ep {
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11) #define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12) #define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13) #define DWC3_EP_DELAY_STOP BIT(13)
#define DWC3_EP_RESOURCE_ALLOCATED BIT(14)
/* This last one is specific to EP0 */ /* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31) #define DWC3_EP0_DIR_IN BIT(31)
......
...@@ -646,6 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ...@@ -646,6 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL; return -EINVAL;
case USB_STATE_ADDRESS: case USB_STATE_ADDRESS:
dwc3_gadget_start_config(dwc, 2);
dwc3_gadget_clear_tx_fifos(dwc); dwc3_gadget_clear_tx_fifos(dwc);
ret = dwc3_ep0_delegate_req(dwc, ctrl); ret = dwc3_ep0_delegate_req(dwc, ctrl);
......
...@@ -519,77 +519,56 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep) ...@@ -519,77 +519,56 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
{ {
struct dwc3_gadget_ep_cmd_params params; struct dwc3_gadget_ep_cmd_params params;
int ret;
if (dep->flags & DWC3_EP_RESOURCE_ALLOCATED)
return 0;
memset(&params, 0x00, sizeof(params)); memset(&params, 0x00, sizeof(params));
params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE, ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
&params); &params);
if (ret)
return ret;
dep->flags |= DWC3_EP_RESOURCE_ALLOCATED;
return 0;
} }
/** /**
* dwc3_gadget_start_config - configure ep resources * dwc3_gadget_start_config - reset endpoint resources
* @dep: endpoint that is being enabled * @dwc: pointer to the DWC3 context
* * @resource_index: DEPSTARTCFG.XferRscIdx value (must be 0 or 2)
* Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's
* completion, it will set Transfer Resource for all available endpoints.
*
* The assignment of transfer resources cannot perfectly follow the data book
* due to the fact that the controller driver does not have all knowledge of the
* configuration in advance. It is given this information piecemeal by the
* composite gadget framework after every SET_CONFIGURATION and
* SET_INTERFACE. Trying to follow the databook programming model in this
* scenario can cause errors. For two reasons:
*
* 1) The databook says to do %DWC3_DEPCMD_DEPSTARTCFG for every
* %USB_REQ_SET_CONFIGURATION and %USB_REQ_SET_INTERFACE (8.1.5). This is
* incorrect in the scenario of multiple interfaces.
*
* 2) The databook does not mention doing more %DWC3_DEPCMD_DEPXFERCFG for new
* endpoint on alt setting (8.1.6).
*
* The following simplified method is used instead:
* *
* All hardware endpoints can be assigned a transfer resource and this setting * Set resource_index=0 to reset all endpoints' resources allocation. Do this as
* will stay persistent until either a core reset or hibernation. So whenever we * part of the power-on/soft-reset initialization.
* do a %DWC3_DEPCMD_DEPSTARTCFG(0) we can go ahead and do
* %DWC3_DEPCMD_DEPXFERCFG for every hardware endpoint as well. We are
* guaranteed that there are as many transfer resources as endpoints.
* *
* This function is called for each endpoint when it is being enabled but is * Set resource_index=2 to reset only non-control endpoints' resources. Do this
* triggered only when called for EP0-out, which always happens first, and which * on receiving the SET_CONFIGURATION request or hibernation resume.
* should only happen in one of the above conditions.
*/ */
static int dwc3_gadget_start_config(struct dwc3_ep *dep) int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
{ {
struct dwc3_gadget_ep_cmd_params params; struct dwc3_gadget_ep_cmd_params params;
struct dwc3 *dwc;
u32 cmd; u32 cmd;
int i; int i;
int ret; int ret;
if (dep->number) if (resource_index != 0 && resource_index != 2)
return 0; return -EINVAL;
memset(&params, 0x00, sizeof(params)); memset(&params, 0x00, sizeof(params));
cmd = DWC3_DEPCMD_DEPSTARTCFG; cmd = DWC3_DEPCMD_DEPSTARTCFG;
dwc = dep->dwc; cmd |= DWC3_DEPCMD_PARAM(resource_index);
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params); ret = dwc3_send_gadget_ep_cmd(dwc->eps[0], cmd, &params);
if (ret) if (ret)
return ret; return ret;
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { /* Reset resource allocation flags */
struct dwc3_ep *dep = dwc->eps[i]; for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
if (!dep)
continue;
ret = dwc3_gadget_set_xfer_resource(dep);
if (ret)
return ret;
}
return 0; return 0;
} }
...@@ -890,16 +869,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) ...@@ -890,16 +869,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
ret = dwc3_gadget_resize_tx_fifos(dep); ret = dwc3_gadget_resize_tx_fifos(dep);
if (ret) if (ret)
return ret; return ret;
ret = dwc3_gadget_start_config(dep);
if (ret)
return ret;
} }
ret = dwc3_gadget_set_ep_config(dep, action); ret = dwc3_gadget_set_ep_config(dep, action);
if (ret) if (ret)
return ret; return ret;
if (!(dep->flags & DWC3_EP_RESOURCE_ALLOCATED)) {
ret = dwc3_gadget_set_xfer_resource(dep);
if (ret)
return ret;
}
if (!(dep->flags & DWC3_EP_ENABLED)) { if (!(dep->flags & DWC3_EP_ENABLED)) {
struct dwc3_trb *trb_st_hw; struct dwc3_trb *trb_st_hw;
struct dwc3_trb *trb_link; struct dwc3_trb *trb_link;
...@@ -1053,7 +1034,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) ...@@ -1053,7 +1034,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false; dep->stream_capable = false;
dep->type = 0; dep->type = 0;
mask = DWC3_EP_TXFIFO_RESIZED; mask = DWC3_EP_TXFIFO_RESIZED | DWC3_EP_RESOURCE_ALLOCATED;
/* /*
* dwc3_remove_requests() can exit early if DWC3 EP delayed stop is * dwc3_remove_requests() can exit early if DWC3 EP delayed stop is
* set. Do not clear DEP flags, so that the end transfer command will * set. Do not clear DEP flags, so that the end transfer command will
...@@ -2914,6 +2895,12 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) ...@@ -2914,6 +2895,12 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
/* Start with SuperSpeed Default */ /* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
ret = dwc3_gadget_start_config(dwc, 0);
if (ret) {
dev_err(dwc->dev, "failed to config endpoints\n");
return ret;
}
dep = dwc->eps[0]; dep = dwc->eps[0];
dep->flags = 0; dep->flags = 0;
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
......
...@@ -121,6 +121,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, ...@@ -121,6 +121,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc); void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index);
/** /**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
......
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