From patchwork Tue Jan 20 21:23:30 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylvain Rochet X-Patchwork-Id: 5672381 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 263CC9F4DC for ; Tue, 20 Jan 2015 21:26:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 1D374204E7 for ; Tue, 20 Jan 2015 21:26:36 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C9E24204E4 for ; Tue, 20 Jan 2015 21:26:34 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YDgHv-0008Oc-JU; Tue, 20 Jan 2015 21:24:39 +0000 Received: from mx-guillaumet.finsecur.com ([91.217.234.131] helo=guillaumet.finsecur.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YDgHG-000813-51 for linux-arm-kernel@lists.infradead.org; Tue, 20 Jan 2015 21:24:00 +0000 Received: from [172.16.8.13] (helo=spice.lan) by guillaumet.finsecur.com with esmtps (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1YDgGq-0006pS-3g; Tue, 20 Jan 2015 22:23:33 +0100 Received: from gradator by spice.lan with local (Exim 4.84) (envelope-from ) id 1YDgGp-0005sJ-TM; Tue, 20 Jan 2015 22:23:31 +0100 From: Sylvain Rochet To: Boris Brezillon , Alexandre Belloni , plagnioj@jcrosoft.com, nicolas.ferre@atmel.com, linux-arm-kernel@lists.infradead.org, voice.shen@atmel.com, Felipe Balbi , Greg Kroah-Hartman , linux-usb@vger.kernel.org, Wenyou Yang Date: Tue, 20 Jan 2015 22:23:30 +0100 Message-Id: <1421789010-21900-4-git-send-email-sylvain.rochet@finsecur.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1421789010-21900-1-git-send-email-sylvain.rochet@finsecur.com> References: <1421789010-21900-1-git-send-email-sylvain.rochet@finsecur.com> X-SA-Exim-Connect-IP: 172.16.8.13 X-SA-Exim-Mail-From: sylvain.rochet@finsecur.com X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Subject: [PATCHv4 3/3] USB: gadget: atmel_usba_udc: Start clocks on rising edge of the Vbus signal, stop clocks on falling edge of the Vbus signal X-SA-Exim-Version: 4.2.1 (built Mon, 26 Dec 2011 16:24:06 +0000) X-SA-Exim-Scanned: Yes (on guillaumet.finsecur.com) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150120_132358_413004_177A46BB X-CRM114-Status: GOOD ( 18.15 ) X-Spam-Score: -0.7 (/) Cc: Sylvain Rochet X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP If USB PLL is not necessary for other USB drivers (e.g. OHCI and EHCI) it will reduce power consumption by switching off the USB PLL if no USB Host is currently connected to this USB Device. We are using Vbus GPIO signal to detect Host presence. If Vbus signal is not available then the device stays continuously clocked. Note this driver does not support suspend/resume yet, it may stay clocked if USB Host is still connected when suspending. For what I need, forbidding suspend from userland if we are still attached to an USB host is fine, but we might as well add suspend/resume to this driver in the future. Signed-off-by: Sylvain Rochet Acked-by: Nicolas Ferre --- drivers/usb/gadget/udc/atmel_usba_udc.c | 86 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 546da63..e05b16c 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -315,6 +315,37 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) } #endif +static int start_clock(struct usba_udc *udc) +{ + int ret; + + if (udc->clocked) + return 0; + + ret = clk_prepare_enable(udc->pclk); + if (ret) + return ret; + ret = clk_prepare_enable(udc->hclk); + if (ret) { + clk_disable_unprepare(udc->pclk); + return ret; + } + + udc->clocked = true; + return 0; +} + +static void stop_clock(struct usba_udc *udc) +{ + if (!udc->clocked) + return; + + clk_disable_unprepare(udc->hclk); + clk_disable_unprepare(udc->pclk); + + udc->clocked = false; +} + static int vbus_is_present(struct usba_udc *udc) { if (gpio_is_valid(udc->vbus_pin)) @@ -1719,37 +1750,48 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) return IRQ_HANDLED; } -static irqreturn_t usba_vbus_irq(int irq, void *devid) +static irqreturn_t usba_vbus_irq_thread(int irq, void *devid) { struct usba_udc *udc = devid; int vbus; + int ret; + unsigned long flags; /* debounce */ udelay(10); - spin_lock(&udc->lock); + mutex_lock(&udc->vbus_mutex); vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { + ret = start_clock(udc); + if (ret) + goto out; + + spin_lock_irqsave(&udc->lock, flags); toggle_bias(1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); + spin_unlock_irqrestore(&udc->lock, flags); } else { + spin_lock_irqsave(&udc->lock, flags); udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - if (udc->driver->disconnect) { - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); + + stop_clock(udc); + + if (udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); - spin_lock(&udc->lock); - } } udc->vbus_prev = vbus; } - spin_unlock(&udc->lock); +out: + mutex_unlock(&udc->vbus_mutex); return IRQ_HANDLED; } @@ -1766,17 +1808,12 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags); - ret = clk_prepare_enable(udc->pclk); - if (ret) - goto err_pclk; - ret = clk_prepare_enable(udc->hclk); - if (ret) - goto err_hclk; + mutex_lock(&udc->vbus_mutex); udc->vbus_prev = 0; if (gpio_is_valid(udc->vbus_pin)) { - ret = request_irq(gpio_to_irq(udc->vbus_pin), - usba_vbus_irq, 0, + ret = request_threaded_irq(gpio_to_irq(udc->vbus_pin), NULL, + usba_vbus_irq_thread, IRQF_ONESHOT, "atmel_usba_udc", udc); if (ret) { udc->vbus_pin = -ENODEV; @@ -1785,23 +1822,24 @@ static int atmel_usba_start(struct usb_gadget *gadget, } /* If Vbus is present, enable the controller and wait for reset */ - spin_lock_irqsave(&udc->lock, flags); if (vbus_is_present(udc) && udc->vbus_prev == 0) { + ret = start_clock(udc); + if (ret) + goto err; + + spin_lock_irqsave(&udc->lock, flags); toggle_bias(1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); + spin_unlock_irqrestore(&udc->lock, flags); udc->vbus_prev = 1; } - spin_unlock_irqrestore(&udc->lock, flags); + mutex_unlock(&udc->vbus_mutex); return 0; - err: - clk_disable_unprepare(udc->hclk); -err_hclk: - clk_disable_unprepare(udc->pclk); -err_pclk: + mutex_unlock(&udc->vbus_mutex); spin_lock_irqsave(&udc->lock, flags); udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); udc->driver = NULL; @@ -1826,8 +1864,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget) toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - clk_disable_unprepare(udc->hclk); - clk_disable_unprepare(udc->pclk); + stop_clock(udc); udc->driver = NULL; @@ -2007,6 +2044,7 @@ static int usba_udc_probe(struct platform_device *pdev) return PTR_ERR(hclk); spin_lock_init(&udc->lock); + mutex_init(&udc->vbus_mutex); udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a70706e..3ceed76 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -308,6 +308,9 @@ struct usba_udc { /* Protect hw registers from concurrent modifications */ spinlock_t lock; + /* Mutex to prevent concurrent start or stop */ + struct mutex vbus_mutex; + void __iomem *regs; void __iomem *fifo; @@ -321,6 +324,7 @@ struct usba_udc { struct clk *pclk; struct clk *hclk; struct usba_ep *usba_ep; + bool clocked; u16 devstatus;