From patchwork Thu Feb 12 17:54:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylvain Rochet X-Patchwork-Id: 5821271 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 54CD5BF440 for ; Thu, 12 Feb 2015 18:02:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5079620172 for ; Thu, 12 Feb 2015 18:02:34 +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 66E8F200E5 for ; Thu, 12 Feb 2015 18:02:32 +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 1YLy3T-00070e-Hv; Thu, 12 Feb 2015 17:59:59 +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 1YLxyT-0002j8-9b for linux-arm-kernel@lists.infradead.org; Thu, 12 Feb 2015 17:54:51 +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 1YLxxw-00065g-CF; Thu, 12 Feb 2015 18:54:17 +0100 Received: from gradator by spice.lan with local (Exim 4.84) (envelope-from ) id 1YLxxw-00039r-5L; Thu, 12 Feb 2015 18:54:16 +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: Thu, 12 Feb 2015 18:54:06 +0100 Message-Id: <1423763647-11687-4-git-send-email-sylvain.rochet@finsecur.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1423763647-11687-1-git-send-email-sylvain.rochet@finsecur.com> References: <1423763647-11687-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: [PATCHv7 3/4] 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-20150212_095449_575350_2A70E803 X-CRM114-Status: GOOD ( 17.12 ) 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) we 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. Signed-off-by: Sylvain Rochet Acked-by: Nicolas Ferre Acked-by: Boris Brezillon --- drivers/usb/gadget/udc/atmel_usba_udc.c | 144 +++++++++++++++++++++----------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 + 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index bbbd5f1..999e2f2 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) return IRQ_HANDLED; } -static irqreturn_t usba_vbus_irq(int irq, void *devid) +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 usba_start(struct usba_udc *udc) +{ + unsigned long flags; + int ret; + + ret = start_clock(udc); + if (ret) + return ret; + + spin_lock_irqsave(&udc->lock, flags); + toggle_bias(udc, 1); + usba_writel(udc, CTRL, USBA_ENABLE_MASK); + usba_int_enb_set(udc, USBA_END_OF_RESET); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static void usba_stop(struct usba_udc *udc) +{ + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + udc->gadget.speed = USB_SPEED_UNKNOWN; + reset_all_endpoints(udc); + + /* This will also disable the DP pullup */ + toggle_bias(udc, 0); + usba_writel(udc, CTRL, USBA_DISABLE_MASK); + spin_unlock_irqrestore(&udc->lock, flags); + + stop_clock(udc); +} + +static irqreturn_t usba_vbus_irq_thread(int irq, void *devid) { struct usba_udc *udc = devid; int vbus; @@ -1747,30 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) /* debounce */ udelay(10); - spin_lock(&udc->lock); + mutex_lock(&udc->vbus_mutex); vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { - toggle_bias(udc, 1); - usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_int_enb_set(udc, USBA_END_OF_RESET); + usba_start(udc); } else { - udc->gadget.speed = USB_SPEED_UNKNOWN; - reset_all_endpoints(udc); - toggle_bias(udc, 0); - usba_writel(udc, CTRL, USBA_DISABLE_MASK); - if (udc->driver->disconnect) { - spin_unlock(&udc->lock); + usba_stop(udc); + + if (udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); - spin_lock(&udc->lock); - } } udc->vbus_prev = vbus; } - spin_unlock(&udc->lock); - + mutex_unlock(&udc->vbus_mutex); return IRQ_HANDLED; } @@ -1782,57 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget, unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags); - 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; - } + mutex_lock(&udc->vbus_mutex); - udc->vbus_prev = 0; if (gpio_is_valid(udc->vbus_pin)) enable_irq(gpio_to_irq(udc->vbus_pin)); /* 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) { - toggle_bias(udc, 1); - usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_int_enb_set(udc, USBA_END_OF_RESET); - - udc->vbus_prev = 1; + udc->vbus_prev = vbus_is_present(udc); + if (udc->vbus_prev) { + ret = usba_start(udc); + if (ret) + goto err; } - spin_unlock_irqrestore(&udc->lock, flags); + mutex_unlock(&udc->vbus_mutex); return 0; + +err: + if (gpio_is_valid(udc->vbus_pin)) + disable_irq(gpio_to_irq(udc->vbus_pin)); + + mutex_unlock(&udc->vbus_mutex); + + spin_lock_irqsave(&udc->lock, flags); + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + udc->driver = NULL; + spin_unlock_irqrestore(&udc->lock, flags); + return ret; } static int atmel_usba_stop(struct usb_gadget *gadget) { struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); - unsigned long flags; if (gpio_is_valid(udc->vbus_pin)) disable_irq(gpio_to_irq(udc->vbus_pin)); - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - reset_all_endpoints(udc); - spin_unlock_irqrestore(&udc->lock, flags); - - /* This will also disable the DP pullup */ - toggle_bias(udc, 0); - usba_writel(udc, CTRL, USBA_DISABLE_MASK); - - clk_disable_unprepare(udc->hclk); - clk_disable_unprepare(udc->pclk); + usba_stop(udc); udc->driver = NULL; @@ -2054,6 +2101,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; @@ -2110,9 +2158,9 @@ static int usba_udc_probe(struct platform_device *pdev) if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { irq_set_status_flags(gpio_to_irq(udc->vbus_pin), IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, - gpio_to_irq(udc->vbus_pin), - usba_vbus_irq, 0, + ret = devm_request_threaded_irq(&pdev->dev, + gpio_to_irq(udc->vbus_pin), NULL, + usba_vbus_irq_thread, IRQF_ONESHOT, "atmel_usba_udc", udc); if (ret) { udc->vbus_pin = -ENODEV; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 497cd18..085749a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -313,6 +313,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; @@ -328,6 +331,7 @@ struct usba_udc { struct clk *hclk; struct usba_ep *usba_ep; bool bias_pulse_needed; + bool clocked; u16 devstatus;