From patchwork Wed Jul 10 00:34:15 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Julius Werner X-Patchwork-Id: 2825491 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AA9A4C0AB2 for ; Wed, 10 Jul 2013 00:34:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6569A20107 for ; Wed, 10 Jul 2013 00:34:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0427F2010A for ; Wed, 10 Jul 2013 00:34:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753848Ab3GJAeh (ORCPT ); Tue, 9 Jul 2013 20:34:37 -0400 Received: from mail-pb0-f52.google.com ([209.85.160.52]:39046 "EHLO mail-pb0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753345Ab3GJAeg (ORCPT ); Tue, 9 Jul 2013 20:34:36 -0400 Received: by mail-pb0-f52.google.com with SMTP id xa12so6037304pbc.11 for ; Tue, 09 Jul 2013 17:34:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:x-mailer; bh=jlTpMv4OuQt8A0XQbgJ6AeRQhq4SC8hAx0NfXUG8Ats=; b=UK0XzOTjpkSoZECtmxBoUsdMMNjDl1taZ0C6SGnL2jZulKanwrJuCqG5nPhK6gEcGl O0gB49WKDx64Dy7lQBevFTUot300qso2iIsnP7TAtetCaOvo104f0RwZ7FcP6ZwOUiMn KZzMeXt0Vg17NePmg7bdBLnVgqUhNjt+dTWRs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:x-gm-message-state; bh=jlTpMv4OuQt8A0XQbgJ6AeRQhq4SC8hAx0NfXUG8Ats=; b=Ku2uTuyqKtvA8EQf6DKWEFfvhu1W8IGqoIhAPCb6EWKzp0aGBRsxD8u1ME4BO/8Hbu bEyhp8ZVWGVZ7mSmxUHi65HCzMDbaPnpxFVdNWfoTb9bn/gUkqVBAVNAOmm0ZerE376w BqEvrtAQ4UZ3szZU+JXzwUv0GPU9BLeyFnIskZOBsRnBMtPxA8zXaFaXRF8gITFfEqXS /TApF02nQZkSR58XcpvUO9DkZ8Gkjy3dHc/Uvl0UbebUGIuKt3iZ8zQX7GggopU0yPS0 lB8/9W3kJVexMcFQxaahZh/I/83ZueggBLit3hKPBCrmMB4pREbHMHAV1iKxSGYtus89 fNnQ== X-Received: by 10.67.23.3 with SMTP id hw3mr1910005pad.54.1373416475015; Tue, 09 Jul 2013 17:34:35 -0700 (PDT) Received: from jwerner-linux.mtv.corp.google.com (jwerner-linux.mtv.corp.google.com [172.22.72.75]) by mx.google.com with ESMTPSA id lk9sm14916880pab.2.2013.07.09.17.34.33 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 09 Jul 2013 17:34:34 -0700 (PDT) From: Julius Werner To: linux-kernel@vger.kernel.org, Felipe Balbi Cc: linux-usb@vger.kernel.org, devicetree-discuss@lists.ozlabs.org, linux-samsung-soc@vger.kernel.org, Vivek Gautam , Praveen Paneri , Kukjin Kim , Tushar Behera , Doug Anderson , Olof Johansson , Vincent Palatin , Julius Werner Subject: [PATCH] usb: phy: samsung-usb2: Toggle HSIC GPIO from device tree Date: Tue, 9 Jul 2013 17:34:15 -0700 Message-Id: <1373416455-30358-1-git-send-email-jwerner@chromium.org> X-Mailer: git-send-email 1.8.3 X-Gm-Message-State: ALoCoQnEXyEwwxNfNm02o0e65y6DKygAAYhEY67R1AohLggelHYNBCQhNjfUp5vPPuif0q/GCX6t Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-7.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds support for a new 'samsung,hsic-reset-gpio' in the device tree, which will be interpreted as an active-low reset pin during PHY initialization when it exists. Useful for intergrated HSIC devices like an SMSC 3503 hub. It is necessary to add this directly to the PHY initialization to get the timing right, since resetting a HSIC device after it has already been enumerated can confuse the USB stack. Also fixes PHY semaphore code to make sure we always go through the setup at least once, even if it was already turned on (e.g. by firmware), and changes a spinlock to a mutex to allow sleeping in the critical section. Change-Id: Ieecac52c27daa7a17a7ed3b2863ddba3aeb8d16f Signed-off-by: Julius Werner --- .../devicetree/bindings/usb/samsung-usbphy.txt | 10 ++++++ drivers/usb/phy/phy-samsung-usb.c | 17 ++++++++++ drivers/usb/phy/phy-samsung-usb.h | 7 ++-- drivers/usb/phy/phy-samsung-usb2.c | 38 ++++++++++------------ drivers/usb/phy/phy-samsung-usb3.c | 12 +++---- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 33fd354..82e2e16 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -31,6 +31,12 @@ Optional properties: - ranges: allows valid translation between child's address space and parent's address space. +- samsung,hsic-reset-gpio: an active low GPIO pin that resets a device + connected to the HSIC port. Useful for things like + an on-board SMSC3503 hub. +- pinctrl-0: Pin control group containing the HSIC reset GPIO pin. +- pinctrl-names: Should contain only one value - "default". + - The child node 'usbphy-sys' to the node 'usbphy' is for the system controller interface for usb-phy. It should provide the following information required by usb-phy controller to control phy. @@ -56,6 +62,10 @@ Example: clocks = <&clock 2>, <&clock 305>; clock-names = "xusbxti", "otg"; + samsung,hsic-reset-gpio = <&gpx2 4 1>; + pinctrl-names = "default"; + pinctrl-0 = <&hsic_reset>; + usbphy-sys { /* USB device and host PHY_CONTROL registers */ reg = <0x10020704 0x8>; diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index ac025ca..23f1d70 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "phy-samsung-usb.h" @@ -58,6 +59,22 @@ int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) if (sphy->sysreg == NULL) dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); + /* + * Some boards have a separate active-low reset GPIO for their HSIC USB + * devices. If they don't, this will just stay at an invalid value and + * the init code will ignore it. + */ + sphy->hsic_reset_gpio = of_get_named_gpio(sphy->dev->of_node, + "samsung,hsic-reset-gpio", 0); + if (gpio_is_valid(sphy->hsic_reset_gpio)) { + if (devm_gpio_request_one(sphy->dev, sphy->hsic_reset_gpio, + GPIOF_OUT_INIT_LOW, "samsung_hsic_reset")) { + dev_err(sphy->dev, "can't request hsic reset gpio %d\n", + sphy->hsic_reset_gpio); + sphy->hsic_reset_gpio = -EINVAL; + } + } + of_node_put(usbphy_sys); return 0; diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 68771bf..0703878 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -16,6 +16,7 @@ * GNU General Public License for more details. */ +#include #include /* Register definitions */ @@ -301,7 +302,8 @@ struct samsung_usbphy_drvdata { * @phy_type: Samsung SoCs specific phy types: #HOST * #DEVICE * @phy_usage: usage count for phy - * @lock: lock for phy operations + * @mutex: mutex for phy operations (usb2phy must sleep, so no spinlock!) + * @hsic_reset_gpio: Active low GPIO that resets connected HSIC device */ struct samsung_usbphy { struct usb_phy phy; @@ -315,7 +317,8 @@ struct samsung_usbphy { const struct samsung_usbphy_drvdata *drv_data; enum samsung_usb_phy_type phy_type; atomic_t phy_usage; - spinlock_t lock; + struct mutex mutex; + int hsic_reset_gpio; }; #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index 1011c16..2db2113 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,15 +44,6 @@ static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -static bool exynos5_phyhost_is_on(void __iomem *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - return !(reg & HOST_CTRL0_SIDDQ); -} - static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -68,10 +60,8 @@ static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) * the last consumer to disable it. */ - atomic_inc(&sphy->phy_usage); - - if (exynos5_phyhost_is_on(regs)) { - dev_info(sphy->dev, "Already power on PHY\n"); + if (atomic_inc_return(&sphy->phy_usage) != 1) { + dev_info(sphy->dev, "USB PHY already initialized\n"); return; } @@ -132,6 +122,13 @@ static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); /* HSIC phy configuration */ + if (gpio_is_valid(sphy->hsic_reset_gpio)) { + gpio_set_value(sphy->hsic_reset_gpio, 0); + udelay(100); /* Keep reset as active/low for 100us */ + gpio_set_value(sphy->hsic_reset_gpio, 1); + usleep_range(4000, 10000); /* wait for device init */ + } + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | HSIC_CTRL_REFCLKSEL | HSIC_CTRL_PHYSWRST); @@ -220,6 +217,9 @@ static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + if (gpio_is_valid(sphy->hsic_reset_gpio)) + gpio_set_value(sphy->hsic_reset_gpio, 0); + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); phyhost |= (HOST_CTRL0_SIDDQ | HOST_CTRL0_FORCESUSPEND | @@ -267,7 +267,6 @@ static int samsung_usb2phy_init(struct usb_phy *phy) { struct samsung_usbphy *sphy; struct usb_bus *host = NULL; - unsigned long flags; int ret = 0; sphy = phy_to_sphy(phy); @@ -281,7 +280,7 @@ static int samsung_usb2phy_init(struct usb_phy *phy) return ret; } - spin_lock_irqsave(&sphy->lock, flags); + mutex_lock(&sphy->mutex); if (host) { /* setting default phy-type for USB 2.0 */ @@ -304,7 +303,7 @@ static int samsung_usb2phy_init(struct usb_phy *phy) /* Initialize usb phy registers */ sphy->drv_data->phy_enable(sphy); - spin_unlock_irqrestore(&sphy->lock, flags); + mutex_unlock(&sphy->mutex); /* Disable the phy clock */ clk_disable_unprepare(sphy->clk); @@ -319,7 +318,6 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy) { struct samsung_usbphy *sphy; struct usb_bus *host = NULL; - unsigned long flags; sphy = phy_to_sphy(phy); @@ -330,7 +328,7 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy) return; } - spin_lock_irqsave(&sphy->lock, flags); + mutex_lock(&sphy->mutex); if (host) { /* setting default phy-type for USB 2.0 */ @@ -350,7 +348,7 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy) else if (sphy->drv_data->set_isolation) sphy->drv_data->set_isolation(sphy, true); - spin_unlock_irqrestore(&sphy->lock, flags); + mutex_unlock(&sphy->mutex); clk_disable_unprepare(sphy->clk); } @@ -422,7 +420,7 @@ static int samsung_usb2phy_probe(struct platform_device *pdev) sphy->phy.otg->phy = &sphy->phy; sphy->phy.otg->set_host = samsung_usbphy_set_host; - spin_lock_init(&sphy->lock); + mutex_init(&sphy->mutex); platform_set_drvdata(pdev, sphy); diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c index 300e0cf..6ec3f08 100644 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -164,7 +164,6 @@ static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy) static int samsung_usb3phy_init(struct usb_phy *phy) { struct samsung_usbphy *sphy; - unsigned long flags; int ret = 0; sphy = phy_to_sphy(phy); @@ -176,7 +175,7 @@ static int samsung_usb3phy_init(struct usb_phy *phy) return ret; } - spin_lock_irqsave(&sphy->lock, flags); + mutex_lock(&sphy->mutex); /* setting default phy-type for USB 3.0 */ samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); @@ -188,7 +187,7 @@ static int samsung_usb3phy_init(struct usb_phy *phy) /* Initialize usb phy registers */ sphy->drv_data->phy_enable(sphy); - spin_unlock_irqrestore(&sphy->lock, flags); + mutex_unlock(&sphy->mutex); /* Disable the phy clock */ clk_disable_unprepare(sphy->clk); @@ -202,7 +201,6 @@ static int samsung_usb3phy_init(struct usb_phy *phy) static void samsung_usb3phy_shutdown(struct usb_phy *phy) { struct samsung_usbphy *sphy; - unsigned long flags; sphy = phy_to_sphy(phy); @@ -211,7 +209,7 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy) return; } - spin_lock_irqsave(&sphy->lock, flags); + mutex_lock(&sphy->mutex); /* setting default phy-type for USB 3.0 */ samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); @@ -223,7 +221,7 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy) if (sphy->drv_data->set_isolation) sphy->drv_data->set_isolation(sphy, true); - spin_unlock_irqrestore(&sphy->lock, flags); + mutex_unlock(&sphy->mutex); clk_disable_unprepare(sphy->clk); } @@ -279,7 +277,7 @@ static int samsung_usb3phy_probe(struct platform_device *pdev) if (sphy->ref_clk_freq < 0) return -EINVAL; - spin_lock_init(&sphy->lock); + mutex_init(&sphy->mutex); platform_set_drvdata(pdev, sphy);