From patchwork Mon Apr 9 22:02:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Osipenko X-Patchwork-Id: 896435 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="khiCdcha"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40Kkpv3p9mz9s1l for ; Tue, 10 Apr 2018 08:04:07 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751945AbeDIWEG (ORCPT ); Mon, 9 Apr 2018 18:04:06 -0400 Received: from mail-wm0-f67.google.com ([74.125.82.67]:52803 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753036AbeDIWDg (ORCPT ); Mon, 9 Apr 2018 18:03:36 -0400 Received: by mail-wm0-f67.google.com with SMTP id g8so22057895wmd.2; Mon, 09 Apr 2018 15:03:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=rAkV+40STm/h23ww8IOE01VsyC54jjq+wroGvX+HAYQ=; b=khiCdchamrJOs20LJJML8kCpNPUSRw63QnVUmPWnz0hKmQz+HU8fuXAImAoI2HIArk mwbs8SYBIIBz/vXwK2N0slVXc33BOACTusm03A5qD5Z7ncCUYpTg+0e79ffU4ax1EKg6 L/KOtGycRVVSkIjBAe6ziDpZTXTVAK34+49VE8KS/NejuelMgc8HM9o5fo0keH6nXVd+ GfmtY4BJDEWU0BU++iegc+m6+xIIVGohwCGInHK2z8FIfiSMIHdH1WWXbSevHH+xiDhY qLzNKJwDnBfaR5AtrWU+iPDpuP/cLyQuUm0Dj2W4tlAjww+c7B9bzVY5bfAdZeh9wsGF Ss6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=rAkV+40STm/h23ww8IOE01VsyC54jjq+wroGvX+HAYQ=; b=mv1hnIdRuokvmqW5PU0gx6V44ECJxNk8LsTMvOuOwZ1uXXZoUD/WdODMWeev9ynh7r qeFidSSVPTVmwEXTAywZM/7I8ogGSpm42yMe51pz2HpKLBbDf4Rem/3YbDUSrOw5k4/z Uu1p7fSewWmWoyp+Bo0ORKBRaL0mPSgXbW1x2p4cl/H35DAS8KHjEEOSzAHWSUr/+3PL IgsgMO/7ozH4w6ZCSJpYmJEktHb5VRntx/b9vMOaoOO3YKvHtD9GVHkHLwp/XXGF6YzL ctCNBOuMz512bWV6VNpU9SYj3eF5vfzP/Uba4viLBGb1InX79N/ys0WtUfgDWcha8CcT mw/A== X-Gm-Message-State: ALQs6tDESRhXntU/Mu37RBy0qNhOiw4eQDL9a6qXb2boPU92/i05LoUq EN36v48xMq2gUdFDqpNe5lg= X-Google-Smtp-Source: AIpwx4/JXKFycPlgSbmPgU5A3JK6qaLsc+/B0r+5zya0oSn+QzeDA5LX62vBByfsqDufPH6ML7BcgA== X-Received: by 10.28.150.194 with SMTP id y185mr1049276wmd.29.1523311414517; Mon, 09 Apr 2018 15:03:34 -0700 (PDT) Received: from localhost.localdomain (ppp109-252-91-235.pppoe.spdop.ru. [109.252.91.235]) by smtp.gmail.com with ESMTPSA id d48sm1830019wrd.12.2018.04.09.15.03.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 09 Apr 2018 15:03:33 -0700 (PDT) From: Dmitry Osipenko To: Thierry Reding , Jonathan Hunter , Felipe Balbi , Alan Stern , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 2/3] usb: tegra: Move utmi-pads reset from ehci-tegra to tegra-phy Date: Tue, 10 Apr 2018 01:02:58 +0300 Message-Id: <66330166c6a53e8f463ec231e3cb8195fa3036cc.1523307883.git.digetx@gmail.com> X-Mailer: git-send-email 2.16.3 In-Reply-To: References: In-Reply-To: References: Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org UTMI pads are shared by USB controllers and reset of UTMI pads is shared with the reset of USB1 controller. Currently reset of UTMI pads is done by the EHCI driver and ChipIdea UDC works because EHCI driver always happen to be probed first. Move reset controls from ehci-tegra to tegra-phy in order to resolve the problem. Signed-off-by: Dmitry Osipenko --- drivers/usb/host/ehci-tegra.c | 87 ++++++++++++++++++--------------------- drivers/usb/phy/phy-tegra-usb.c | 79 ++++++++++++++++++++++++++++++++--- include/linux/usb/tegra_usb_phy.h | 2 + 3 files changed, 115 insertions(+), 53 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a6f4389f7e88..4d2cdec4cb78 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -36,7 +36,6 @@ #define DRV_NAME "tegra-ehci" static struct hc_driver __read_mostly tegra_ehci_hc_driver; -static bool usb1_reset_attempted; struct tegra_ehci_soc_config { bool has_hostpc; @@ -51,67 +50,54 @@ struct tegra_ehci_hcd { enum tegra_usb_phy_port_speed port_speed; }; -/* - * The 1st USB controller contains some UTMI pad registers that are global for - * all the controllers on the chip. Those registers are also cleared when - * reset is asserted to the 1st controller. This means that the 1st controller - * can only be reset when no other controlled has finished probing. So we'll - * reset the 1st controller before doing any other setup on any of the - * controllers, and then never again. - * - * Since this is a PHY issue, the Tegra PHY driver should probably be doing - * the resetting of the USB controllers. But to keep compatibility with old - * device trees that don't have reset phandles in the PHYs, do it here. - * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI - * device isn't the first one to finish probing, so warn them. - */ static int tegra_reset_usb_controller(struct platform_device *pdev) { struct device_node *phy_np; struct usb_hcd *hcd = platform_get_drvdata(pdev); struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; - bool has_utmi_pad_registers = false; + struct reset_control *rst; + int err; phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0); if (!phy_np) return -ENOENT; - if (of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) - has_utmi_pad_registers = true; + /* + * The 1st USB controller contains some UTMI pad registers that are + * global for all the controllers on the chip. Those registers are + * also cleared when reset is asserted to the 1st controller. + */ + rst = of_reset_control_get_shared(phy_np, "utmi-pads"); + if (IS_ERR(rst)) { + dev_warn(&pdev->dev, + "can't get utmi-pads reset from the PHY\n"); + dev_warn(&pdev->dev, + "continuing, but please update your DT\n"); + } else { + /* + * PHY driver performs UTMI-pads reset in a case of + * non-legacy DT. + */ + reset_control_put(rst); + } - if (!usb1_reset_attempted) { - struct reset_control *usb1_reset; + of_node_put(phy_np); - if (!has_utmi_pad_registers) - usb1_reset = of_reset_control_get(phy_np, "utmi-pads"); - else - usb1_reset = tegra->rst; - - if (IS_ERR(usb1_reset)) { - dev_warn(&pdev->dev, - "can't get utmi-pads reset from the PHY\n"); - dev_warn(&pdev->dev, - "continuing, but please update your DT\n"); - } else { - reset_control_assert(usb1_reset); - udelay(1); - reset_control_deassert(usb1_reset); - - if (!has_utmi_pad_registers) - reset_control_put(usb1_reset); - } + /* reset control is shared, hence initialize it first */ + err = reset_control_deassert(tegra->rst); + if (err) + return err; - usb1_reset_attempted = true; - } + err = reset_control_assert(tegra->rst); + if (err) + return err; - if (!has_utmi_pad_registers) { - reset_control_assert(tegra->rst); - udelay(1); - reset_control_deassert(tegra->rst); - } + udelay(1); - of_node_put(phy_np); + err = reset_control_deassert(tegra->rst); + if (err) + return err; return 0; } @@ -440,7 +426,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto cleanup_hcd_create; } - tegra->rst = devm_reset_control_get(&pdev->dev, "usb"); + tegra->rst = devm_reset_control_get_shared(&pdev->dev, "usb"); if (IS_ERR(tegra->rst)) { dev_err(&pdev->dev, "Can't get ehci reset\n"); err = PTR_ERR(tegra->rst); @@ -452,8 +438,10 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto cleanup_hcd_create; err = tegra_reset_usb_controller(pdev); - if (err) + if (err) { + dev_err(&pdev->dev, "Failed to reset controller\n"); goto cleanup_clk_en; + } u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { @@ -538,6 +526,9 @@ static int tegra_ehci_remove(struct platform_device *pdev) usb_phy_shutdown(hcd->usb_phy); usb_remove_hcd(hcd); + reset_control_assert(tegra->rst); + udelay(1); + clk_disable_unprepare(tegra->clk); usb_put_hcd(hcd); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index e46219e7fa93..ea7ef1dc0b42 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -236,17 +236,83 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable) static int utmip_pad_open(struct tegra_usb_phy *phy) { - int err; + int ret; phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads"); if (IS_ERR(phy->pad_clk)) { - err = PTR_ERR(phy->pad_clk); + ret = PTR_ERR(phy->pad_clk); dev_err(phy->u_phy.dev, - "Failed to get UTMIP pad clock: %d\n", err); - return err; + "Failed to get UTMIP pad clock: %d\n", ret); + return ret; } - return 0; + phy->pad_rst = devm_reset_control_get_optional_shared( + phy->u_phy.dev, "utmi-pads"); + if (IS_ERR(phy->pad_rst)) { + ret = PTR_ERR(phy->pad_rst); + dev_err(phy->u_phy.dev, + "Failed to get UTMI-pads reset: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(phy->pad_clk); + if (ret) { + dev_err(phy->u_phy.dev, + "Failed to enable UTMI-pads clock: %d\n", ret); + return ret; + } + + spin_lock(&utmip_pad_lock); + + ret = reset_control_deassert(phy->pad_rst); + if (ret) { + dev_err(phy->u_phy.dev, + "Failed to initialize UTMI-pads reset: %d\n", ret); + goto unlock; + } + + ret = reset_control_assert(phy->pad_rst); + if (ret) { + dev_err(phy->u_phy.dev, + "Failed to assert UTMI-pads reset: %d\n", ret); + goto unlock; + } + + udelay(1); + + ret = reset_control_deassert(phy->pad_rst); + if (ret) + dev_err(phy->u_phy.dev, + "Failed to deassert UTMI-pads reset: %d\n", ret); +unlock: + spin_unlock(&utmip_pad_lock); + + clk_disable_unprepare(phy->pad_clk); + + return ret; +} + +static int utmip_pad_close(struct tegra_usb_phy *phy) +{ + int ret; + + ret = clk_prepare_enable(phy->pad_clk); + if (ret) { + dev_err(phy->u_phy.dev, + "Failed to enable UTMI-pads clock: %d\n", ret); + return ret; + } + + ret = reset_control_assert(phy->pad_rst); + if (ret) + dev_err(phy->u_phy.dev, + "Failed to assert UTMI-pads reset: %d\n", ret); + + udelay(1); + + clk_disable_unprepare(phy->pad_clk); + + return ret; } static void utmip_pad_power_on(struct tegra_usb_phy *phy) @@ -700,6 +766,9 @@ static void tegra_usb_phy_close(struct tegra_usb_phy *phy) if (!IS_ERR(phy->vbus)) regulator_disable(phy->vbus); + if (!phy->is_ulpi_phy) + utmip_pad_close(phy); + clk_disable_unprepare(phy->pll_u); } diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d641ea1660b7..0c5c3ea8b2d7 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -17,6 +17,7 @@ #define __TEGRA_USB_PHY_H #include +#include #include /* @@ -76,6 +77,7 @@ struct tegra_usb_phy { bool is_legacy_phy; bool is_ulpi_phy; int reset_gpio; + struct reset_control *pad_rst; }; void tegra_usb_phy_preresume(struct usb_phy *phy);