From patchwork Wed Oct 12 11:37:31 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: 77551 Delivered-To: patch@linaro.org Received: by 10.140.97.247 with SMTP id m110csp405412qge; Wed, 12 Oct 2016 04:38:17 -0700 (PDT) X-Received: by 10.66.162.138 with SMTP id ya10mr861022pab.154.1476272297630; Wed, 12 Oct 2016 04:38:17 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s17si5464009pgh.144.2016.10.12.04.38.17; Wed, 12 Oct 2016 04:38:17 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-usb-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-usb-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-usb-owner@vger.kernel.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753374AbcJLLiP (ORCPT + 4 others); Wed, 12 Oct 2016 07:38:15 -0400 Received: from mail-pf0-f175.google.com ([209.85.192.175]:32797 "EHLO mail-pf0-f175.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752706AbcJLLiN (ORCPT ); Wed, 12 Oct 2016 07:38:13 -0400 Received: by mail-pf0-f175.google.com with SMTP id 128so17806660pfz.0 for ; Wed, 12 Oct 2016 04:38:13 -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=QNUmqspS0k+PEpRv1GpbAVpf7km/Xhxi13jwYOWoGjY=; b=IHTgtWLddpNVJrFUTzByVHusMYB1cyJJx0KcwANsSq19Sk7P8dWVZS6m123Yfj7vwX 3ULll45ajtM6QgVvpap9+HUCksfmkg0ZLo6FI+8ffivQFhqIls4x9CNoSsyiHNpUUaiq zhaxQS2BnIxSxsJfkEfHIw6LbtG+2dxl5pEos= 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=QNUmqspS0k+PEpRv1GpbAVpf7km/Xhxi13jwYOWoGjY=; b=Hf4lIsrJahDLFyWflQovnie11zxfgrbyoGFib3nSI4D6eGIhPjcAdGFs0I50jt5Lyj YCvdbjyz7oyByVB+dBZBJUkpy3uDBn21xj4Nsj5mhcsMT7pdzS2+eirFzlNcnWBkBDS+ id1Nr1lH6+5lWJP0WEJCWJiHncH1gIPvivjXGcDs5tEajoq67MNtggC4z56w891fZQdY F/V2JvfV76mldxPOJBGplllrEnzWA0Da7mxG9PKGPhCfsbpAPZzXLIGK69Q5ykMCk795 iLrZ5WyultoVxRaXyDIdT+wFovIAPjiYyaTFj2jm34FMgzoQ2NEZ//kX3bhdXmWHtQrE bPRg== X-Gm-Message-State: AA6/9RncmQRDTwjONQWkv+JLnTaIhW5k/qvVbOBno346+J5UDwU1pZP/elTpqD67L43UYXz4 X-Received: by 10.98.139.217 with SMTP id e86mr1052422pfl.84.1476272292991; Wed, 12 Oct 2016 04:38:12 -0700 (PDT) Received: from baolinwangubtpc.spreadtrum.com ([175.111.195.49]) by smtp.gmail.com with ESMTPSA id l187sm11222740pfc.0.2016.10.12.04.38.10 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Oct 2016 04:38: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 v2] usb: dwc3: gadget: Wait for end transfer complete before free irq Date: Wed, 12 Oct 2016 19:37:31 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@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 --- Changes since v1: - Simply the operation of cleaning the dep flags. --- drivers/usb/dwc3/core.h | 3 ++ drivers/usb/dwc3/gadget.c | 73 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 3 deletions(-) -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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..32e3d4d 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 &= 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;