]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - drivers/usb/dwc3/gadget.c
Merge v6.8-rc6 into usb-next
[thirdparty/kernel/stable.git] / drivers / usb / dwc3 / gadget.c
index 28f49400f3e8b178e23c881120577da461178c35..40c52dbc28d3b4a1b2ff9580dc183e0db1152bdd 100644 (file)
@@ -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)
 {
        struct dwc3_gadget_ep_cmd_params params;
+       int ret;
+
+       if (dep->flags & DWC3_EP_RESOURCE_ALLOCATED)
+               return 0;
 
        memset(&params, 0x00, sizeof(params));
 
        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);
+       if (ret)
+               return ret;
+
+       dep->flags |= DWC3_EP_RESOURCE_ALLOCATED;
+       return 0;
 }
 
 /**
- * dwc3_gadget_start_config - configure ep resources
- * @dep: endpoint that is being enabled
- *
- * 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:
+ * dwc3_gadget_start_config - reset endpoint resources
+ * @dwc: pointer to the DWC3 context
+ * @resource_index: DEPSTARTCFG.XferRscIdx value (must be 0 or 2)
  *
- * All hardware endpoints can be assigned a transfer resource and this setting
- * will stay persistent until either a core reset or hibernation. So whenever we
- * 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.
+ * Set resource_index=0 to reset all endpoints' resources allocation. Do this as
+ * part of the power-on/soft-reset initialization.
  *
- * This function is called for each endpoint when it is being enabled but is
- * triggered only when called for EP0-out, which always happens first, and which
- * should only happen in one of the above conditions.
+ * Set resource_index=2 to reset only non-control endpoints' resources. Do this
+ * on receiving the SET_CONFIGURATION request or hibernation resume.
  */
-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             *dwc;
        u32                     cmd;
        int                     i;
        int                     ret;
 
-       if (dep->number)
-               return 0;
+       if (resource_index != 0 && resource_index != 2)
+               return -EINVAL;
 
        memset(&params, 0x00, sizeof(params));
        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)
                return ret;
 
-       for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
-               struct dwc3_ep *dep = dwc->eps[i];
-
-               if (!dep)
-                       continue;
-
-               ret = dwc3_gadget_set_xfer_resource(dep);
-               if (ret)
-                       return ret;
-       }
+       /* Reset resource allocation flags */
+       for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
+               dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
 
        return 0;
 }
@@ -884,16 +863,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
                ret = dwc3_gadget_resize_tx_fifos(dep);
                if (ret)
                        return ret;
-
-               ret = dwc3_gadget_start_config(dep);
-               if (ret)
-                       return ret;
        }
 
        ret = dwc3_gadget_set_ep_config(dep, action);
        if (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)) {
                struct dwc3_trb *trb_st_hw;
                struct dwc3_trb *trb_link;
@@ -1047,7 +1028,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
 
        dep->stream_capable = false;
        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
         * set.  Do not clear DEP flags, so that the end transfer command will
@@ -2913,6 +2894,12 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
        /* Start with SuperSpeed Default */
        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->flags = 0;
        ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
@@ -3428,7 +3415,7 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
                struct dwc3_request *req, const struct dwc3_event_depevt *event,
                int status)
 {
-       struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
+       struct dwc3_trb *trb;
        struct scatterlist *sg = req->sg;
        struct scatterlist *s;
        unsigned int num_queued = req->num_queued_sgs;