usb: dwc3: fix PM resume error for rockchip platforms
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc3 / gadget.c
index e4ff991b43b2eedf371240cf2ed712fc3ef3adcf..9bddcd27005ebb8d633d69b19c6c404d0fcc7613 100644 (file)
@@ -462,10 +462,14 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
 static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
                const struct usb_endpoint_descriptor *desc,
                const struct usb_ss_ep_comp_descriptor *comp_desc,
-               bool ignore, bool restore)
+               bool modify, bool restore)
 {
        struct dwc3_gadget_ep_cmd_params params;
 
+       if (dev_WARN_ONCE(dwc->dev, modify && restore,
+                                       "Can't modify and restore\n"))
+               return -EINVAL;
+
        memset(&params, 0x00, sizeof(params));
 
        params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
@@ -477,16 +481,19 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
                params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
        }
 
-       if (ignore)
-               params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
-
-       if (restore) {
+       if (modify) {
+               params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;
+       } else if (restore) {
                params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
                params.param2 |= dep->saved_state;
+       } else {
+               params.param0 |= DWC3_DEPCFG_ACTION_INIT;
        }
 
-       params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
-               | DWC3_DEPCFG_XFER_NOT_READY_EN;
+       params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
+
+       if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
+               params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
 
        if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
                params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
@@ -542,7 +549,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
 static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
                const struct usb_endpoint_descriptor *desc,
                const struct usb_ss_ep_comp_descriptor *comp_desc,
-               bool ignore, bool restore)
+               bool modify, bool restore)
 {
        struct dwc3             *dwc = dep->dwc;
        u32                     reg;
@@ -556,7 +563,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
                        return ret;
        }
 
-       ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
+       ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, modify,
                        restore);
        if (ret)
                return ret;
@@ -601,15 +608,13 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
 
-       if (!list_empty(&dep->started_list)) {
-               dwc3_stop_active_transfer(dwc, dep->number, true);
+       dwc3_stop_active_transfer(dwc, dep->number, true);
 
-               /* - giveback all requests to gadget driver */
-               while (!list_empty(&dep->started_list)) {
-                       req = next_request(&dep->started_list);
+       /* - giveback all requests to gadget driver */
+       while (!list_empty(&dep->started_list)) {
+               req = next_request(&dep->started_list);
 
-                       dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
-               }
+               dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
        }
 
        while (!list_empty(&dep->pending_list)) {
@@ -741,6 +746,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
        req->epnum      = dep->number;
        req->dep        = dep;
 
+       dep->allocated_requests++;
+
        trace_dwc3_alloc_request(req);
 
        return &req->request;
@@ -750,7 +757,9 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
                struct usb_request *request)
 {
        struct dwc3_request             *req = to_dwc3_request(request);
+       struct dwc3_ep                  *dep = to_dwc3_ep(ep);
 
+       dep->allocated_requests--;
        trace_dwc3_free_request(req);
        kfree(req);
 }
@@ -831,6 +840,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
 
        trb->ctrl |= DWC3_TRB_CTRL_HWO;
 
+       dep->queued_requests++;
+
        trace_dwc3_prepare_trb(dep, trb);
 }
 
@@ -883,7 +894,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
 }
 
 static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
-               struct dwc3_request *req, unsigned int trbs_left)
+               struct dwc3_request *req, unsigned int trbs_left,
+               unsigned int more_coming)
 {
        struct usb_request *request = &req->request;
        struct scatterlist *sg = request->sg;
@@ -900,13 +912,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
                dma = sg_dma_address(s);
 
                if (sg_is_last(s)) {
-                       if (list_is_last(&req->list, &dep->pending_list))
+                       if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
+                               !more_coming)
                                last = true;
 
                        chain = false;
                }
 
-               if (!trbs_left)
+               if (!trbs_left--)
                        last = true;
 
                if (last)
@@ -921,7 +934,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
 }
 
 static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
-               struct dwc3_request *req, unsigned int trbs_left)
+               struct dwc3_request *req, unsigned int trbs_left,
+               unsigned int more_coming)
 {
        unsigned int    last = false;
        unsigned int    length;
@@ -934,7 +948,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                last = true;
 
        /* Is this the last request? */
-       if (list_is_last(&req->list, &dep->pending_list))
+       if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming)
                last = true;
 
        dwc3_prepare_one_trb(dep, req, dma, length,
@@ -952,6 +966,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
 static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 {
        struct dwc3_request     *req, *n;
+       unsigned int            more_coming;
        u32                     trbs_left;
 
        BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
@@ -960,11 +975,15 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
        if (!trbs_left)
                return;
 
+       more_coming = dep->allocated_requests - dep->queued_requests;
+
        list_for_each_entry_safe(req, n, &dep->pending_list, list) {
                if (req->request.num_mapped_sgs > 0)
-                       dwc3_prepare_one_trb_sg(dep, req, trbs_left--);
+                       dwc3_prepare_one_trb_sg(dep, req, trbs_left--,
+                                       more_coming);
                else
-                       dwc3_prepare_one_trb_linear(dep, req, trbs_left--);
+                       dwc3_prepare_one_trb_linear(dep, req, trbs_left--,
+                                       more_coming);
 
                if (!trbs_left)
                        return;
@@ -994,12 +1013,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
        if (starting) {
                params.param0 = upper_32_bits(req->trb_dma);
                params.param1 = lower_32_bits(req->trb_dma);
-               cmd = DWC3_DEPCMD_STARTTRANSFER;
+               cmd = DWC3_DEPCMD_STARTTRANSFER |
+                       DWC3_DEPCMD_PARAM(cmd_param);
        } else {
-               cmd = DWC3_DEPCMD_UPDATETRANSFER;
+               cmd = DWC3_DEPCMD_UPDATETRANSFER |
+                       DWC3_DEPCMD_PARAM(dep->resource_index);
        }
 
-       cmd |= DWC3_DEPCMD_PARAM(cmd_param);
        ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
        if (ret < 0) {
                /*
@@ -1119,8 +1139,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
         * little bit faster.
         */
        if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
-                       !usb_endpoint_xfer_int(dep->endpoint.desc) &&
-                       !(dep->flags & DWC3_EP_BUSY)) {
+                       !usb_endpoint_xfer_int(dep->endpoint.desc)) {
                ret = __dwc3_gadget_kick_transfer(dep, 0);
                goto out;
        }
@@ -1306,9 +1325,21 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
        memset(&params, 0x00, sizeof(params));
 
        if (value) {
-               if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
-                               (!list_empty(&dep->started_list) ||
-                                !list_empty(&dep->pending_list)))) {
+               struct dwc3_trb *trb;
+
+               unsigned transfer_in_flight;
+               unsigned started;
+
+               if (dep->number > 1)
+                       trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+               else
+                       trb = &dwc->ep0_trb[dep->trb_enqueue];
+
+               transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO;
+               started = !list_empty(&dep->started_list);
+
+               if (!protocol && ((dep->direction && transfer_in_flight) ||
+                               (!dep->direction && started))) {
                        dwc3_trace(trace_dwc3_gadget,
                                        "%s: pending request, cannot halt",
                                        dep->name);
@@ -1543,18 +1574,11 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
        do {
                reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-               if (is_on) {
-                       if (!(reg & DWC3_DSTS_DEVCTRLHLT))
-                               break;
-               } else {
-                       if (reg & DWC3_DSTS_DEVCTRLHLT)
-                               break;
-               }
-               timeout--;
-               if (!timeout)
-                       return -ETIMEDOUT;
-               udelay(1);
-       } while (1);
+               reg &= DWC3_DSTS_DEVCTRLHLT;
+       } while (--timeout && !(!is_on ^ !reg));
+
+       if (!timeout)
+               return -ETIMEDOUT;
 
        dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
                        dwc->gadget_driver
@@ -1745,7 +1769,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
        int                     ret = 0;
        int                     irq;
 
-       irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+       irq = dwc->irq_gadget;
        ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
                        IRQF_SHARED, "dwc3", dwc->ev_buf);
        if (ret) {
@@ -1753,7 +1777,6 @@ static int dwc3_gadget_start(struct usb_gadget *g,
                                irq, ret);
                goto err0;
        }
-       dwc->irq_gadget = irq;
 
        spin_lock_irqsave(&dwc->lock, flags);
        if (dwc->gadget_driver) {
@@ -1783,6 +1806,9 @@ err0:
 
 static void __dwc3_gadget_stop(struct dwc3 *dwc)
 {
+       if (pm_runtime_suspended(dwc->dev))
+               return;
+
        dwc3_gadget_disable_irq(dwc);
        __dwc3_gadget_ep_disable(dwc->eps[0]);
        __dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -1939,6 +1965,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
        unsigned int            s_pkt = 0;
        unsigned int            trb_status;
 
+       dep->queued_requests--;
        trace_dwc3_complete_trb(dep, trb);
 
        if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
@@ -2665,6 +2692,17 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
        dwc->link_state = next;
 }
 
+static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
+                                         unsigned int evtinfo)
+{
+       enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+
+       if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
+               dwc3_suspend_gadget(dwc);
+
+       dwc->link_state = next;
+}
+
 static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
                unsigned int evtinfo)
 {
@@ -2716,7 +2754,20 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
                dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
                break;
        case DWC3_DEVICE_EVENT_EOPF:
-               dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+               /* It changed to be suspend event for version 2.30a and above */
+               if (dwc->revision < DWC3_REVISION_230A) {
+                       dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+               } else {
+                       dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
+
+                       /*
+                        * Ignore suspend event until the gadget enters into
+                        * USB_STATE_CONFIGURED state.
+                        */
+                       if (dwc->gadget.state >= USB_STATE_CONFIGURED)
+                               dwc3_gadget_suspend_interrupt(dwc,
+                                               event->event_info);
+               }
                break;
        case DWC3_DEVICE_EVENT_SOF:
                dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
@@ -2822,7 +2873,10 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
        u32 count;
        u32 reg;
 
-       if (pm_runtime_suspended(dwc->dev)) {
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+
+       if (pm_runtime_suspended(dwc->dev) &&
+           DWC3_GCTL_PRTCAP(reg) != DWC3_GCTL_PRTCAP_HOST) {
                pm_runtime_get(dwc->dev);
                disable_irq_nosync(dwc->irq_gadget);
                dwc->pending_events = true;
@@ -2860,7 +2914,33 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
  */
 int dwc3_gadget_init(struct dwc3 *dwc)
 {
-       int                                     ret;
+       int ret, irq;
+       struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+
+       irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
+       if (irq == -EPROBE_DEFER)
+               return irq;
+
+       if (irq <= 0) {
+               irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+               if (irq == -EPROBE_DEFER)
+                       return irq;
+
+               if (irq <= 0) {
+                       irq = platform_get_irq(dwc3_pdev, 0);
+                       if (irq <= 0) {
+                               if (irq != -EPROBE_DEFER) {
+                                       dev_err(dwc->dev,
+                                               "missing peripheral IRQ\n");
+                               }
+                               if (!irq)
+                                       irq = -EINVAL;
+                               return irq;
+                       }
+               }
+       }
+
+       dwc->irq_gadget = irq;
 
        dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
                        &dwc->ctrl_req_addr, GFP_KERNEL);