From patchwork Fri Aug 28 01:10:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: JC Kuo X-Patchwork-Id: 253284 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78A23C433E6 for ; Fri, 28 Aug 2020 01:12:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4EB812080C for ; Fri, 28 Aug 2020 01:12:01 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="gir4tIU4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728228AbgH1BMA (ORCPT ); Thu, 27 Aug 2020 21:12:00 -0400 Received: from hqnvemgate24.nvidia.com ([216.228.121.143]:9882 "EHLO hqnvemgate24.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726147AbgH1BL7 (ORCPT ); Thu, 27 Aug 2020 21:11:59 -0400 Received: from hqpgpgate102.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate24.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 27 Aug 2020 18:09:56 -0700 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate102.nvidia.com (PGP Universal service); Thu, 27 Aug 2020 18:11:58 -0700 X-PGP-Universal: processed; by hqpgpgate102.nvidia.com on Thu, 27 Aug 2020 18:11:58 -0700 Received: from HQMAIL109.nvidia.com (172.20.187.15) by HQMAIL111.nvidia.com (172.20.187.18) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 28 Aug 2020 01:11:57 +0000 Received: from rnnvemgw01.nvidia.com (10.128.109.123) by HQMAIL109.nvidia.com (172.20.187.15) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Fri, 28 Aug 2020 01:11:57 +0000 Received: from jckuo-lt.nvidia.com (Not Verified[10.19.101.4]) by rnnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 27 Aug 2020 18:11:56 -0700 From: JC Kuo To: , , , , CC: , , , , , JC Kuo Subject: [PATCH 02/12] clk: tegra: don't enable PLLE HW sequencer at init Date: Fri, 28 Aug 2020 09:10:46 +0800 Message-ID: <20200828011056.1067559-3-jckuo@nvidia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200828011056.1067559-1-jckuo@nvidia.com> References: <20200828011056.1067559-1-jckuo@nvidia.com> MIME-Version: 1.0 X-NVConfidentiality: public DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1598576996; bh=H0R9Siiu+twi27jJX/Jx8a1i8OSPS6fZLapkwSfwBYg=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:X-NVConfidentiality: Content-Transfer-Encoding:Content-Type; b=gir4tIU4i7QiR/gjr2bUR54nyhEjRZnO1uhL3csVFY0pYSrFU9zXnOfDc3Be+5is4 ubWBUOcJYU35hdkwADyug2Zw+it6sOKdyf8s/hjaipG122vyq2rx09qkYe9/9f2/ib 2v2NHpLdIlCszVSXx+c4YgK9p0DRAdgD6Pb0ORfL47AKKh4x4w6pvrO7fbPXu6dOkx b3OWu+0JULuIgBAIUTAbLjyCRlJFO6spDH9hIzVw3BPo/IvXcq07vehsvCkolCiDju 7cBLhNY3KpIgooQWJ8LsFOWFSpykALQUynPUCo4KDCyAPWWSsx28HRV5F+r1I5mIKA 5XnKsTbu5eljA== Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware power sequencers' output to enable/disable PLLE. PLLE hardware power sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers are enabled. Signed-off-by: JC Kuo --- drivers/clk/tegra/clk-pll.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index c5cc0a2dac6f..0193cebe8c5a 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -2515,18 +2515,6 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw) pll_writel(val, PLLE_SS_CTRL, pll); udelay(1); - val = pll_readl_misc(pll); - val &= ~PLLE_MISC_IDDQ_SW_CTRL; - pll_writel_misc(val, pll); - - val = pll_readl(pll->params->aux_reg, pll); - val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE); - val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL); - pll_writel(val, pll->params->aux_reg, pll); - udelay(1); - val |= PLLE_AUX_SEQ_ENABLE; - pll_writel(val, pll->params->aux_reg, pll); - out: if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); From patchwork Fri Aug 28 01:10:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: JC Kuo X-Patchwork-Id: 253279 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 139C5C433E6 for ; Fri, 28 Aug 2020 01:13:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DBF1F2080C for ; Fri, 28 Aug 2020 01:13:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="YvfSerXk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728251AbgH1BNJ (ORCPT ); Thu, 27 Aug 2020 21:13:09 -0400 Received: from hqnvemgate25.nvidia.com ([216.228.121.64]:15010 "EHLO hqnvemgate25.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728324AbgH1BMJ (ORCPT ); Thu, 27 Aug 2020 21:12:09 -0400 Received: from hqpgpgate102.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate25.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 27 Aug 2020 18:11:25 -0700 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate102.nvidia.com (PGP Universal service); Thu, 27 Aug 2020 18:12:08 -0700 X-PGP-Universal: processed; by hqpgpgate102.nvidia.com on Thu, 27 Aug 2020 18:12:08 -0700 Received: from HQMAIL107.nvidia.com (172.20.187.13) by HQMAIL101.nvidia.com (172.20.187.10) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 28 Aug 2020 01:12:04 +0000 Received: from rnnvemgw01.nvidia.com (10.128.109.123) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Fri, 28 Aug 2020 01:12:04 +0000 Received: from jckuo-lt.nvidia.com (Not Verified[10.19.101.4]) by rnnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 27 Aug 2020 18:12:04 -0700 From: JC Kuo To: , , , , CC: , , , , , JC Kuo Subject: [PATCH 05/12] phy: tegra: xusb: add sleepwalk and suspend/resume Date: Fri, 28 Aug 2020 09:10:49 +0800 Message-ID: <20200828011056.1067559-6-jckuo@nvidia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200828011056.1067559-1-jckuo@nvidia.com> References: <20200828011056.1067559-1-jckuo@nvidia.com> MIME-Version: 1.0 X-NVConfidentiality: public DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1598577085; bh=kX0RwgveLiINhWbSkybtsDBLi2N6bZpxn7iuTnuZPAY=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:X-NVConfidentiality: Content-Transfer-Encoding:Content-Type; b=YvfSerXkhclGj0RSn+rxvV6JPnNNZ2F643vhhpk66ZYWJPkFrXI1OGkApULndR0RM 9DJOtXRORudac/F/cfGL5urUG+RyRPv6ZCzaKLPqMUbkG3qbDlJkEJrNw6ITDnlAp5 cBSKTmGH7100W8RP1x+9f8ZpljGERKVKUuN6/LsqUkOaIKrP2Ije3AntxFsdfCe406 WYuwdRK9JSTznp46HLSK4ZKfwh5fLTjiCcRiEoRyqYoDM8k2KB4aKQpHD9cUF9VNXT 2wGXiCpVyvBZ3eoNlGOidR08X5O5MLDJQQcsYXgceBj/qNr/2/9hipKps9p1SnIEnm M/DZNA8Mnm1Jw== Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This commit adds sleepwalk/wake and suspend/resume interfaces to Tegra XUSB PHY driver. Tegra XUSB host controller driver makes use of sleepwalk functions to enable/disable sleepwalk circuit which is in always-on partition can respond to USB resume signals when controller is not powered. Sleepwalk can be enabled/disabled for any USB phy individually. - tegra_xusb_padctl_enable_phy_sleepwalk() - tegra_xusb_padctl_disable_phy_sleepwalk() Tegra XUSB host controller driver makes use of wake functions to enable/disable/query wake circuit which is in always-on partition can wake system up when USB resume happens. Wake circuit can be enabled/disabled for any USB phy individually. - tegra_xusb_padctl_enable_phy_wake() - tegra_xusb_padctl_disable_phy_wake() - tegra_xusb_padctl_remote_wake_detected() This commit also adds two system suspend stubs that can be used to save and restore XUSB PADCTL context during system suspend and resume. - tegra_xusb_padctl_suspend_noirq() - tegra_xusb_padctl_resume_noirq() Signed-off-by: JC Kuo --- drivers/phy/tegra/xusb.c | 78 ++++++++++++++++++++++++++++++++++ drivers/phy/tegra/xusb.h | 13 ++++++ include/linux/phy/tegra/xusb.h | 13 ++++++ 3 files changed, 104 insertions(+) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 74abd67e3a31..3a1551c42c29 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1274,10 +1274,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev) return err; } +static int tegra_xusb_padctl_suspend_noirq(struct device *dev) +{ + struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); + + if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq) + return padctl->soc->ops->suspend_noirq(padctl); + + return 0; +} + +static int tegra_xusb_padctl_resume_noirq(struct device *dev) +{ + struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); + + if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq) + return padctl->soc->ops->resume_noirq(padctl); + + return 0; +} + +static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq, + tegra_xusb_padctl_resume_noirq) +}; + static struct platform_driver tegra_xusb_padctl_driver = { .driver = { .name = "tegra-xusb-padctl", .of_match_table = tegra_xusb_padctl_of_match, + .pm = &tegra_xusb_padctl_pm_ops, }, .probe = tegra_xusb_padctl_probe, .remove = tegra_xusb_padctl_remove, @@ -1344,6 +1370,58 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); +int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy, + enum usb_device_speed speed) +{ + if (padctl->soc->ops->enable_phy_sleepwalk) + return padctl->soc->ops->enable_phy_sleepwalk(padctl, phy, + speed); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk); + +int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (padctl->soc->ops->disable_phy_sleepwalk) + return padctl->soc->ops->disable_phy_sleepwalk(padctl, phy); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk); + +int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (padctl->soc->ops->enable_phy_wake) + return padctl->soc->ops->enable_phy_wake(padctl, phy); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake); + +int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (padctl->soc->ops->disable_phy_wake) + return padctl->soc->ops->disable_phy_wake(padctl, phy); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake); + +int tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (padctl->soc->ops->remote_wake_detected) + return padctl->soc->ops->remote_wake_detected(phy); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected); + int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int port, bool enable) { diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index 485b692a3b15..c1c4ea3ce050 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -392,6 +393,8 @@ struct tegra_xusb_padctl_ops { const struct tegra_xusb_padctl_soc *soc); void (*remove)(struct tegra_xusb_padctl *padctl); + int (*suspend_noirq)(struct tegra_xusb_padctl *padctl); + int (*resume_noirq)(struct tegra_xusb_padctl *padctl); int (*usb3_save_context)(struct tegra_xusb_padctl *padctl, unsigned int index); int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl, @@ -400,6 +403,16 @@ struct tegra_xusb_padctl_ops { unsigned int index, bool enable); int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set); int (*utmi_port_reset)(struct phy *phy); + int (*enable_phy_sleepwalk)(struct tegra_xusb_padctl *padctl, + struct phy *phy, + enum usb_device_speed speed); + int (*disable_phy_sleepwalk)(struct tegra_xusb_padctl *padctl, + struct phy *phy); + int (*enable_phy_wake)(struct tegra_xusb_padctl *padctl, + struct phy *phy); + int (*disable_phy_wake)(struct tegra_xusb_padctl *padctl, + struct phy *phy); + int (*remote_wake_detected)(struct phy *phy); }; struct tegra_xusb_padctl_soc { diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h index 71d956935405..9621e69067e1 100644 --- a/include/linux/phy/tegra/xusb.h +++ b/include/linux/phy/tegra/xusb.h @@ -8,6 +8,7 @@ struct tegra_xusb_padctl; struct device; +enum usb_device_speed; struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev); void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl); @@ -23,4 +24,16 @@ int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl, int tegra_phy_xusb_utmi_port_reset(struct phy *phy); int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, unsigned int port); +int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy, + enum usb_device_speed speed); +int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy); +int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy); +int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy); +int tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, + struct phy *phy); + #endif /* PHY_TEGRA_XUSB_H */ From patchwork Fri Aug 28 01:10:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: JC Kuo X-Patchwork-Id: 253283 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A44BDC433E7 for ; Fri, 28 Aug 2020 01:12:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7BE0320848 for ; Fri, 28 Aug 2020 01:12:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="TF6l2YFW" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728367AbgH1BMT (ORCPT ); Thu, 27 Aug 2020 21:12:19 -0400 Received: from hqnvemgate26.nvidia.com ([216.228.121.65]:6745 "EHLO hqnvemgate26.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728295AbgH1BMN (ORCPT ); Thu, 27 Aug 2020 21:12:13 -0400 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate26.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 27 Aug 2020 18:11:59 -0700 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Thu, 27 Aug 2020 18:12:13 -0700 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Thu, 27 Aug 2020 18:12:13 -0700 Received: from HQMAIL109.nvidia.com (172.20.187.15) by HQMAIL111.nvidia.com (172.20.187.18) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 28 Aug 2020 01:12:09 +0000 Received: from rnnvemgw01.nvidia.com (10.128.109.123) by HQMAIL109.nvidia.com (172.20.187.15) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Fri, 28 Aug 2020 01:12:09 +0000 Received: from jckuo-lt.nvidia.com (Not Verified[10.19.101.4]) by rnnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 27 Aug 2020 18:12:08 -0700 From: JC Kuo To: , , , , CC: , , , , , JC Kuo Subject: [PATCH 07/12] arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop Date: Fri, 28 Aug 2020 09:10:51 +0800 Message-ID: <20200828011056.1067559-8-jckuo@nvidia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200828011056.1067559-1-jckuo@nvidia.com> References: <20200828011056.1067559-1-jckuo@nvidia.com> MIME-Version: 1.0 X-NVConfidentiality: public DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1598577119; bh=ULNJL432P9gTH+qGQQEOhfds0zQ9eDoNC9eYcP0k/AM=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:X-NVConfidentiality: Content-Transfer-Encoding:Content-Type; b=TF6l2YFWFo+kbP7Gy0fnvBNTSIxfbX96sVWoXGOpU3pzBqjOn0+IYYVrJKjmFFsJx 0vlS/i6CzML1vJ0F9jfoD7fH+VbntBEgA1iYYk7yvEBBRM+TKfNvDinfQtPbGDOw7J BaajsNDQZ4gpuIiSJbjyRsH6hEHMkFVsQiFtYy98j06YWPC2KXfeRlRI2xblqxn0sN PTvJC2fiqrXMbG+csZnUPifZx9zeE5bAiNhemSDU+3a3UPFnGwIBEZZlgHsgM4ykpt 2koX/8hnbIlGB06x2m2hI5KRGMml4z3cV6zURnHwW+uFPm6QeRP5SxTrwkFuEP1bKo Mz0fmuiO0ZMTQ== Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org PMC driver provides USB sleepwalk registers access to XUSB PADCTL driver. This commit adds a "nvidia,pmc" property which points to PMC node to XUSB PADCTL device node. Signed-off-by: JC Kuo --- arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi index 829f786af133..67c90a0ea32e 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi @@ -1040,6 +1040,7 @@ padctl: padctl@7009f000 { reg = <0x0 0x7009f000 0x0 0x1000>; resets = <&tegra_car 142>; reset-names = "padctl"; + nvidia,pmc = <&tegra_pmc>; status = "disabled"; From patchwork Fri Aug 28 01:10:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: JC Kuo X-Patchwork-Id: 253281 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8DFE9C433E7 for ; Fri, 28 Aug 2020 01:12:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 545C22080C for ; Fri, 28 Aug 2020 01:12:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="i3FIJ6yk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728114AbgH1BMT (ORCPT ); Thu, 27 Aug 2020 21:12:19 -0400 Received: from hqnvemgate26.nvidia.com ([216.228.121.65]:6751 "EHLO hqnvemgate26.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728352AbgH1BMO (ORCPT ); Thu, 27 Aug 2020 21:12:14 -0400 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate26.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 27 Aug 2020 18:11:59 -0700 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Thu, 27 Aug 2020 18:12:13 -0700 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Thu, 27 Aug 2020 18:12:13 -0700 Received: from HQMAIL107.nvidia.com (172.20.187.13) by HQMAIL111.nvidia.com (172.20.187.18) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 28 Aug 2020 01:12:12 +0000 Received: from rnnvemgw01.nvidia.com (10.128.109.123) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Fri, 28 Aug 2020 01:12:11 +0000 Received: from jckuo-lt.nvidia.com (Not Verified[10.19.101.4]) by rnnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 27 Aug 2020 18:12:11 -0700 From: JC Kuo To: , , , , CC: , , , , , JC Kuo Subject: [PATCH 08/12] phy: tegra: xusb: t210: support wake and sleepwalk Date: Fri, 28 Aug 2020 09:10:52 +0800 Message-ID: <20200828011056.1067559-9-jckuo@nvidia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200828011056.1067559-1-jckuo@nvidia.com> References: <20200828011056.1067559-1-jckuo@nvidia.com> MIME-Version: 1.0 X-NVConfidentiality: public DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1598577119; bh=mEfULxPcF6D320LeMby9oTHQfyo2DFMZ0KaGeJJqiBA=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:X-NVConfidentiality: Content-Transfer-Encoding:Content-Type; b=i3FIJ6ykNxHfAE8dLMDdLvHv2YpZXqrHMj9mdHb51kW31HW3reWFcnBB0UoxKoH8z UzXbnI6D80diHbWP5ZLo5BMcsHKNy0EpWGnESk79GKiSdg3I8HKq+p+CzoQ51AK4Zo mrg9WjqUrJYkr8RlPrzcqFHCJT3PXvUjYUtejqmUGn1rg7t7GLqq4td2fbWUbRfSA6 FP9qrDD+Qo6MrGjpV1vSgu6fUkzgK1JvEXmye8DkNx+EZ8qWWTmSrNyHsYEdUTRyge eJqN+bpH6NHpNzpWhkO5bQx2C8PbLTC2iQEz4yCm4n4T4m9MXQnoIKMCmGnRqqcp1Y UpPXFuxNs8Sxg== Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This commit implements Tegra210 XUSB PADCTL wake and sleepwalk routines. Sleepwalk logic is in PMC (always-on) hardware block. PMC driver provides managed access to the sleepwalk registers via regmap framework. Signed-off-by: JC Kuo --- drivers/phy/tegra/xusb-tegra210.c | 1094 ++++++++++++++++++++++++++++- 1 file changed, 1079 insertions(+), 15 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index fe1ab440424d..1c03f4ec4b59 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include @@ -52,6 +54,20 @@ #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5)) #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7 +#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20 +#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x)) +#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7) +#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14) +#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21) +#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28) +#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30) +#define ALL_WAKE_EVENTS ( \ + USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \ + USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \ + SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \ + SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \ + USB2_HSIC_PORT_WAKEUP_EVENT(0)) + #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) @@ -90,6 +106,8 @@ #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) +#define RPD_CTRL(x) (((x) & 0x1f) << 26) +#define RPD_CTRL_VALUE(x) (((x) >> 26) & 0x1f) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11) @@ -108,6 +126,8 @@ #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e +#define TCTRL_VALUE(x) (((x) & 0x3f) >> 0) +#define PCTRL_VALUE(x) (((x) >> 6) & 0x3f) #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20) #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18) @@ -251,16 +271,161 @@ #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0 +/* USB2 SLEEPWALK registers */ +#define UTMIP(_port, _offset1, _offset2) \ + (((_port) <= 2) ? (_offset1) : (_offset2)) + +#define PMC_UTMIP_UHSIC_SLEEP_CFG(x) UTMIP(x, 0x1fc, 0x4d0) +#define UTMIP_MASTER_ENABLE(x) UTMIP(x, BIT(8 * (x)), BIT(0)) +#define UTMIP_FSLS_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 1), \ + BIT(1)) +#define UTMIP_PCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 2), \ + BIT(2)) +#define UTMIP_TCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 3), \ + BIT(3)) +#define UTMIP_WAKE_VAL(_port, _value) (((_value) & 0xf) << \ + (UTMIP(_port, 8 * (_port) + 4, 4))) +#define UTMIP_WAKE_VAL_NONE(_port) UTMIP_WAKE_VAL(_port, 12) +#define UTMIP_WAKE_VAL_ANY(_port) UTMIP_WAKE_VAL(_port, 15) + +#define PMC_UTMIP_UHSIC_SLEEP_CFG1 (0x4d0) +#define UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x) BIT((x) + 8) +#define UTMIP_RPD_CTRL_USE_PMC_PX(x) BIT((x) + 16) + +#define PMC_UTMIP_MASTER_CONFIG (0x274) +#define UTMIP_PWR(x) UTMIP(x, BIT(x), BIT(4)) +#define UHSIC_PWR(x) BIT(3) + +#define PMC_USB_DEBOUNCE_DEL (0xec) +#define DEBOUNCE_VAL(x) (((x) & 0xffff) << 0) +#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16) +#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 20) + +#define PMC_UTMIP_UHSIC_FAKE(x) UTMIP(x, 0x218, 0x294) +#define UTMIP_FAKE_USBOP_VAL(x) UTMIP(x, BIT(4 * (x)), BIT(8)) +#define UTMIP_FAKE_USBON_VAL(x) UTMIP(x, BIT(4 * (x) + 1), \ + BIT(9)) +#define UTMIP_FAKE_USBOP_EN(x) UTMIP(x, BIT(4 * (x) + 2), \ + BIT(10)) +#define UTMIP_FAKE_USBON_EN(x) UTMIP(x, BIT(4 * (x) + 3), \ + BIT(11)) + +#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x) UTMIP(x, 0x200, 0x288) +#define UTMIP_LINEVAL_WALK_EN(x) UTMIP(x, BIT(8 * (x) + 7), \ + BIT(15)) + +#define PMC_USB_AO (0xf0) +#define USBOP_VAL_PD(x) UTMIP(x, BIT(4 * (x)), BIT(20)) +#define USBON_VAL_PD(x) UTMIP(x, BIT(4 * (x) + 1), \ + BIT(21)) +#define STROBE_VAL_PD(x) BIT(12) +#define DATA0_VAL_PD(x) BIT(13) +#define DATA1_VAL_PD BIT(24) + +#define PMC_UTMIP_UHSIC_SAVED_STATE(x) UTMIP(x, 0x1f0, 0x280) +#define SPEED(_port, _value) (((_value) & 0x3) << \ + (UTMIP(_port, 8 * (_port), 8))) +#define UTMI_HS(_port) SPEED(_port, 0) +#define UTMI_FS(_port) SPEED(_port, 1) +#define UTMI_LS(_port) SPEED(_port, 2) +#define UTMI_RST(_port) SPEED(_port, 3) + +#define PMC_UTMIP_UHSIC_TRIGGERS (0x1ec) +#define UTMIP_CLR_WALK_PTR(x) UTMIP(x, BIT(x), BIT(16)) +#define UTMIP_CAP_CFG(x) UTMIP(x, BIT((x) + 4), BIT(17)) +#define UTMIP_CLR_WAKE_ALARM(x) UTMIP(x, BIT((x) + 12), \ + BIT(19)) +#define UHSIC_CLR_WALK_PTR BIT(3) +#define UHSIC_CLR_WAKE_ALARM BIT(15) + +#define PMC_UTMIP_SLEEPWALK_PX(x) UTMIP(x, 0x204 + (4 * (x)), \ + 0x4e0) +/* phase A */ +#define UTMIP_USBOP_RPD_A BIT(0) +#define UTMIP_USBON_RPD_A BIT(1) +#define UTMIP_AP_A BIT(4) +#define UTMIP_AN_A BIT(5) +#define UTMIP_HIGHZ_A BIT(6) +/* phase B */ +#define UTMIP_USBOP_RPD_B BIT(8) +#define UTMIP_USBON_RPD_B BIT(9) +#define UTMIP_AP_B BIT(12) +#define UTMIP_AN_B BIT(13) +#define UTMIP_HIGHZ_B BIT(14) +/* phase C */ +#define UTMIP_USBOP_RPD_C BIT(16) +#define UTMIP_USBON_RPD_C BIT(17) +#define UTMIP_AP_C BIT(20) +#define UTMIP_AN_C BIT(21) +#define UTMIP_HIGHZ_C BIT(22) +/* phase D */ +#define UTMIP_USBOP_RPD_D BIT(24) +#define UTMIP_USBON_RPD_D BIT(25) +#define UTMIP_AP_D BIT(28) +#define UTMIP_AN_D BIT(29) +#define UTMIP_HIGHZ_D BIT(30) + +#define PMC_UTMIP_UHSIC_LINE_WAKEUP (0x26c) +#define UTMIP_LINE_WAKEUP_EN(x) UTMIP(x, BIT(x), BIT(4)) +#define UHSIC_LINE_WAKEUP_EN BIT(3) + +#define PMC_UTMIP_TERM_PAD_CFG (0x1f8) +#define PCTRL_VAL(x) (((x) & 0x3f) << 1) +#define TCTRL_VAL(x) (((x) & 0x3f) << 7) + +#define PMC_UTMIP_PAD_CFGX(x) (0x4c0 + (4 * (x))) +#define RPD_CTRL_PX(x) (((x) & 0x1f) << 22) + +#define PMC_UHSIC_SLEEP_CFG PMC_UTMIP_UHSIC_SLEEP_CFG(0) +#define UHSIC_MASTER_ENABLE BIT(24) +#define UHSIC_WAKE_VAL(_value) (((_value) & 0xf) << 28) +#define UHSIC_WAKE_VAL_SD10 UHSIC_WAKE_VAL(2) +#define UHSIC_WAKE_VAL_NONE UHSIC_WAKE_VAL(12) + +#define PMC_UHSIC_FAKE PMC_UTMIP_UHSIC_FAKE(0) +#define UHSIC_FAKE_STROBE_VAL BIT(12) +#define UHSIC_FAKE_DATA_VAL BIT(13) +#define UHSIC_FAKE_STROBE_EN BIT(14) +#define UHSIC_FAKE_DATA_EN BIT(15) + +#define PMC_UHSIC_SAVED_STATE PMC_UTMIP_UHSIC_SAVED_STATE(0) +#define UHSIC_MODE(_value) (((_value) & 0x1) << 24) +#define UHSIC_HS UHSIC_MODE(0) +#define UHSIC_RST UHSIC_MODE(1) + +#define PMC_UHSIC_SLEEPWALK_CFG PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0) +#define UHSIC_WAKE_WALK_EN BIT(30) +#define UHSIC_LINEVAL_WALK_EN BIT(31) + +#define PMC_UHSIC_SLEEPWALK_P0 (0x210) +#define UHSIC_DATA0_RPD_A BIT(1) +#define UHSIC_DATA0_RPU_B BIT(11) +#define UHSIC_DATA0_RPU_C BIT(19) +#define UHSIC_DATA0_RPU_D BIT(27) +#define UHSIC_STROBE_RPU_A BIT(2) +#define UHSIC_STROBE_RPD_B BIT(8) +#define UHSIC_STROBE_RPD_C BIT(16) +#define UHSIC_STROBE_RPD_D BIT(24) + struct tegra210_xusb_fuse_calibration { u32 hs_curr_level[4]; u32 hs_term_range_adj; u32 rpd_ctrl; }; +struct tegra210_xusb_padctl_context { + u32 usb2_pad_mux; + u32 usb2_port_cap; + u32 ss_port_map; + u32 usb3_pad_mux; +}; + struct tegra210_xusb_padctl { struct tegra_xusb_padctl base; struct tegra210_xusb_fuse_calibration fuse; + struct tegra210_xusb_padctl_context context; + struct regmap *pmc_reg; }; static inline struct tegra210_xusb_padctl * @@ -886,6 +1051,671 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, return 0; } +static int tegra210_usb3_enable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy enable sleepwalk usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_disable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy disable sleepwalk usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_enable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy enable wake usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKE_INTERRUPT_ENABLE(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_disable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy disable wake usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_utmi_enable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy enable wake on usb2 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_utmi_disable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy disable wake on usb2 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_hsic_enable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy enable wake on hsic %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_hsic_disable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy disable wake on hsic %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_phy_remote_wake_detected( + struct tegra_xusb_padctl *padctl, int port) +{ + u32 value; + + if (port < 0) { + dev_err(padctl->dev, "invalid usb3 port number %d\n", + port); + return false; + } + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(port)) && + (value & SS_PORT_WAKEUP_EVENT(port))) + return true; + else + return false; +} + +static int tegra210_utmi_phy_remote_wake_detected( + struct tegra_xusb_padctl *padctl, int port) +{ + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(port)) && + (value & USB2_PORT_WAKEUP_EVENT(port))) + return true; + else + return false; +} + +static int tegra210_hsic_phy_remote_wake_detected( + struct tegra_xusb_padctl *padctl, int port) +{ + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(port)) && + (value & USB2_HSIC_PORT_WAKEUP_EVENT(port))) + return true; + else + return false; +} + +#define padctl_pmc_readl(_priv, _offset) \ +({ \ + int rc; \ + u32 val; \ + rc = regmap_read(_priv->pmc_reg, _offset, &val); \ + if (rc) \ + return rc; \ + val; \ +}) + +#define padctl_pmc_writel(_priv, _val, _offset) \ +do { \ + int rc; \ + rc = regmap_write(_priv->pmc_reg, _offset, _val); \ + if (rc) \ + return rc; \ +} while (0) + +/* T210 USB2 SLEEPWALK APIs */ +int tegra_pmc_utmi_enable_phy_sleepwalk(struct phy *phy, + enum usb_device_speed speed) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 val, tctrl, pctrl, rpd_ctrl; + + if (speed > USB_SPEED_HIGH) + return -EINVAL; + + dev_dbg(dev, "phy enable sleepwalk usb2 %d speed %d\n", port, speed); + + val = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + tctrl = TCTRL_VALUE(val); + pctrl = PCTRL_VALUE(val); + + val = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); + rpd_ctrl = RPD_CTRL_VALUE(val); + + /* ensure sleepwalk logic is disabled */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val &= ~UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* ensure sleepwalk logics are in low power mode */ + val = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + val |= UTMIP_PWR(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + val = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + val &= ~UTMIP_LINE_DEB_CNT(~0); + val |= UTMIP_LINE_DEB_CNT(0x1); + padctl_pmc_writel(priv, val, PMC_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port)); + val &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) | + UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_FAKE(port)); + + /* ensure wake events of sleepwalk logic are not latched */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + val &= ~UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val &= ~UTMIP_WAKE_VAL(port, ~0); + val |= UTMIP_WAKE_VAL_NONE(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* power down the line state detectors of the pad */ + val = padctl_pmc_readl(priv, PMC_USB_AO); + val |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, val, PMC_USB_AO); + + /* save state per speed */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + val &= ~SPEED(port, ~0); + if (speed == USB_SPEED_HIGH) + val |= UTMI_HS(port); + else if (speed == USB_SPEED_FULL) + val |= UTMI_FS(port); + else if (speed == USB_SPEED_LOW) + val |= UTMI_LS(port); + else + val |= UTMI_RST(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + + /* enable the trigger of the sleepwalk logic */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + val |= UTMIP_LINEVAL_WALK_EN(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + + /* reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 pad + */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + val |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | + UTMIP_CAP_CFG(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_TRIGGERS); + + /* program electrical parameters read from XUSB PADCTL */ + val = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG); + val &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0)); + val |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl)); + padctl_pmc_writel(priv, val, PMC_UTMIP_TERM_PAD_CFG); + + val = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port)); + val &= ~RPD_CTRL_PX(~0); + val |= RPD_CTRL_PX(rpd_ctrl); + padctl_pmc_writel(priv, val, PMC_UTMIP_PAD_CFGX(port)); + + /* setup the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. + * if device is connected, program sleepwalk logic to maintain a J and + * keep driving K upon seeing remote wake. + */ + val = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port)); + val = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | + UTMIP_USBOP_RPD_D); + val |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | + UTMIP_USBON_RPD_D); + if (speed == USB_SPEED_UNKNOWN) { + val |= (UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | + UTMIP_HIGHZ_D); + } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) { + /* J state: D+/D- = high/low, K state: D+/D- = low/high */ + val |= UTMIP_HIGHZ_A; + val |= UTMIP_AP_A; + val |= (UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D); + } else if (speed == USB_SPEED_LOW) { + /* J state: D+/D- = low/high, K state: D+/D- = high/low */ + val |= UTMIP_HIGHZ_A; + val |= UTMIP_AN_A; + val |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D); + } + padctl_pmc_writel(priv, val, PMC_UTMIP_SLEEPWALK_PX(port)); + + /* power up the line state detectors of the pad */ + val = padctl_pmc_readl(priv, PMC_USB_AO); + val &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, val, PMC_USB_AO); + + usleep_range(50, 100); + + /* switch the electric control of the USB2.0 pad to PMC */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val |= (UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | + UTMIP_TCTRL_USE_PMC(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); + val |= (UTMIP_RPD_CTRL_USE_PMC_PX(port) | + UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG1); + + /* set the wake signaling trigger events */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val &= ~UTMIP_WAKE_VAL(port, ~0); + val |= UTMIP_WAKE_VAL_ANY(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* enable the wake detection */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val |= UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + val |= UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + return 0; +} + +int tegra_pmc_utmi_disable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 value; + + dev_dbg(dev, "phy disable sleepwalk usb2 %d\n", port); + + /* disable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* switch the electric control of the USB2.0 pad to XUSB or USB2 */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | + UTMIP_TCTRL_USE_PMC(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); + value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) | + UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_WAKE_VAL(port, ~0); + value |= UTMIP_WAKE_VAL_NONE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* clear alarm of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UTMIP_CLR_WAKE_ALARM(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + return 0; +} + +int tegra_pmc_hsic_enable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 value; + + dev_dbg(dev, "phy enable sleepwalk hsic %d\n", port); + + /* ensure sleepwalk logic is disabled */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* ensure sleepwalk logics are in low power mode */ + value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + value |= UHSIC_PWR(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + value &= ~UHSIC_LINE_DEB_CNT(~0); + value |= UHSIC_LINE_DEB_CNT(0x1); + padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + value = padctl_pmc_readl(priv, PMC_UHSIC_FAKE); + value &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL | + UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN); + padctl_pmc_writel(priv, value, PMC_UHSIC_FAKE); + + /* ensure wake events of sleepwalk logic are not latched */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_NONE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* save state, HSIC always comes up as HS */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SAVED_STATE); + value &= ~UHSIC_MODE(~0); + value |= UHSIC_HS; + padctl_pmc_writel(priv, value, PMC_UHSIC_SAVED_STATE); + + /* enable the trigger of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_CFG); + value |= (UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN); + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_CFG); + + /* reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 port + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= (UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + /* setup the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. + * maintain a HSIC IDLE and keep driving HSIC RESUME upon remote wake + */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_P0); + value = (UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C | + UHSIC_DATA0_RPU_D); + value |= (UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C | + UHSIC_STROBE_RPD_D); + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_P0); + + /* power up the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value &= ~(STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + usleep_range(50, 100); + + /* set the wake signaling trigger events */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_SD10; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* enable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value |= UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value |= UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + return 0; +} + +int tegra_pmc_hsic_disable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 value; + + dev_dbg(dev, "phy disable sleepwalk hsic %d\n", port); + + /* disable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_NONE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* clear alarm of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UHSIC_CLR_WAKE_ALARM; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + return 0; +} + static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int index, bool enable) { @@ -988,8 +1818,23 @@ static int tegra210_usb2_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct tegra_xusb_usb2_port *port; + int err; u32 value; + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + err = regulator_enable(port->supply); + if (err) + return err; + + mutex_lock(&padctl->lock); + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK << XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT); @@ -997,11 +1842,29 @@ static int tegra210_usb2_phy_init(struct phy *phy) XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT; padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); + mutex_unlock(&padctl->lock); + return 0; } static int tegra210_usb2_phy_exit(struct phy *phy) { + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct tegra_xusb_usb2_port *port; + int err; + + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + err = regulator_disable(port->supply); + if (err) + return err; + return 0; } @@ -1122,6 +1985,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) priv = to_tegra210_xusb_padctl(padctl); + mutex_lock(&padctl->lock); + if (port->usb3_port_fake != -1) { value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( @@ -1215,14 +2080,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); - if (port->supply && port->mode == USB_DR_MODE_HOST) { - err = regulator_enable(port->supply); - if (err) - return err; - } - - mutex_lock(&padctl->lock); - if (pad->enable > 0) { pad->enable++; mutex_unlock(&padctl->lock); @@ -1231,7 +2088,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) err = clk_prepare_enable(pad->clk); if (err) - goto disable_regulator; + goto out; value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << @@ -1263,8 +2120,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) return 0; -disable_regulator: - regulator_disable(port->supply); +out: mutex_unlock(&padctl->lock); return err; } @@ -1275,12 +2131,12 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra_xusb_usb2_port *port; + unsigned int index = lane->index; u32 value; - port = tegra_xusb_find_usb2_port(padctl, lane->index); + port = tegra_xusb_find_usb2_port(padctl, index); if (!port) { - dev_err(&phy->dev, "no port found for USB2 lane %u\n", - lane->index); + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); return -ENODEV; } @@ -1318,12 +2174,19 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) if (--pad->enable > 0) goto out; + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); out: - regulator_disable(port->supply); mutex_unlock(&padctl->lock); return 0; } @@ -2120,6 +2983,120 @@ static const struct phy_ops tegra210_sata_phy_ops = { .owner = THIS_MODULE, }; +static inline bool is_usb3_phy(struct phy *phy) +{ + return (phy->ops == &tegra210_pcie_phy_ops || + phy->ops == &tegra210_sata_phy_ops); +} + +static inline bool is_hsic_phy(struct phy *phy) +{ + return phy->ops == &tegra210_hsic_phy_ops; +} + +static inline bool is_utmi_phy(struct phy *phy) +{ + return phy->ops == &tegra210_usb2_phy_ops; +} + +static int +tegra210_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_enable_phy_wake(phy); + else if (is_utmi_phy(phy)) + return tegra210_utmi_enable_phy_wake(phy); + else if (is_hsic_phy(phy)) + return tegra210_hsic_enable_phy_wake(phy); + else + return -EINVAL; +} + +static int +tegra210_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_disable_phy_wake(phy); + else if (is_utmi_phy(phy)) + return tegra210_utmi_disable_phy_wake(phy); + else if (is_hsic_phy(phy)) + return tegra210_hsic_disable_phy_wake(phy); + else + return -EINVAL; +} + +int tegra210_xusb_padctl_remote_wake_detected(struct phy *phy) +{ + struct tegra_xusb_lane *lane; + struct tegra_xusb_padctl *padctl; + + if (!phy) + return 0; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; + + if (is_utmi_phy(phy)) + return tegra210_utmi_phy_remote_wake_detected(padctl, + lane->index); + else if (is_hsic_phy(phy)) + return tegra210_hsic_phy_remote_wake_detected(padctl, + lane->index); + else if (is_usb3_phy(phy)) + return tegra210_usb3_phy_remote_wake_detected(padctl, + tegra210_usb3_lane_map(lane)); + + return -EINVAL; +} + +static int +tegra210_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy, + enum usb_device_speed speed) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_enable_phy_sleepwalk(phy); + else if (is_utmi_phy(phy)) + return tegra_pmc_utmi_enable_phy_sleepwalk(phy, speed); + else if (is_hsic_phy(phy)) + return tegra_pmc_hsic_enable_phy_sleepwalk(phy); + else + return -EINVAL; + + return 0; +} + +static int +tegra210_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_disable_phy_sleepwalk(phy); + else if (is_utmi_phy(phy)) + return tegra_pmc_utmi_disable_phy_sleepwalk(phy); + else if (is_hsic_phy(phy)) + return tegra_pmc_hsic_disable_phy_sleepwalk(phy); + else + return -EINVAL; + + return 0; +} + + static struct tegra_xusb_pad * tegra210_sata_pad_probe(struct tegra_xusb_padctl *padctl, const struct tegra_xusb_pad_soc *soc, @@ -2317,6 +3294,8 @@ tegra210_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { struct tegra210_xusb_padctl *padctl; + struct device_node *node, *np = dev->of_node; + struct platform_device *pmc_dev; int err; padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); @@ -2330,6 +3309,24 @@ tegra210_xusb_padctl_probe(struct device *dev, if (err < 0) return ERR_PTR(err); + node = of_parse_phandle(np, "nvidia,pmc", 0); + if (!node) { + dev_err(dev, "nvidia,pmc property is missing\n"); + return ERR_PTR(-ENODEV); + } + + pmc_dev = of_find_device_by_node(node); + if (!pmc_dev) { + dev_err(dev, "pmc device is not available\n"); + return ERR_PTR(-ENODEV); + } + + padctl->pmc_reg = dev_get_regmap(&pmc_dev->dev, "usb_sleepwalk"); + if (!padctl->pmc_reg) { + dev_err(dev, "pmc regmap is not available.\n"); + return ERR_PTR(-ENODEV); + } + return &padctl->base; } @@ -2337,13 +3334,80 @@ static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } +static void tegra210_xusb_padctl_save(struct tegra_xusb_padctl *padctl) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + + priv->context.usb2_pad_mux = + padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); + priv->context.usb2_port_cap = + padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); + priv->context.ss_port_map = + padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + priv->context.usb3_pad_mux = + padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); +} + +static void tegra210_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + int i; + + padctl_writel(padctl, priv->context.usb2_pad_mux, + XUSB_PADCTL_USB2_PAD_MUX); + padctl_writel(padctl, priv->context.usb2_port_cap, + XUSB_PADCTL_USB2_PORT_CAP); + padctl_writel(padctl, priv->context.ss_port_map, + XUSB_PADCTL_SS_PORT_MAP); + + for (i = 0; i <= 7; i ++) + tegra210_uphy_lane_iddq_enable(padctl, i); + + padctl_writel(padctl, priv->context.usb3_pad_mux, + XUSB_PADCTL_USB3_PAD_MUX); + + for (i = 0; i <= 7; i ++) + tegra210_uphy_lane_iddq_disable(padctl, i); +} + +static int tegra210_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + + tegra210_uphy_deinit(padctl); + + tegra210_xusb_padctl_save(padctl); + + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra210_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + + tegra210_xusb_padctl_restore(padctl); + + tegra210_uphy_init(padctl); + + mutex_unlock(&padctl->lock); + return 0; +} + static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { .probe = tegra210_xusb_padctl_probe, .remove = tegra210_xusb_padctl_remove, + .suspend_noirq = tegra210_xusb_padctl_suspend_noirq, + .resume_noirq = tegra210_xusb_padctl_resume_noirq, .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, .hsic_set_idle = tegra210_hsic_set_idle, .vbus_override = tegra210_xusb_padctl_vbus_override, .utmi_port_reset = tegra210_utmi_port_reset, + .enable_phy_sleepwalk = tegra210_xusb_padctl_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_xusb_padctl_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_xusb_padctl_enable_phy_wake, + .disable_phy_wake = tegra210_xusb_padctl_disable_phy_wake, + .remote_wake_detected = tegra210_xusb_padctl_remote_wake_detected, }; static const char * const tegra210_xusb_padctl_supply_names[] = { From patchwork Fri Aug 28 01:10:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: JC Kuo X-Patchwork-Id: 253280 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CE235C433E6 for ; Fri, 28 Aug 2020 01:13:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AD7E42080C for ; Fri, 28 Aug 2020 01:13:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="PDsM7ei1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728453AbgH1BMw (ORCPT ); Thu, 27 Aug 2020 21:12:52 -0400 Received: from hqnvemgate26.nvidia.com ([216.228.121.65]:6772 "EHLO hqnvemgate26.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728251AbgH1BMS (ORCPT ); Thu, 27 Aug 2020 21:12:18 -0400 Received: from hqpgpgate102.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate26.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 27 Aug 2020 18:12:04 -0700 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate102.nvidia.com (PGP Universal service); Thu, 27 Aug 2020 18:12:18 -0700 X-PGP-Universal: processed; by hqpgpgate102.nvidia.com on Thu, 27 Aug 2020 18:12:18 -0700 Received: from HQMAIL109.nvidia.com (172.20.187.15) by HQMAIL105.nvidia.com (172.20.187.12) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 28 Aug 2020 01:12:16 +0000 Received: from rnnvemgw01.nvidia.com (10.128.109.123) by HQMAIL109.nvidia.com (172.20.187.15) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Fri, 28 Aug 2020 01:12:16 +0000 Received: from jckuo-lt.nvidia.com (Not Verified[10.19.101.4]) by rnnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 27 Aug 2020 18:12:15 -0700 From: JC Kuo To: , , , , CC: , , , , , JC Kuo Subject: [PATCH 10/12] arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq Date: Fri, 28 Aug 2020 09:10:54 +0800 Message-ID: <20200828011056.1067559-11-jckuo@nvidia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200828011056.1067559-1-jckuo@nvidia.com> References: <20200828011056.1067559-1-jckuo@nvidia.com> MIME-Version: 1.0 X-NVConfidentiality: public DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1598577124; bh=5xHfZPttdwW7Er6d1zUMfgP3rWvN2dNH9BKH/oThNWo=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:X-NVConfidentiality: Content-Transfer-Encoding:Content-Type; b=PDsM7ei1l8uxxgf63WsYrPojptM5wjoH9ZWvjpUukakuZIXuE8wNWnBbhv/l8GJ1j yWlhR8VOX55VNXLxCSJzuSfbqTpL9Al0WlguVcJdg7yHbhEhvzop4pPZEHpSVVjN2N YI5562sjKcv0oMvZHSQLRdLqnFLjTVfGUIns8251u3t8ywixjS/F4N00/oP1JbwBCB zoRXDFOk3ZX5lGqy+x8veQ29It7mgIi7OYVTsiq4xHVQnHpV8BmnBq69z1Z9cQPRip 4UkWj3IbO+bbKE0gQ8uQo5ygRW4x0D/5i46cYEQEHZd5q48SduYo4lIFim6UrSqkL+ PvbbUipAxx4Eg== Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This commit adds "interrupts" property to Tegra210/Tegra186/Tegra194 XUSB PADCTL node. XUSB PADCTL interrupt will be raised when USB wake event happens. This is required for supporting XUSB host controller ELPG. Signed-off-by: JC Kuo --- arch/arm64/boot/dts/nvidia/tegra186.dtsi | 1 + arch/arm64/boot/dts/nvidia/tegra194.dtsi | 1 + arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi index 34d249d85da7..454a857e5e56 100644 --- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi @@ -466,6 +466,7 @@ padctl: padctl@3520000 { reg = <0x0 0x03520000 0x0 0x1000>, <0x0 0x03540000 0x0 0x1000>; reg-names = "padctl", "ao"; + interrupts = ; resets = <&bpmp TEGRA186_RESET_XUSB_PADCTL>; reset-names = "padctl"; diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index 48160f48003a..2b5216a34c06 100644 --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi @@ -561,6 +561,7 @@ xusb_padctl: padctl@3520000 { reg = <0x03520000 0x1000>, <0x03540000 0x1000>; reg-names = "padctl", "ao"; + interrupts = ; resets = <&bpmp TEGRA194_RESET_XUSB_PADCTL>; reset-names = "padctl"; diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi index 67c90a0ea32e..2ba526779ee5 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi @@ -1041,6 +1041,7 @@ padctl: padctl@7009f000 { resets = <&tegra_car 142>; reset-names = "padctl"; nvidia,pmc = <&tegra_pmc>; + interrupts = ; status = "disabled"; From patchwork Fri Aug 28 01:10:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: JC Kuo X-Patchwork-Id: 253282 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1F0E8C433E2 for ; Fri, 28 Aug 2020 01:12:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EE94B20848 for ; Fri, 28 Aug 2020 01:12:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="MKmC9O3F" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728396AbgH1BMZ (ORCPT ); Thu, 27 Aug 2020 21:12:25 -0400 Received: from hqnvemgate25.nvidia.com ([216.228.121.64]:15038 "EHLO hqnvemgate25.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728387AbgH1BMY (ORCPT ); Thu, 27 Aug 2020 21:12:24 -0400 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate25.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 27 Aug 2020 18:11:40 -0700 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Thu, 27 Aug 2020 18:12:23 -0700 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Thu, 27 Aug 2020 18:12:23 -0700 Received: from HQMAIL101.nvidia.com (172.20.187.10) by HQMAIL101.nvidia.com (172.20.187.10) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 28 Aug 2020 01:12:18 +0000 Received: from rnnvemgw01.nvidia.com (10.128.109.123) by HQMAIL101.nvidia.com (172.20.187.10) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Fri, 28 Aug 2020 01:12:18 +0000 Received: from jckuo-lt.nvidia.com (Not Verified[10.19.101.4]) by rnnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 27 Aug 2020 18:12:18 -0700 From: JC Kuo To: , , , , CC: , , , , , JC Kuo Subject: [PATCH 11/12] usb: host: xhci-tegra: unlink power domain devices Date: Fri, 28 Aug 2020 09:10:55 +0800 Message-ID: <20200828011056.1067559-12-jckuo@nvidia.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200828011056.1067559-1-jckuo@nvidia.com> References: <20200828011056.1067559-1-jckuo@nvidia.com> MIME-Version: 1.0 X-NVConfidentiality: public DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1598577100; bh=0CTHi9mIhl9G6jvo2lJZ946QRa9r6dmOL3TNK2XctPQ=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:X-NVConfidentiality: Content-Transfer-Encoding:Content-Type; b=MKmC9O3FChYVa7LT+0dg7kXTTrJhTd6eBGT7RBJaAj6FiCp+bS9BknFEKyBOuU23r F2+WxBR7+bvysDNF+2ZwDpg3C3AD2DQ5EbIYWB4pRFKNr0jG1g1X6Dq2bNwIJp9EwW DkO/zeaqsXWuv3K50kFBIvNo2e1cOhMpGHMtTXaa36d91zSMNpfn9JNylHxU5WGW+b OzVctFRE9KvNlZkwrhd70ruOjRH2axrUt1Hiry/93i4KiiHXLvtroK+W/uiYkW6TcL 3IJqjkHiY1z0zlCKUfQftCAVw2f0Ob+ZKVlWql7VfJyP7KzuVilH6qRZX6gFflQeLv FPAy0UxQoXgzQ== Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This commit unlinks xhci-tegra platform device with ss/host power domain devices. Reasons for this change is - at elpg entry, phy sleepwalk and wake configuration need to be done before powering down ss/host partitions, and phy need be powered off after powering down ss/host partitions. Sequence looks like roughly below: tegra_xusb_enter_elpg() -> xhci_suspend() -> enable phy sleepwalk and wake if needed -> power down ss/host partitions -> power down phy If ss/host power domains are linked to xhci-tegra platform device, we are not able to perform the sequence like above. This commit introduces: 1. tegra_xusb_unpowergate_partitions() to power up ss and host partitions together. If ss/host power domain devices are available, it invokes pm_runtime_get_sync() to request power driver to power up partitions; If power domain devices are not available, tegra_powergate_sequence_power_up() will be used to power up partitions. 2. tegra_xusb_powergate_partitions() to power down ss and host partitions together. If ss/host power domain devices are available, it invokes pm_runtime_put_sync() to request power driver to power down partitions; If power domain devices are not available, tegra_powergate_power_off() will be used to power down partitions. Signed-off-by: JC Kuo --- drivers/usb/host/xhci-tegra.c | 202 +++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 91 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 934be1686352..ce6526c2caf6 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -249,8 +249,6 @@ struct tegra_xusb { struct device *genpd_dev_host; struct device *genpd_dev_ss; - struct device_link *genpd_dl_host; - struct device_link *genpd_dl_ss; struct phy **phys; unsigned int num_phys; @@ -814,36 +812,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra) static int tegra_xusb_runtime_suspend(struct device *dev) { - struct tegra_xusb *tegra = dev_get_drvdata(dev); - - regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); - tegra_xusb_clk_disable(tegra); - return 0; } static int tegra_xusb_runtime_resume(struct device *dev) { - struct tegra_xusb *tegra = dev_get_drvdata(dev); - int err; - - err = tegra_xusb_clk_enable(tegra); - if (err) { - dev_err(dev, "failed to enable clocks: %d\n", err); - return err; - } - - err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies); - if (err) { - dev_err(dev, "failed to enable regulators: %d\n", err); - goto disable_clk; - } - return 0; - -disable_clk: - tegra_xusb_clk_disable(tegra); - return err; } #ifdef CONFIG_PM_SLEEP @@ -1019,10 +993,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) static void tegra_xusb_powerdomain_remove(struct device *dev, struct tegra_xusb *tegra) { - if (tegra->genpd_dl_ss) - device_link_del(tegra->genpd_dl_ss); - if (tegra->genpd_dl_host) - device_link_del(tegra->genpd_dl_host); if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss)) dev_pm_domain_detach(tegra->genpd_dev_ss, true); if (!IS_ERR_OR_NULL(tegra->genpd_dev_host)) @@ -1048,20 +1018,88 @@ static int tegra_xusb_powerdomain_init(struct device *dev, return err; } - tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host, - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS); - if (!tegra->genpd_dl_host) { - dev_err(dev, "adding host device link failed!\n"); - return -ENODEV; + return 0; +} + +static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra) +{ + struct device *dev = tegra->dev; + bool use_genpd; + int rc; + + use_genpd = of_property_read_bool(dev->of_node, "power-domains"); + + if (use_genpd) { + rc = pm_runtime_get_sync(tegra->genpd_dev_ss); + if (rc < 0) { + dev_err(dev, "failed to enable XUSB SS partition\n"); + return rc; + } + + rc = pm_runtime_get_sync(tegra->genpd_dev_host); + if (rc < 0) { + dev_err(dev, "failed to enable XUSB Host partition\n"); + pm_runtime_put_sync(tegra->genpd_dev_ss); + return rc; + } + } else { + rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA, + tegra->ss_clk, + tegra->ss_rst); + if (rc < 0) { + dev_err(dev, "failed to enable XUSB SS partition\n"); + return rc; + } + + rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, + tegra->host_clk, + tegra->host_rst); + if (rc < 0) { + dev_err(dev, "failed to enable XUSB Host partition\n"); + tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); + return rc; + } } - tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss, - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS); - if (!tegra->genpd_dl_ss) { - dev_err(dev, "adding superspeed device link failed!\n"); - return -ENODEV; + return 0; +} + +static int tegra_xusb_powergate_partitions(struct tegra_xusb *tegra) +{ + struct device *dev = tegra->dev; + bool use_genpd; + int rc; + + use_genpd = of_property_read_bool(dev->of_node, "power-domains"); + + if (use_genpd) { + rc = pm_runtime_put_sync(tegra->genpd_dev_host); + if (rc < 0) { + dev_err(dev, "failed to disable XUSB Host partition\n"); + return rc; + } + + rc = pm_runtime_put_sync(tegra->genpd_dev_ss); + if (rc < 0) { + dev_err(dev, "failed to disable XUSB SS partition\n"); + pm_runtime_get_sync(tegra->genpd_dev_host); + return rc; + } + } else { + rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); + if (rc < 0) { + dev_err(dev, "failed to disable XUSB Host partition\n"); + return rc; + } + + rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); + if (rc < 0) { + dev_err(dev, "failed to disable XUSB SS partition\n"); + tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, + tegra->host_clk, + tegra->host_rst); + return rc; + } } return 0; @@ -1425,25 +1463,6 @@ static int tegra_xusb_probe(struct platform_device *pdev) err); goto put_padctl; } - - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA, - tegra->ss_clk, - tegra->ss_rst); - if (err) { - dev_err(&pdev->dev, - "failed to enable XUSBA domain: %d\n", err); - goto put_padctl; - } - - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, - tegra->host_clk, - tegra->host_rst); - if (err) { - tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); - dev_err(&pdev->dev, - "failed to enable XUSBC domain: %d\n", err); - goto put_padctl; - } } else { err = tegra_xusb_powerdomain_init(&pdev->dev, tegra); if (err) @@ -1518,10 +1537,22 @@ static int tegra_xusb_probe(struct platform_device *pdev) */ platform_set_drvdata(pdev, tegra); + err = tegra_xusb_clk_enable(tegra); + if (err) { + dev_err(tegra->dev, "failed to enable clocks: %d\n", err); + goto put_hcd; + } + + err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies); + if (err) { + dev_err(tegra->dev, "failed to enable regulators: %d\n", err); + goto disable_clk; + } + err = tegra_xusb_phy_enable(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err); - goto put_hcd; + goto disable_regulator; } /* @@ -1540,30 +1571,22 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto disable_phy; } - pm_runtime_enable(&pdev->dev); - - if (!pm_runtime_enabled(&pdev->dev)) - err = tegra_xusb_runtime_resume(&pdev->dev); - else - err = pm_runtime_get_sync(&pdev->dev); - - if (err < 0) { - dev_err(&pdev->dev, "failed to enable device: %d\n", err); + err = tegra_xusb_unpowergate_partitions(tegra); + if (err) goto free_firmware; - } tegra_xusb_config(tegra); err = tegra_xusb_load_firmware(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to load firmware: %d\n", err); - goto put_rpm; + goto powergate; } err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED); if (err < 0) { dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err); - goto put_rpm; + goto powergate; } device_wakeup_enable(tegra->hcd->self.controller); @@ -1615,24 +1638,21 @@ static int tegra_xusb_probe(struct platform_device *pdev) usb_put_hcd(xhci->shared_hcd); remove_usb2: usb_remove_hcd(tegra->hcd); -put_rpm: - if (!pm_runtime_status_suspended(&pdev->dev)) - tegra_xusb_runtime_suspend(&pdev->dev); -put_hcd: - usb_put_hcd(tegra->hcd); +powergate: + tegra_xusb_powergate_partitions(tegra); free_firmware: dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt, tegra->fw.phys); disable_phy: tegra_xusb_phy_disable(tegra); - pm_runtime_disable(&pdev->dev); +disable_regulator: + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); +disable_clk: + tegra_xusb_clk_disable(tegra); +put_hcd: + usb_put_hcd(tegra->hcd); put_powerdomains: - if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { - tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); - tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); - } else { - tegra_xusb_powerdomain_remove(&pdev->dev, tegra); - } + tegra_xusb_powerdomain_remove(&pdev->dev, tegra); put_padctl: tegra_xusb_padctl_put(tegra->padctl); return err; @@ -1657,15 +1677,15 @@ static int tegra_xusb_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { - tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); - tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); - } else { + tegra_xusb_powergate_partitions(tegra); + + if (of_property_read_bool(pdev->dev.of_node, "power-domains")) { tegra_xusb_powerdomain_remove(&pdev->dev, tegra); } tegra_xusb_phy_disable(tegra); - + tegra_xusb_clk_disable(tegra); + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); tegra_xusb_padctl_put(tegra->padctl); return 0;