From patchwork Mon Oct 10 06:30:38 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "\(Exiting\) Baolin Wang" X-Patchwork-Id: 77427 Delivered-To: patch@linaro.org Received: by 10.140.97.247 with SMTP id m110csp1080807qge; Sun, 9 Oct 2016 23:32:13 -0700 (PDT) X-Received: by 10.66.26.49 with SMTP id i17mr51829238pag.145.1476081133275; Sun, 09 Oct 2016 23:32:13 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id rf4si19991801pab.9.2016.10.09.23.32.12; Sun, 09 Oct 2016 23:32:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752041AbcJJGbu (ORCPT + 27 others); Mon, 10 Oct 2016 02:31:50 -0400 Received: from mail-pa0-f53.google.com ([209.85.220.53]:35722 "EHLO mail-pa0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751586AbcJJGbr (ORCPT ); Mon, 10 Oct 2016 02:31:47 -0400 Received: by mail-pa0-f53.google.com with SMTP id qn10so45137710pac.2 for ; Sun, 09 Oct 2016 23:31:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=pTlyMMnnAkQ1W1I/b4NMmAaWZbg7dREzmzkDQTap3sg=; b=hIS7OeNb8vhMvjGMsbHYhseaatiVu+vIJ3LvdZTP2WhOYWeRZXZY/ywJBmhWIqq+qi aYtttP1JLYXHQiTr+sHRVGu610m/dP7szuxtaeNj9V1vEA+nfNyuI4jUuK9ask4uTHMC XOVNMt1hb2C2VJHmUL6Qyhcq7zNjKO9nKvNbA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=pTlyMMnnAkQ1W1I/b4NMmAaWZbg7dREzmzkDQTap3sg=; b=OSsvX5aQeG2qzaXSULC8MIbT/mJ4A83feApdQ6qB0fdeEEO/JDadKO8igC2L2qXCbW KWsj0TXUeah76AL55g/KXkUJqW94oqwe6KWrnvITlqbpSSID3/ccLXLTXMkZ+F4ba898 cfQUaI/Qe6+VHOGv1QQx+h6OTfpgKuEkmq2wHcgwcLf4lRAl8ix6dzzh4kSba1zZ7k4I /UyReBCSPKvvbsoxIfNR5BGgc4lS3TCc6eEFCH2pohhWr5O87OJ1DgPnCyKu+RJFPZv6 DAggTWOHo5QD+XfpTqUqFIJEknpZpRRdnr9AfqMcsWX/cWtuRzxbjBVaV6y3ckuHKGOw Qcdw== X-Gm-Message-State: AA6/9RlWTk2piom8etCOuKljc8zF7bnCHh5onX72Sz+3g3lfv2vG4Dfg72+paO7GDVrTguvF X-Received: by 10.66.75.39 with SMTP id z7mr51713626pav.83.1476081072498; Sun, 09 Oct 2016 23:31:12 -0700 (PDT) Received: from baolinwangubtpc.spreadtrum.com ([175.111.195.49]) by smtp.gmail.com with ESMTPSA id ah5sm51982435pad.30.2016.10.09.23.31.10 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 09 Oct 2016 23:31:12 -0700 (PDT) From: Baolin Wang To: balbi@kernel.org Cc: gregkh@linuxfoundation.org, broonie@kernel.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, baolin.wang@linaro.org Subject: [PATCH] usb: dwc3: gadget: Wait for end transfer complete before free irq Date: Mon, 10 Oct 2016 14:30:38 +0800 Message-Id: <1f6c3e012c303231cf4b05f32f5bbf7df25eb9bc.1476066840.git.baolin.wang@linaro.org> X-Mailer: git-send-email 1.7.9.5 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When we change the USB function with configfs frequently, sometimes it will hang the system to crash. The reason is the gadget driver can not hanle the end transfer complete event after free the gadget irq (since the xHCI will share the same interrupt number with gadget, thus when free the gadget irq, it will not shutdown this gadget irq line), which will trigger the interrupt all the time. Thus we should check if we need wait for the end transfer command completion before free gadget irq. Signed-off-by: Baolin Wang --- drivers/usb/dwc3/core.h | 3 ++ drivers/usb/dwc3/gadget.c | 73 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 3 deletions(-) -- 1.7.9.5 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 9128725..f01d8fd 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -537,6 +537,7 @@ struct dwc3_ep { #define DWC3_EP_BUSY (1 << 4) #define DWC3_EP_PENDING_REQUEST (1 << 5) #define DWC3_EP_MISSED_ISOC (1 << 6) +#define DWC3_EP_CMDCMPLT_BUSY (1 << 7) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN (1 << 31) @@ -746,6 +747,7 @@ struct dwc3_scratchpad_array { * @ep0_bounce_addr: dma address of ep0_bounce * @scratch_addr: dma address of scratchbuf * @ep0_in_setup: One control transfer is completed and enter setup phase + * @cmd_complete: endpoint command completion * @lock: for synchronizing * @dev: pointer to our struct device * @xhci: pointer to our xHCI child @@ -845,6 +847,7 @@ struct dwc3 { dma_addr_t scratch_addr; struct dwc3_request ep0_usb_req; struct completion ep0_in_setup; + struct completion cmd_complete; /* device lock */ spinlock_t lock; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fef023a..36db8ba 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -573,6 +573,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, dep->comp_desc = comp_desc; dep->type = usb_endpoint_type(desc); dep->flags |= DWC3_EP_ENABLED; + dep->flags &= ~DWC3_EP_CMDCMPLT_BUSY; reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); reg |= DWC3_DALEPENA_EP(dep->number); @@ -650,7 +651,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) dep->endpoint.desc = NULL; dep->comp_desc = NULL; dep->type = 0; - dep->flags = 0; + dep->flags = 0 | (dep->flags & DWC3_EP_CMDCMPLT_BUSY); return 0; } @@ -1732,6 +1733,54 @@ static void __dwc3_gadget_stop(struct dwc3 *dwc) __dwc3_gadget_ep_disable(dwc->eps[1]); } +static void dwc3_wait_command_complete(struct dwc3 *dwc) +{ + u32 epnum, epstart = 2; + int ret, wait_cmd_complete = 0; + unsigned long flags; + +check_next: + spin_lock_irqsave(&dwc->lock, flags); + /* + * If the gadget has been in suspend state, then don't + * need to wait for the end transfer complete event. + */ + if (pm_runtime_suspended(dwc->dev)) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + for (epnum = epstart; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + if (dep->flags & DWC3_EP_CMDCMPLT_BUSY) { + reinit_completion(&dwc->cmd_complete); + epstart = epnum + 1; + wait_cmd_complete = 1; + break; + } + } + spin_unlock_irqrestore(&dwc->lock, flags); + + /* + * Wait for 500ms to complete the end transfer command before free irq. + */ + if (wait_cmd_complete) { + wait_cmd_complete = 0; + ret = wait_for_completion_timeout(&dwc->cmd_complete, + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(dwc->dev, + "timeout to wait for command complete.\n"); + + goto check_next; + } +} + static int dwc3_gadget_stop(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); @@ -1742,6 +1791,17 @@ static int dwc3_gadget_stop(struct usb_gadget *g) dwc->gadget_driver = NULL; spin_unlock_irqrestore(&dwc->lock, flags); + /* + * Since the xHCI will share the same interrupt with gadget, thus when + * free the gadget irq, it will not shutdown this gadget irq line. Then + * the gadget driver can not handle the end transfer command complete + * event after free the gadget irq, which will hang the system to crash. + * + * So we should wait for the end transfer command complete event before + * free it to avoid this situation. + */ + dwc3_wait_command_complete(dwc); + free_irq(dwc->irq_gadget, dwc->ev_buf); return 0; @@ -2108,7 +2168,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dep = dwc->eps[epnum]; - if (!(dep->flags & DWC3_EP_ENABLED)) + if (!(dep->flags & DWC3_EP_ENABLED) && + !(dep->flags & DWC3_EP_CMDCMPLT_BUSY)) return; if (epnum == 0 || epnum == 1) { @@ -2180,6 +2241,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name); break; case DWC3_DEPEVT_EPCMDCMPLT: + if (dep->flags & DWC3_EP_CMDCMPLT_BUSY) { + dep->flags &= ~DWC3_EP_CMDCMPLT_BUSY; + complete(&dwc->cmd_complete); + } + dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete"); break; } @@ -2278,7 +2344,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) dep->flags &= ~DWC3_EP_BUSY; if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) - udelay(100); + dep->flags |= DWC3_EP_CMDCMPLT_BUSY; } static void dwc3_stop_active_transfers(struct dwc3 *dwc) @@ -2936,6 +3002,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) } init_completion(&dwc->ep0_in_setup); + init_completion(&dwc->cmd_complete); dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.speed = USB_SPEED_UNKNOWN;