Merge tag 'usb-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
[linux-2.6-microblaze.git] / drivers / usb / dwc2 / gadget.c
index 3146df6..837237e 100644 (file)
@@ -1806,7 +1806,8 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
                case USB_ENDPOINT_HALT:
                        halted = ep->halted;
 
-                       dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
+                       if (!ep->wedged)
+                               dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
 
                        ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);
                        if (ret) {
@@ -4066,6 +4067,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
        hs_ep->isochronous = 0;
        hs_ep->periodic = 0;
        hs_ep->halted = 0;
+       hs_ep->wedged = 0;
        hs_ep->interval = desc->bInterval;
 
        switch (ep_type) {
@@ -4306,6 +4308,27 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
        return 0;
 }
 
+/**
+ * dwc2_gadget_ep_set_wedge - set wedge on a given endpoint
+ * @ep: The endpoint to be wedged.
+ *
+ */
+static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+       struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
+       struct dwc2_hsotg *hs = hs_ep->parent;
+
+       unsigned long   flags;
+       int             ret;
+
+       spin_lock_irqsave(&hs->lock, flags);
+       hs_ep->wedged = 1;
+       ret = dwc2_hsotg_ep_sethalt(ep, 1, false);
+       spin_unlock_irqrestore(&hs->lock, flags);
+
+       return ret;
+}
+
 /**
  * dwc2_hsotg_ep_sethalt - set halt on a given endpoint
  * @ep: The endpoint to set halt.
@@ -4357,6 +4380,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
                                epctl |= DXEPCTL_EPDIS;
                } else {
                        epctl &= ~DXEPCTL_STALL;
+                       hs_ep->wedged = 0;
                        xfertype = epctl & DXEPCTL_EPTYPE_MASK;
                        if (xfertype == DXEPCTL_EPTYPE_BULK ||
                            xfertype == DXEPCTL_EPTYPE_INTERRUPT)
@@ -4376,6 +4400,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
                        // STALL bit will be set in GOUTNAKEFF interrupt handler
                } else {
                        epctl &= ~DXEPCTL_STALL;
+                       hs_ep->wedged = 0;
                        xfertype = epctl & DXEPCTL_EPTYPE_MASK;
                        if (xfertype == DXEPCTL_EPTYPE_BULK ||
                            xfertype == DXEPCTL_EPTYPE_INTERRUPT)
@@ -4415,6 +4440,7 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
        .queue          = dwc2_hsotg_ep_queue_lock,
        .dequeue        = dwc2_hsotg_ep_dequeue,
        .set_halt       = dwc2_hsotg_ep_sethalt_lock,
+       .set_wedge      = dwc2_gadget_ep_set_wedge,
        /* note, don't believe we have any call for the fifo routines */
 };
 
@@ -4683,12 +4709,35 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
        return usb_phy_set_power(hsotg->uphy, mA);
 }
 
+static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
+{
+       struct dwc2_hsotg *hsotg = to_hsotg(g);
+       unsigned long           flags;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       switch (speed) {
+       case USB_SPEED_HIGH:
+               hsotg->params.speed = DWC2_SPEED_PARAM_HIGH;
+               break;
+       case USB_SPEED_FULL:
+               hsotg->params.speed = DWC2_SPEED_PARAM_FULL;
+               break;
+       case USB_SPEED_LOW:
+               hsotg->params.speed = DWC2_SPEED_PARAM_LOW;
+               break;
+       default:
+               dev_err(hsotg->dev, "invalid speed (%d)\n", speed);
+       }
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
 static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
        .get_frame      = dwc2_hsotg_gadget_getframe,
        .set_selfpowered        = dwc2_hsotg_set_selfpowered,
        .udc_start              = dwc2_hsotg_udc_start,
        .udc_stop               = dwc2_hsotg_udc_stop,
        .pullup                 = dwc2_hsotg_pullup,
+       .udc_set_speed          = dwc2_gadget_set_speed,
        .vbus_session           = dwc2_hsotg_vbus_session,
        .vbus_draw              = dwc2_hsotg_vbus_draw,
 };