From patchwork Fri May 13 10:24:29 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: 67759 Delivered-To: patch@linaro.org Received: by 10.140.92.199 with SMTP id b65csp187903qge; Fri, 13 May 2016 03:24:55 -0700 (PDT) X-Received: by 10.98.29.137 with SMTP id d131mr21910641pfd.2.1463135095283; Fri, 13 May 2016 03:24:55 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id hn5si23924062pac.203.2016.05.13.03.24.55; Fri, 13 May 2016 03:24:55 -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 S1752256AbcEMKYx (ORCPT + 4 others); Fri, 13 May 2016 06:24:53 -0400 Received: from mail-pf0-f170.google.com ([209.85.192.170]:35656 "EHLO mail-pf0-f170.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751479AbcEMKYw (ORCPT ); Fri, 13 May 2016 06:24:52 -0400 Received: by mail-pf0-f170.google.com with SMTP id 77so41048476pfv.2 for ; Fri, 13 May 2016 03:24:52 -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=qRO2nZj4iT01Q2n+S2yC+HrNS1oLOFrOB+OYbqsBtdY=; b=KpLa+4IXBtCr44WQ5epKSWGVe4kNflPZ46pmdUGkwJXb713d08e/zOyM6f8DCitbPo KMF2ZHh4vXV1c9dgJpTNCLJHanqI7vxF5tD0sMtUo2oHJz19bSa2daYAXRUst6xkznF8 fUdJXumY3fphqMUMycBErtlWVQY1reUzs8unM= 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=qRO2nZj4iT01Q2n+S2yC+HrNS1oLOFrOB+OYbqsBtdY=; b=MlFehwS9ukwbIhhud0LGIdU8+SYshIx/T//sUxYj6aH3bjWt8bY7q7UlpDg2svCK6E 6RXI+uK72Eerwsl0nqCUD6zxvNfqR41nDm77EAXBP0HVLlFhhhrpLonhh7CcK0WGNfPi T9q3TpQ4dLk1MA+AC9brq3+xMpIXYlN3irlLvTtMjqbz/y5kNRdNjYBsi5zfaL+/kfdX yZgJhoyZpUKeZkS4iTU1W8JRzvb7G/r6G9VQHTHfVLPDB3EfgZyL9PA8+GoNXEa0Ya0q cwPh1TwbjU7Y2vOVyDz7EjJkyD4T8wyj0+mKJAN2drFxHTFnXQJWJyHc2SgZXPzG7KVz eD6Q== X-Gm-Message-State: AOPr4FUx/3e+Y3R9VQuLh7BhcV1ePbjWHyP8QROFId6Q0h0Qc5PR5mFdp0AfR2EfDD/H472z X-Received: by 10.98.9.154 with SMTP id 26mr21653829pfj.121.1463135091868; Fri, 13 May 2016 03:24:51 -0700 (PDT) Received: from baolinwangubtpc.spreadtrum.com ([175.111.195.49]) by smtp.gmail.com with ESMTPSA id 1sm26329184pah.7.2016.05.13.03.24.49 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 13 May 2016 03:24:51 -0700 (PDT) From: Baolin Wang To: balbi@kernel.org, gregkh@linuxfoundation.org Cc: broonie@kernel.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, baolin.wang@linaro.org Subject: [PATCH] dwc3: gadget: Defer starting the gadget device until gadget is power on Date: Fri, 13 May 2016 18:24:29 +0800 Message-Id: <4d6528e4b742cacf34f384b766a7c3296dfe9dbf.1463134786.git.baolin.wang@linaro.org> 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 Currently on some platforms, the gadget device can be power off to save power when the Vbus is off, which means no cable plugging in now. In this situation we should defer starting the gadget until the gadget device is power on by connecting host. Signed-off-by: Baolin Wang --- drivers/usb/dwc3/core.c | 5 +- drivers/usb/dwc3/core.h | 14 ++++ drivers/usb/dwc3/gadget.c | 144 ++++++++++++++++++++++++++++++-------- drivers/usb/dwc3/platform_data.h | 1 + 4 files changed, 131 insertions(+), 33 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.c b/drivers/usb/dwc3/core.c index 34277ce..825462a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -109,7 +109,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) * dwc3_soft_reset - Issue soft reset * @dwc: Pointer to our controller context structure */ -static int dwc3_soft_reset(struct dwc3 *dwc) +int dwc3_soft_reset(struct dwc3 *dwc) { unsigned long timeout; u32 reg; @@ -253,7 +253,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; int n; @@ -948,6 +948,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hsphy_interface = pdata->hsphy_interface; fladj = pdata->fladj_value; + dwc->can_save_power = pdata->can_save_power; } dwc->lpm_nyet_threshold = lpm_nyet_threshold; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 6254b2f..dada5c6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -734,6 +734,9 @@ struct dwc3_scratchpad_array { * 1 - -3.5dB de-emphasis * 2 - No de-emphasis * 3 - Reserved + * @can_save_power: set if the gadget will power off when no cable plug in. + * @need_restart: set if we need to restart the gadget. + * @cable_connected: set if one usb cable is plugging in. */ struct dwc3 { struct usb_ctrlrequest *ctrl_req; @@ -876,6 +879,9 @@ struct dwc3 { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + unsigned can_save_power:1; + unsigned need_restart:1; + unsigned cable_connected:1; }; /* -------------------------------------------------------------------------- */ @@ -1026,6 +1032,8 @@ struct dwc3_gadget_ep_cmd_params { /* prototypes */ void dwc3_set_mode(struct dwc3 *dwc, u32 mode); int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); +int dwc3_soft_reset(struct dwc3 *dwc); +int dwc3_event_buffers_setup(struct dwc3 *dwc); /* check whether we are on the DWC_usb31 core */ static inline bool dwc3_is_usb31(struct dwc3 *dwc) @@ -1052,6 +1060,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +void dwc3_gadget_connect(struct dwc3 *dwc); +void dwc3_gadget_disconnect(struct dwc3 *dwc); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } @@ -1071,6 +1081,10 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) { return 0; } +static inline void dwc3_gadget_connect(struct dwc3 *dwc) +{ } +static inline void dwc3_gadget_disconnect(struct dwc3 *dwc) +{ } #endif /* power management interface */ diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8e4a1b1..90805f9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1512,13 +1512,75 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, return 0; } +void dwc3_gadget_connect(struct dwc3 *dwc) +{ + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->cable_connected = true; + spin_unlock_irqrestore(&dwc->lock, flags); +} + +void dwc3_gadget_disconnect(struct dwc3 *dwc) +{ + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->cable_connected = false; + spin_unlock_irqrestore(&dwc->lock, flags); +} + +static bool dwc3_gadget_is_connected(struct dwc3 *dwc) +{ + /* + * If the gadget is always power on, then no need to check if the + * cable is plugin or not. + */ + if (!dwc->can_save_power) + return true; + + return dwc->cable_connected; +} + +static int __dwc3_gadget_start(struct dwc3 *dwc); + static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg; u32 timeout = 500; + int ret; + + if (!dwc3_gadget_is_connected(dwc) || !dwc->gadget_driver) + return 0; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { + if (dwc->need_restart) { + /* + * We need to reset the device firstly when the device + * is power on. + */ + ret = dwc3_soft_reset(dwc); + if (ret) + return ret; + + /* + * After resetting the device, it need to re-setup the + * event buffer. + */ + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, + "failed to setup event buffers\n"); + return ret; + } + + /* Start the gadget */ + ret = __dwc3_gadget_start(dwc); + if (ret) + return ret; + } + if (dwc->revision <= DWC3_REVISION_187A) { reg &= ~DWC3_DCTL_TRGTULST_MASK; reg |= DWC3_DCTL_TRGTULST_RX_DET; @@ -1608,37 +1670,12 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) static irqreturn_t dwc3_interrupt(int irq, void *_dwc); static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); -static int dwc3_gadget_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static int __dwc3_gadget_start(struct dwc3 *dwc) { - struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3_ep *dep; - unsigned long flags; int ret = 0; - int irq; u32 reg; - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, - IRQF_SHARED, "dwc3", dwc); - if (ret) { - dev_err(dwc->dev, "failed to request irq #%d --> %d\n", - irq, ret); - goto err0; - } - - spin_lock_irqsave(&dwc->lock, flags); - - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->driver.name); - ret = -EBUSY; - goto err1; - } - - dwc->gadget_driver = driver; - reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); @@ -1690,7 +1727,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err2; + return ret; } dep = dwc->eps[1]; @@ -1698,7 +1735,8 @@ static int dwc3_gadget_start(struct usb_gadget *g, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err3; + __dwc3_gadget_ep_disable(dwc->eps[0]); + return ret; } /* begin to receive SETUP packets */ @@ -1707,13 +1745,57 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_gadget_enable_irq(dwc); + return 0; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret = 0; + int irq; + + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED, "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err0; + } + + spin_lock_irqsave(&dwc->lock, flags); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err1; + } + + dwc->gadget_driver = driver; + + /* + * If the gadget can be power off when there is no cable plug in, we + * need to check if the device power is on or not. If not, we should + * not access the device registers. + */ + if (!dwc3_gadget_is_connected(dwc)) { + dwc->need_restart = 1; + spin_unlock_irqrestore(&dwc->lock, flags); + return 0; + } + + ret = __dwc3_gadget_start(dwc); + if (ret) + goto err2; + spin_unlock_irqrestore(&dwc->lock, flags); return 0; -err3: - __dwc3_gadget_ep_disable(dwc->eps[0]); - err2: dwc->gadget_driver = NULL; diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index 2bb4d3a..7e15266 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -46,6 +46,7 @@ struct dwc3_platform_data { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + unsigned can_save_power:1; u32 fladj_value;