These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / usb / chipidea / udc.c
index 764f668..391a122 100644 (file)
@@ -445,7 +445,7 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
                rest -= count;
        }
 
-       if (hwreq->req.zero && hwreq->req.length
+       if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
            && (hwreq->req.length % hwep->ep.maxpacket == 0))
                add_td_to_list(hwep, hwreq, 0);
 
@@ -656,6 +656,44 @@ __acquires(hwep->lock)
        return 0;
 }
 
+static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer)
+{
+       struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
+       int direction, retval = 0;
+       unsigned long flags;
+
+       if (ep == NULL || hwep->ep.desc == NULL)
+               return -EINVAL;
+
+       if (usb_endpoint_xfer_isoc(hwep->ep.desc))
+               return -EOPNOTSUPP;
+
+       spin_lock_irqsave(hwep->lock, flags);
+
+       if (value && hwep->dir == TX && check_transfer &&
+               !list_empty(&hwep->qh.queue) &&
+                       !usb_endpoint_xfer_control(hwep->ep.desc)) {
+               spin_unlock_irqrestore(hwep->lock, flags);
+               return -EAGAIN;
+       }
+
+       direction = hwep->dir;
+       do {
+               retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value);
+
+               if (!value)
+                       hwep->wedge = 0;
+
+               if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
+                       hwep->dir = (hwep->dir == TX) ? RX : TX;
+
+       } while (hwep->dir != direction);
+
+       spin_unlock_irqrestore(hwep->lock, flags);
+       return retval;
+}
+
+
 /**
  * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts
  * @gadget: gadget
@@ -1051,7 +1089,7 @@ __acquires(ci->lock)
                                num += ci->hw_ep_max / 2;
 
                        spin_unlock(&ci->lock);
-                       err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
+                       err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false);
                        spin_lock(&ci->lock);
                        if (!err)
                                isr_setup_status_phase(ci);
@@ -1090,6 +1128,13 @@ __acquires(ci->lock)
                                if (ci_otg_is_fsm_mode(ci))
                                        err = otg_a_alt_hnp_support(ci);
                                break;
+                       case USB_DEVICE_A_HNP_SUPPORT:
+                               if (ci_otg_is_fsm_mode(ci)) {
+                                       ci->gadget.a_hnp_support = 1;
+                                       err = isr_setup_status_phase(
+                                                       ci);
+                               }
+                               break;
                        default:
                                goto delegate;
                        }
@@ -1110,8 +1155,8 @@ delegate:
 
        if (err < 0) {
                spin_unlock(&ci->lock);
-               if (usb_ep_set_halt(&hwep->ep))
-                       dev_err(ci->dev, "error: ep_set_halt\n");
+               if (_ep_set_halt(&hwep->ep, 1, false))
+                       dev_err(ci->dev, "error: _ep_set_halt\n");
                spin_lock(&ci->lock);
        }
 }
@@ -1142,9 +1187,9 @@ __acquires(ci->lock)
                                        err = isr_setup_status_phase(ci);
                                if (err < 0) {
                                        spin_unlock(&ci->lock);
-                                       if (usb_ep_set_halt(&hwep->ep))
+                                       if (_ep_set_halt(&hwep->ep, 1, false))
                                                dev_err(ci->dev,
-                                                       "error: ep_set_halt\n");
+                                               "error: _ep_set_halt\n");
                                        spin_lock(&ci->lock);
                                }
                        }
@@ -1390,41 +1435,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
  */
 static int ep_set_halt(struct usb_ep *ep, int value)
 {
-       struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
-       int direction, retval = 0;
-       unsigned long flags;
-
-       if (ep == NULL || hwep->ep.desc == NULL)
-               return -EINVAL;
-
-       if (usb_endpoint_xfer_isoc(hwep->ep.desc))
-               return -EOPNOTSUPP;
-
-       spin_lock_irqsave(hwep->lock, flags);
-
-#ifndef STALL_IN
-       /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */
-       if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX &&
-           !list_empty(&hwep->qh.queue)) {
-               spin_unlock_irqrestore(hwep->lock, flags);
-               return -EAGAIN;
-       }
-#endif
-
-       direction = hwep->dir;
-       do {
-               retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value);
-
-               if (!value)
-                       hwep->wedge = 0;
-
-               if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
-                       hwep->dir = (hwep->dir == TX) ? RX : TX;
-
-       } while (hwep->dir != direction);
-
-       spin_unlock_irqrestore(hwep->lock, flags);
-       return retval;
+       return _ep_set_halt(ep, value, true);
 }
 
 /**
@@ -1624,6 +1635,20 @@ static int init_eps(struct ci_hdrc *ci)
 
                        hwep->ep.name      = hwep->name;
                        hwep->ep.ops       = &usb_ep_ops;
+
+                       if (i == 0) {
+                               hwep->ep.caps.type_control = true;
+                       } else {
+                               hwep->ep.caps.type_iso = true;
+                               hwep->ep.caps.type_bulk = true;
+                               hwep->ep.caps.type_int = true;
+                       }
+
+                       if (j == TX)
+                               hwep->ep.caps.dir_in = true;
+                       else
+                               hwep->ep.caps.dir_out = true;
+
                        /*
                         * for ep0: maxP defined in desc, for other
                         * eps, maxP is set by epautoconfig() called
@@ -1726,6 +1751,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
        return retval;
 }
 
+static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
+{
+       if (!ci_otg_is_fsm_mode(ci))
+               return;
+
+       mutex_lock(&ci->fsm.lock);
+       if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
+               ci->fsm.a_bidl_adis_tmout = 1;
+               ci_hdrc_otg_fsm_start(ci);
+       } else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
+               ci->fsm.protocol = PROTO_UNDEF;
+               ci->fsm.otg->state = OTG_STATE_UNDEFINED;
+       }
+       mutex_unlock(&ci->fsm.lock);
+}
+
 /**
  * ci_udc_stop: unregister a gadget driver
  */
@@ -1750,6 +1791,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
        ci->driver = NULL;
        spin_unlock_irqrestore(&ci->lock, flags);
 
+       ci_udc_stop_for_otg_fsm(ci);
        return 0;
 }
 
@@ -1827,6 +1869,7 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
 static int udc_start(struct ci_hdrc *ci)
 {
        struct device *dev = ci->dev;
+       struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
        int retval = 0;
 
        spin_lock_init(&ci->lock);
@@ -1834,8 +1877,12 @@ static int udc_start(struct ci_hdrc *ci)
        ci->gadget.ops          = &usb_gadget_ops;
        ci->gadget.speed        = USB_SPEED_UNKNOWN;
        ci->gadget.max_speed    = USB_SPEED_HIGH;
-       ci->gadget.is_otg       = ci->is_otg ? 1 : 0;
        ci->gadget.name         = ci->platdata->name;
+       ci->gadget.otg_caps     = otg_caps;
+
+       if (ci->is_otg && (otg_caps->hnp_support || otg_caps->srp_support ||
+                                               otg_caps->adp_support))
+               ci->gadget.is_otg = 1;
 
        INIT_LIST_HEAD(&ci->gadget.ep_list);