From patchwork Thu Mar 25 16:40:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164573 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 EE729C433E0 for ; Thu, 25 Mar 2021 16:40:53 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7A48A61A25 for ; Thu, 25 Mar 2021 16:40:53 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7A48A61A25 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=LHYxpKK/pt60TCYugofTlSPvd8fr7+qumFWFQd8r6f8=; b=KlGR6d0t8MEy6Wp/vBzbVGyXO /7LR2ufgRRRopD/x0vz+SsWH9Zk0VRXT/PvOclndVIHGrMS+WGaA4nLtDZuZpAKcElSKeU3FF5flp ReQzYvMFM1zoh6DrdLrioK/12f8GljxCorOkQGlFqYYFuWwVAZUEyG4WLHsmeYBL4FSyNyws4wk37 6NjHaDv/6IO0UTlWrA4KXs4NpccT6WlUsfBEfVBGOAbACZTxZYBNKQSN47frVTtWChhXUdLHfhy7t xl9v1arj+KSULlSeIo19x4vUAUSz08gGq5k38GZJy/Gc4ZZ13k1BtENkrW/qfrN9ckqWB5Szy3WPk 5120sE8yA==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2R-001qJf-Pa; Thu, 25 Mar 2021 16:40:51 +0000 Received: from mail-wr1-x432.google.com ([2a00:1450:4864:20::432]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2O-001qIz-8B for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:40:50 +0000 Received: by mail-wr1-x432.google.com with SMTP id j7so2982053wrd.1 for ; Thu, 25 Mar 2021 09:40:48 -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 :mime-version:content-transfer-encoding; bh=BIOzYjpuWV2ikVARWL5rnMXlYplC4fVIyiluPMIE1oE=; b=T+5lmaYU0760xN0cgcjj5c8+SDzlH7A/atUQUf4fLVOx2WE54T7z8ZItEJZl86LVVR o0FPEerykYnJ0HBljGhYcwKI4YJepfTiet/oep+mOhuOJOvgJyonZP+cHnoTSfmLfYWV lRPTLweJBT7lz99SB3JACg4kCo0ZbWBiI2jLX8C6u9fzHoCJy4GGRANnNF+O3SvYEXDB UYAzmpR8wcGZc4F+05TL3vSTLf+98outM3Z9NS+G69NKHOAEx158eCryef57QwYZiKpz 5/PU5edUocmXm1scX0khV7VMSg2OVn7VZPp69hyzYYEEZAF5mdyx36Lx04en2FUyCY5r U6YQ== 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:mime-version:content-transfer-encoding; bh=BIOzYjpuWV2ikVARWL5rnMXlYplC4fVIyiluPMIE1oE=; b=cIMl+RD0ar3r2+E1J6I8GxrYrB7cLRhihJNDg8ob70MNu54lbzruNZXZO48ET8vkjq Qbp1nV/0ypG8vC5vcP+4U/d5Sk+yGpHXeckiZqwmkAlUWpFo7tSVGFQq470buXN9q/Xu VBi2AYDh39Ylr63TDULZrroIq2+Qay/9KFSo/woUfYDlC1wYMnFE5jTTZ+yHs0HaP/7t fyFVr1+M9XclA2RJbfMiUSnGMOo/DsateSXoYn595d92XuDiSs3gNRCEQzhSj3vgnwKi t5nkjOuVan66li1eep2O8L6OtMTOgx1tQ4cYUuZ7xYSd+23Icul5f2YTfQSpyhIFxv5T j45Q== X-Gm-Message-State: AOAM531Yn+Zpmm/PKsFLTuMqediwvHvaEW5KJCnXauq2SkkrbacqcJSR qCg8OYQG9z5wfF3zqnHhSHU= X-Google-Smtp-Source: ABdhPJyZAKo+sD+iopvWd3BFRQ8xlnIOc5+qTBvaeiQHoQwgOF2llYd0lTLSDaZWT58a8VW8MaFZOw== X-Received: by 2002:adf:9261:: with SMTP id 88mr9676623wrj.270.1616690447651; Thu, 25 Mar 2021 09:40:47 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id j16sm11847179wmi.2.2021.03.25.09.40.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:40:45 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org, Stephen Boyd Subject: [PATCH v8 01/13] clk: tegra: Add PLLE HW power sequencer control Date: Thu, 25 Mar 2021 17:40:45 +0100 Message-Id: <20210325164057.793954-2-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164048_416688_697EB27D X-CRM114-Status: GOOD ( 15.45 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo PLLE has a hardware power sequencer logic which is a state machine that can power on/off PLLE without any software intervention. The sequencer has two inputs, one from XUSB UPHY PLL and the other from SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY PLLs. When both of the downstream PLLs are powered-off, PLLE hardware power sequencer will automatically power off PLLE for power saving. XUSB and SATA UPHY PLLs also have their own hardware power sequencer logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE controllers. The XUSB UPHY PLL hardware power sequencer has inputs from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE controllers are in low power state, XUSB UPHY PLL hardware power sequencer automatically power off PLL and flags idle to PLLE hardware power sequencer. Similar applies to SATA UPHY PLL. PLLE hardware power sequencer has to be enabled after both downstream sequencers are enabled. This commit adds two helper functions: 1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable PLLE hardware sequencer at proper time. 2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to check whether PLLE hardware sequencer has been enabled or not. Signed-off-by: JC Kuo Acked-by: Stephen Boyd Signed-off-by: Thierry Reding --- drivers/clk/tegra/clk-tegra210.c | 53 +++++++++++++++++++++++++++++++- include/linux/clk/tegra.h | 4 ++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 68cbb98af567..b9099012dc7b 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2012-2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2020 NVIDIA CORPORATION. All rights reserved. */ #include @@ -403,6 +403,14 @@ static unsigned long tegra210_input_freq[] = { #define PLLRE_BASE_DEFAULT_MASK 0x1c000000 #define PLLRE_MISC0_WRITE_MASK 0x67ffffff +/* PLLE */ +#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14) +#define PLLE_AUX_USE_LOCKDET (1 << 3) +#define PLLE_AUX_SS_SEQ_INCLUDE (1 << 31) +#define PLLE_AUX_ENABLE_SWCTL (1 << 4) +#define PLLE_AUX_SS_SWCTL (1 << 6) +#define PLLE_AUX_SEQ_ENABLE (1 << 24) + /* PLLX */ #define PLLX_USE_DYN_RAMP 1 #define PLLX_BASE_LOCK (1 << 27) @@ -489,6 +497,49 @@ static unsigned long tegra210_input_freq[] = { #define PLLU_MISC0_WRITE_MASK 0xbfffffff #define PLLU_MISC1_WRITE_MASK 0x00000007 +bool tegra210_plle_hw_sequence_is_enabled(void) +{ + u32 value; + + value = readl_relaxed(clk_base + PLLE_AUX); + if (value & PLLE_AUX_SEQ_ENABLE) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled); + +int tegra210_plle_hw_sequence_start(void) +{ + u32 value; + + if (tegra210_plle_hw_sequence_is_enabled()) + return 0; + + /* skip if PLLE is not enabled yet */ + value = readl_relaxed(clk_base + PLLE_MISC0); + if (!(value & PLLE_MISC_LOCK)) + return -EIO; + + value &= ~PLLE_MISC_IDDQ_SW_CTRL; + writel_relaxed(value, clk_base + PLLE_MISC0); + + value = readl_relaxed(clk_base + PLLE_AUX); + value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE); + value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL); + writel_relaxed(value, clk_base + PLLE_AUX); + + fence_udelay(1, clk_base); + + value |= PLLE_AUX_SEQ_ENABLE; + writel_relaxed(value, clk_base + PLLE_AUX); + + fence_udelay(1, clk_base); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start); + void tegra210_xusb_pll_hw_control_enable(void) { u32 val; diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h index eb016fc9cc0b..f7ff722a03dd 100644 --- a/include/linux/clk/tegra.h +++ b/include/linux/clk/tegra.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2020, NVIDIA CORPORATION. All rights reserved. */ #ifndef __LINUX_CLK_TEGRA_H_ @@ -123,6 +123,8 @@ static inline void tegra_cpu_clock_resume(void) } #endif +extern int tegra210_plle_hw_sequence_start(void); +extern bool tegra210_plle_hw_sequence_is_enabled(void); extern void tegra210_xusb_pll_hw_control_enable(void); extern void tegra210_xusb_pll_hw_sequence_start(void); extern void tegra210_sata_pll_hw_control_enable(void); From patchwork Thu Mar 25 16:40:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164575 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 3B1B4C433C1 for ; Thu, 25 Mar 2021 16:40:56 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D6801619BF for ; Thu, 25 Mar 2021 16:40:55 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D6801619BF Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=hTHm4e9PBxAqGZqYSSGN5vpjnhE4I+9hz5ojG4I5CJU=; b=SfGJqVOOpbQ+4SkTKYSrbVm5K NgdIEAvOkl70nXS2Ukq6yascyWz1a+fqYB/uKB/vk87RkQt9yrxyXpgb9kZ9nOqx4HTaDUjJIF4J1 W7cv3l/iT8W6u96+fuOFOGXJeC/RXw1EMDM9ACK2R7CpAHLYe6lmHpuRNvhmkLITu7ZvZunyRDWYU sU7gqrjcBV1UmyITI3kJmLoFuaxqHZ7sHGLuyyCuz1XF+wpKKKjCTBaGi6QW4x8UaJpW7iNZLWMwI xwCAbX6EnoASfO7NY5FYJmJUvj5pT2L4wD70NkJdcPH2fhhztEsNJAL3B5gGND44/d4MQSYiHtoLt xpvN0vmUA==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2U-001qK9-Ei; Thu, 25 Mar 2021 16:40:54 +0000 Received: from mail-wm1-x336.google.com ([2a00:1450:4864:20::336]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2Q-001qJP-En for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:40:52 +0000 Received: by mail-wm1-x336.google.com with SMTP id z6-20020a1c4c060000b029010f13694ba2so1529897wmf.5 for ; Thu, 25 Mar 2021 09:40:50 -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 :mime-version:content-transfer-encoding; bh=7xHWkhn/z0JEymImWDnOlsM+EPtRLYWHNs/Xv1gr+uM=; b=Q+0cCvOricq0GsG0pDLQC5rPnSBWSZv1Cw7sGhPN6hgzMXFSUCBwTYYT4gWsaqpp+C iZzZRyZ8TTz8Wh/EhBTG29RNDoKhmSfBVG+OnZi6KYwqWRvtOBgFSa1q/slLfTTi7qpv juF/luqVjJ83YlDfU/C95TbD0Hn/Ll+X4R6qSxHVguAYHBJVInoTV+aOCvqWVEmB1EK7 OY5BoJikPRpy+vvbOiw3tHa0sncm/xvjyIcza/aCG3s7KMr8P6DCMHXOf9sARVLzldjF PiUHdz+wLNw/npyPjwqeCIEERXsyojS7UAy8yYPT5QGayaVdJmJ3HAAEij5wmjJVkCi6 w67g== 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:mime-version:content-transfer-encoding; bh=7xHWkhn/z0JEymImWDnOlsM+EPtRLYWHNs/Xv1gr+uM=; b=oz1LzkQyxSKjQwEuWIwiLv/f3eCShq+Id/esHCvHEpyid1pJhGIlf58ayImv+KrOtq oQCj3YCiOkvstO4DouhxKaVTum5WsQmb9Mu70fE1Afps60rOxWGEaGjEL4znxHgSFyTh 0NSMsN/uWQtVIZbLlCMLz7wOBbeFCFU4LLSu3tYenfAH/ssa2ea06fpxjxd33cNb2r9Z qRfzR/oPKUqfDFqU4KvyOnyvQ+AFtACIo5ztsu+uOMWXYn9N6kcgIYoT41XiyDm56wo1 xVGs4IKCQ7hbpjWGo7WHUImnFnSgVTa6vxmNUe5YYRoQfFanuBBZBwv6/JQjaye66uhu d8mQ== X-Gm-Message-State: AOAM530Yu9KvuOg3wk4sQpv6GigVfnVSL0+rVTiXNpAmtZ/6qsAT1O7Q JPwtMNwYemoWXX52U4phgMLqDOZQHh0= X-Google-Smtp-Source: ABdhPJxqtyCkFdufHDekeuhCqWrE08Px7391PietRFnTEt4dlFECQEGki4iWZXp1/0HZZUGVbBPkmg== X-Received: by 2002:a05:600c:19cf:: with SMTP id u15mr8645258wmq.7.1616690449958; Thu, 25 Mar 2021 09:40:49 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id m17sm8496286wrx.92.2021.03.25.09.40.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:40:48 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org, Stephen Boyd Subject: [PATCH v8 02/13] clk: tegra: Don't enable PLLE HW sequencer at init Date: Thu, 25 Mar 2021 17:40:46 +0100 Message-Id: <20210325164057.793954-3-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164051_451215_98CC49A3 X-CRM114-Status: UNSURE ( 9.03 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo 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 Acked-by: Stephen Boyd Signed-off-by: Thierry Reding --- 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 Thu Mar 25 16:40:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164577 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 C246FC433C1 for ; Thu, 25 Mar 2021 16:40:59 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 413F661A1E for ; Thu, 25 Mar 2021 16:40:59 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 413F661A1E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=ZzvxXgKQilblEVvci8K16Lp1p7UItqq1dc15OfKqLvM=; b=EGD1eRoOLZq4Brs2e7VYEhqit B/jy3W2BhN3Vi8ceWpgxqc0s7It+wtTcOjGPjaGyL1WM8thSomYLL4wUZmG/MXBSPuJqi8zzeq+jb 2RLEZHPenten0NAWyK4CxRTZNgPMGeGqFCxDmEqH+5v1c1RVeey15bFBRN7Fw3ktvw+ZMdS+3nS/A QPc1UUBmMnd9CkVGWXDEjPSIJiYManPCbCbDk2pkIVAZEYEDGDYy8ztqXkal4UZlNWMqhJRqqLd8K STw6yOpH+rfle2RE+RwjskXhnzA1edd0Wjjde3xYCl1LI6H0ZbX+Z0QWJRHw4nmSVk+arkPDrCbjw u/jgG93ZA==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2X-001qKt-LR; Thu, 25 Mar 2021 16:40:57 +0000 Received: from mail-wr1-x435.google.com ([2a00:1450:4864:20::435]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2T-001qJp-0l for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:40:55 +0000 Received: by mail-wr1-x435.google.com with SMTP id b9so2936619wrt.8 for ; Thu, 25 Mar 2021 09:40:52 -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 :mime-version:content-transfer-encoding; bh=ebtKj0t5fFS91geJ2HeaRMHYkRbLC4QYHc0p43CsX9w=; b=q+Pn0eHDRGblDBTQKQ99yrmEfSD5rB4AvnIHNMR7vdUbf70JRiV6v0hriZElf0TDog NEcgjjr21y9t+5xx3CxasbvpNco2I8cyyzGTUBpxNJ6WjS74PwFqndfw4C7tIvrTc4Un CGTr0Gf8HODF+etbfKSvMwmINhKLNTZaQuzgqwfJUT7gmnAREBchUyz4MtiVEefLgAgN hXHIL41IVSjhXuwr3B9WhZtTg/T104OBtH8dUm9dqfE99G9cztd/c8py6sjrM8InDNcv cKNa9w8/n4i0MeClY0E6+A0mLSRlCU0CFki1SHcwycwJFjVJv7C/9VvRGpZ/gIce5aW0 0DsQ== 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:mime-version:content-transfer-encoding; bh=ebtKj0t5fFS91geJ2HeaRMHYkRbLC4QYHc0p43CsX9w=; b=BB4n0UdfYrR5apTKh5EnIvbjjxxEZX0zsMVQx8pQ/QeEUi7XousNONOQtC6PzF6g5W X14ZGvP9K6pZdrVxtTWYTKEoNKjwFSAEWstGhZtKxZXQajlANZYjf2jd8gIbPVw4kWYS 18c6TSolfrGGMvic2a0cRomZnnhNuvzqJTqD/nUTzHUR2tyT2UoR5jfjE73/5dXm0v2q ok4vj++RWTDZo15jfG14uSjnG8bWU0H7Cvwm/XqyU8Rg22c5JuXlFZd3QMZpzoK6zOd2 /zN3WuqI1zgeTHAxhQghzIyJzLdKAsmCOKXw+ovTh9qJ0h0ZU/lWrmz8MlSLryFGTwUK CBrA== X-Gm-Message-State: AOAM5313Nf0Wa3VRZyO5Kr0hkunY76XuudKQ2XC/XOf8FFlKOOna+KYw I7IZ9G8C0FypXn+mfLJdrUYBZ19T/48= X-Google-Smtp-Source: ABdhPJwXZVteXJzRKOqCn0xLNhJis1zZd2vpns53KRLu5+NKaqUR9+JdNb2fkmzo8LrehgxliH3uFA== X-Received: by 2002:adf:e7c2:: with SMTP id e2mr10446296wrn.338.1616690452300; Thu, 25 Mar 2021 09:40:52 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id l9sm6546359wmq.2.2021.03.25.09.40.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:40:51 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 03/13] phy: tegra: xusb: Move usb3 port init for Tegra210 Date: Thu, 25 Mar 2021 17:40:47 +0100 Message-Id: <20210325164057.793954-4-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164053_277923_94386FCA X-CRM114-Status: GOOD ( 22.02 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo The programming sequence in tegra210_usb3_port_enable() is required for both cold boot and SC7 exit, and must be performed only after PEX/SATA UPHY is initialized. Therefore, this commit moves the programming sequence to tegra210_usb3_phy_power_on(). PCIE/SATA phy .power_on() stub will invoke tegra210_usb3_phy_power_on() if the lane is assigned for XUSB super-speed. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding Acked-By: Vinod Koul --- drivers/phy/tegra/xusb-tegra210.c | 316 +++++++++++++++++------------- drivers/phy/tegra/xusb.c | 4 +- drivers/phy/tegra/xusb.h | 4 +- 3 files changed, 180 insertions(+), 144 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 66bd4613835b..4dc9286ec1b8 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. * Copyright (C) 2015 Google, Inc. */ @@ -256,6 +256,32 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl) return container_of(padctl, struct tegra210_xusb_padctl, base); } +static const struct tegra_xusb_lane_map tegra210_usb3_map[] = { + { 0, "pcie", 6 }, + { 1, "pcie", 5 }, + { 2, "pcie", 0 }, + { 2, "pcie", 3 }, + { 3, "pcie", 4 }, + { 3, "sata", 0 }, + { 0, NULL, 0 } +}; + +static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane) +{ + const struct tegra_xusb_lane_map *map; + + for (map = tegra210_usb3_map; map->type; map++) { + if (map->index == lane->index && + strcmp(map->type, lane->pad->soc->name) == 0) { + dev_dbg(lane->pad->padctl->dev, "lane = %s map to port = usb3-%d\n", + lane->pad->soc->lanes[lane->index].name, map->port); + return map->port; + } + } + + return -EINVAL; +} + /* must be called under padctl->lock */ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) { @@ -470,19 +496,14 @@ static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); - mutex_lock(&padctl->lock); - if (WARN_ON(pcie->enable == 0)) - goto unlock; + return; if (--pcie->enable > 0) - goto unlock; + return; reset_control_assert(pcie->rst); clk_disable_unprepare(pcie->pll); - -unlock: - mutex_unlock(&padctl->lock); } /* must be called under padctl->lock */ @@ -712,19 +733,14 @@ static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); - mutex_lock(&padctl->lock); - if (WARN_ON(sata->enable == 0)) - goto unlock; + return; if (--sata->enable > 0) - goto unlock; + return; reset_control_assert(sata->rst); clk_disable_unprepare(sata->pll); - -unlock: - mutex_unlock(&padctl->lock); } static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) @@ -1599,6 +1615,128 @@ static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = { TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie), }; +static struct tegra_xusb_usb3_port * +tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane) +{ + int port; + + if (!lane || !lane->pad || !lane->pad->padctl) + return NULL; + + port = tegra210_usb3_lane_map(lane); + if (port < 0) + return NULL; + + return tegra_xusb_find_usb3_port(lane->pad->padctl, port); +} + +static int tegra210_usb3_phy_power_on(struct phy *phy) +{ + struct device *dev = &phy->dev; + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane); + unsigned int index; + u32 value; + + if (!usb3) { + dev_err(dev, "no USB3 port found for lane %u\n", lane->index); + return -ENODEV; + } + + index = usb3->base.index; + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + + if (!usb3->internal) + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + else + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); + + padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL, + XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); + + padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL, + XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); + 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_EARLY(index); + 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(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + return 0; +} + +static int tegra210_usb3_phy_power_off(struct phy *phy) +{ + struct device *dev = &phy->dev; + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane); + unsigned int index; + u32 value; + + if (!usb3) { + dev_err(dev, "no USB3 port found for lane %u\n", lane->index); + return -ENODEV; + } + + index = usb3->base.index; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); + 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(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + return 0; +} static struct tegra_xusb_lane * tegra210_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, unsigned int index) @@ -1668,6 +1806,9 @@ static int tegra210_pcie_phy_power_on(struct phy *phy) value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_on(phy); + unlock: mutex_unlock(&padctl->lock); return err; @@ -1677,15 +1818,22 @@ static int tegra210_pcie_phy_power_off(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int err = 0; u32 value; + mutex_lock(&padctl->lock); + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); tegra210_pex_uphy_disable(padctl); - return 0; + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_off(phy); + + mutex_unlock(&padctl->lock); + return err; } static const struct phy_ops tegra210_pcie_phy_ops = { @@ -1839,6 +1987,9 @@ static int tegra210_sata_phy_power_on(struct phy *phy) value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_on(phy); + unlock: mutex_unlock(&padctl->lock); return err; @@ -1848,15 +1999,22 @@ static int tegra210_sata_phy_power_off(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int err = 0; u32 value; + mutex_lock(&padctl->lock); + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); tegra210_sata_uphy_disable(lane->pad->padctl); - return 0; + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_off(phy); + + mutex_unlock(&padctl->lock); + return err; } static const struct phy_ops tegra210_sata_phy_ops = { @@ -1984,137 +2142,13 @@ static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = { static int tegra210_usb3_port_enable(struct tegra_xusb_port *port) { - struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); - struct tegra_xusb_padctl *padctl = port->padctl; - struct tegra_xusb_lane *lane = usb3->base.lane; - unsigned int index = port->index; - u32 value; - int err; - - value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); - - if (!usb3->internal) - value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); - else - value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); - - value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); - value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port); - padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); - - /* - * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks - * and conditionalize based on mux function? This seems to work, but - * might not be the exact proper sequence. - */ - err = regulator_enable(usb3->supply); - if (err < 0) - return err; - - value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); - value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT); - value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT; - padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); - - value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); - value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT); - value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT; - padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); - - padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL, - XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index)); - - value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); - value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT); - value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT; - padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); - - padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL, - XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index)); - - if (lane->pad == padctl->sata) - err = tegra210_sata_uphy_enable(padctl, true); - else - err = tegra210_pex_uphy_enable(padctl); - - if (err) { - dev_err(&port->dev, "%s: failed to enable UPHY: %d\n", - __func__, err); - return err; - } - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); - 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_EARLY(index); - 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(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - return 0; } static void tegra210_usb3_port_disable(struct tegra_xusb_port *port) { - struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); - struct tegra_xusb_padctl *padctl = port->padctl; - struct tegra_xusb_lane *lane = port->lane; - unsigned int index = port->index; - u32 value; - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); - 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(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - usleep_range(250, 350); - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - if (lane->pad == padctl->sata) - tegra210_sata_uphy_disable(padctl); - else - tegra210_pex_uphy_disable(padctl); - - regulator_disable(usb3->supply); - - value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); - value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); - value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, 0x7); - padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); } -static const struct tegra_xusb_lane_map tegra210_usb3_map[] = { - { 0, "pcie", 6 }, - { 1, "pcie", 5 }, - { 2, "pcie", 0 }, - { 2, "pcie", 3 }, - { 3, "pcie", 4 }, - { 3, "pcie", 4 }, - { 0, NULL, 0 } -}; - static struct tegra_xusb_lane * tegra210_usb3_port_map(struct tegra_xusb_port *port) { diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 941006f503e4..3110aafa8cf6 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. */ #include @@ -376,7 +376,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) return 0; } -static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, +bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function) { const char *func = lane->soc->funcs[lane->function]; diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index ea35af747066..37a5550a84ac 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2015, Google Inc. */ @@ -128,6 +128,8 @@ struct tegra_xusb_lane_ops { void (*remove)(struct tegra_xusb_lane *lane); }; +bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function); + /* * pads */ From patchwork Thu Mar 25 16:40:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164579 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 4873DC433DB for ; Thu, 25 Mar 2021 16:41:03 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C803561A25 for ; Thu, 25 Mar 2021 16:41:02 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C803561A25 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=bHSDjgaRoRemV2ubpueZoJH8ZTIyVH1Ca9uC2TsdD9A=; b=Xo7cc7QKgXdNXBzNsXZm2zS/n EEBsWrmk4AcKNu9IVjjm2lIY/zAaETDxM+IV2tQAGtbUkFMx8ewrGDLrx/LmuKaSw5Jvodqg06SSE Kjpz8saHrQbNgPssP65j/61YkxRtfZxqZ0jceRp9uDULOqZ/3Sr7JMEi1aJdVSF782IiSl+PAA/Jq bODF5C9n0uj6+Gn9AI9fyFIPtaEQ69o2zdO/Vrl4E7B6NErDghVtELDlXckwvd2eIHMKQ7TnGjURT 2qNJF/JkfBbM02d0I6I+g4kfWZ0aaTuP3cQs6MpVzvgFOtLgd4QXUDib1OYf6kJ4FvKt6xrK2pvxi sCjpxv8Uw==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2b-001qLS-6z; Thu, 25 Mar 2021 16:41:01 +0000 Received: from mail-wm1-x330.google.com ([2a00:1450:4864:20::330]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2X-001qKO-24 for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:40:59 +0000 Received: by mail-wm1-x330.google.com with SMTP id m20-20020a7bcb940000b029010cab7e5a9fso3465602wmi.3 for ; Thu, 25 Mar 2021 09:40:56 -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 :mime-version:content-transfer-encoding; bh=bBxf9gBp9k49tjzx0XsWTcdYpobBC9Pn/39efvdl5rc=; b=qCPIOUccU7XljI5VTQFccIZV4+J7rlVS1W7jP9BsFCGztpGLgKiRYX3MxGbBQ4+/Vp jAQxtNl1lXzHO+75x+1C7niOmQjoRXx+TLHRne/x4IAT64lkNyDMQY8rUjB+4/rNDv6k RLc+2Q2pVQ4gH+BU5Qnw5GwcXifd+Jkmo+/HV7JveLprFDx3x/pQHvOaiTfa7IZOK2Lo dJiRNzCQ2l/5SR0gG1LC8ehc8vF/p9Nd7/lK2F0HdAAMQDCeLkSKxriUdmkpq96RpvQC EaiWXqjop0SpzGn6qrW87vyTA2UWSIpATqGlfhHWstTN69hqNxZAvxhUxgEqDM3SceW7 ByXA== 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:mime-version:content-transfer-encoding; bh=bBxf9gBp9k49tjzx0XsWTcdYpobBC9Pn/39efvdl5rc=; b=uVuPI9naIkGxpev/Sf0r7PQlCq8m5eFhphtH+3rj+Rhu1YGw7fLzBZIM6Hf7jXLjM9 5/Ym64PRu9WLF8h1UhyUjRO9oOQ18TeYdCCyI1KKolvykreys4XCJWroCiM39k3djmjH t4N6eJqPnjsmtYdsPNQ4vV+70eG6qYA+FhDVYvgodi3VwWGIHCMiCftaMoxVQsqXCqJT 7ZU9YtSVesWCgPjQ6I0OWlsMX2eTpBFQ3K+MUMdGiriHCUT6DxidmvS9fDQ3RLk5ki1R hs2jbHNJHphhYbSOJAM1o6tuVsiE+D0EwRcmC0P6YnphDvsOWeRCaYv4iOmvgD1Y9FK4 Z4fA== X-Gm-Message-State: AOAM5308iNhC03I7VGDSHxUBJekBA7GeIFEROPLxbEWmtOjd9hlf4ocj e+35zJRJtinlfx1aHGJNIYQ= X-Google-Smtp-Source: ABdhPJxmG6Yl0yapLMAEo0QEzO8tJBX+RRzr4p9emu9Cfcb8CMQGI+0Z+iptw3HF97MaKquh7FQwhQ== X-Received: by 2002:a05:600c:4a06:: with SMTP id c6mr8932653wmp.35.1616690456072; Thu, 25 Mar 2021 09:40:56 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id s83sm7496830wms.16.2021.03.25.09.40.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:40:53 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 04/13] phy: tegra: xusb: Rearrange UPHY init on Tegra210 Date: Thu, 25 Mar 2021 17:40:48 +0100 Message-Id: <20210325164057.793954-5-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164057_185553_00AD7CF8 X-CRM114-Status: GOOD ( 19.59 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo This commit is a preparation for enabling XUSB SC7 support. It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence, for the following reasons: 1. PLLE hardware power sequencer has to be enabled only after both PEX UPHY PLL and SATA UPHY PLL are initialized. tegra210_uphy_init() -> tegra210_pex_uphy_enable() -> tegra210_sata_uphy_enable() -> tegra210_plle_hw_sequence_start() -> tegra210_aux_mux_lp0_clamp_disable() 2. At cold boot and SC7 exit, the following bits must be cleared after PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1). a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN, b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits will be cleared by tegra210_aux_mux_lp0_clamp_disable(). 3. Once UPHY PLL hardware power sequencer is enabled, do not assert reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be broken. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding Acked-By: Vinod Koul --- drivers/phy/tegra/xusb-tegra210.c | 197 +++++++++++++++--------------- drivers/phy/tegra/xusb.h | 4 +- 2 files changed, 103 insertions(+), 98 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 4dc9286ec1b8..faacb866cd1f 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -288,17 +288,19 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); unsigned long timeout; u32 value; + unsigned int i; int err; - if (pcie->enable > 0) { - pcie->enable++; + if (pcie->enable) return 0; - } err = clk_prepare_enable(pcie->pll); if (err < 0) return err; + if (tegra210_plle_hw_sequence_is_enabled()) + goto skip_pll_init; + err = reset_control_deassert(pcie->rst); if (err < 0) goto disable; @@ -481,7 +483,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) tegra210_xusb_pll_hw_sequence_start(); - pcie->enable++; +skip_pll_init: + pcie->enable = true; + + for (i = 0; i < padctl->pcie->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } return 0; @@ -495,29 +504,44 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); + u32 value; + unsigned int i; - if (WARN_ON(pcie->enable == 0)) + if (WARN_ON(!pcie->enable)) return; - if (--pcie->enable > 0) - return; + pcie->enable = false; + + for (i = 0; i < padctl->pcie->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } - reset_control_assert(pcie->rst); clk_disable_unprepare(pcie->pll); } /* must be called under padctl->lock */ -static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb) +static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); + struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0); unsigned long timeout; u32 value; + unsigned int i; int err; + bool usb; - if (sata->enable > 0) { - sata->enable++; + if (sata->enable) + return 0; + + if (IS_ERR(lane)) return 0; - } + + if (tegra210_plle_hw_sequence_is_enabled()) + goto skip_pll_init; + + usb = tegra_xusb_lane_check(lane, "usb3-ss"); err = clk_prepare_enable(sata->pll); if (err < 0) @@ -718,7 +742,14 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb) tegra210_sata_pll_hw_sequence_start(); - sata->enable++; +skip_pll_init: + sata->enable = true; + + for (i = 0; i < padctl->sata->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } return 0; @@ -732,26 +763,27 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb) static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); + u32 value; + unsigned int i; - if (WARN_ON(sata->enable == 0)) + if (WARN_ON(!sata->enable)) return; - if (--sata->enable > 0) - return; + sata->enable = false; + + for (i = 0; i < padctl->sata->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } - reset_control_assert(sata->rst); clk_disable_unprepare(sata->pll); } -static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) +static void tegra210_aux_mux_lp0_clamp_disable(struct tegra_xusb_padctl *padctl) { u32 value; - mutex_lock(&padctl->lock); - - if (padctl->enable++ > 0) - goto out; - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); @@ -767,24 +799,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - -out: - mutex_unlock(&padctl->lock); - return 0; } -static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) +static void tegra210_aux_mux_lp0_clamp_enable(struct tegra_xusb_padctl *padctl) { u32 value; - mutex_lock(&padctl->lock); - - if (WARN_ON(padctl->enable == 0)) - goto out; - - if (--padctl->enable > 0) - goto out; - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); @@ -800,12 +820,38 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); +} + +static int tegra210_uphy_init(struct tegra_xusb_padctl *padctl) +{ + if (padctl->pcie) + tegra210_pex_uphy_enable(padctl); + + if (padctl->sata) + tegra210_sata_uphy_enable(padctl); + + if (!tegra210_plle_hw_sequence_is_enabled()) + tegra210_plle_hw_sequence_start(); + else + dev_dbg(padctl->dev, "PLLE is already in HW control\n"); + + tegra210_aux_mux_lp0_clamp_disable(padctl); -out: - mutex_unlock(&padctl->lock); return 0; } +static void __maybe_unused +tegra210_uphy_deinit(struct tegra_xusb_padctl *padctl) +{ + tegra210_aux_mux_lp0_clamp_enable(padctl); + + if (padctl->sata) + tegra210_sata_uphy_disable(padctl); + + if (padctl->pcie) + tegra210_pex_uphy_disable(padctl); +} + static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, unsigned int index, bool idle) { @@ -942,14 +988,12 @@ 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); - return tegra210_xusb_padctl_enable(padctl); + return 0; } static int tegra210_usb2_phy_exit(struct phy *phy) { - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); - - return tegra210_xusb_padctl_disable(lane->pad->padctl); + return 0; } static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, @@ -1407,14 +1451,12 @@ static int tegra210_hsic_phy_init(struct phy *phy) XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT; padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); - return tegra210_xusb_padctl_enable(padctl); + return 0; } static int tegra210_hsic_phy_exit(struct phy *phy) { - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); - - return tegra210_xusb_padctl_disable(lane->pad->padctl); + return 0; } static int tegra210_hsic_phy_power_on(struct phy *phy) @@ -1778,38 +1820,28 @@ static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = { static int tegra210_pcie_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; - return tegra210_xusb_padctl_enable(lane->pad->padctl); -} + mutex_lock(&padctl->lock); -static int tegra210_pcie_phy_exit(struct phy *phy) -{ - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + tegra210_uphy_init(padctl); + + mutex_unlock(&padctl->lock); - return tegra210_xusb_padctl_disable(lane->pad->padctl); + return 0; } static int tegra210_pcie_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - u32 value; - int err; + int err = 0; mutex_lock(&padctl->lock); - err = tegra210_pex_uphy_enable(padctl); - if (err < 0) - goto unlock; - - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); - if (tegra_xusb_lane_check(lane, "usb3-ss")) err = tegra210_usb3_phy_power_on(phy); -unlock: mutex_unlock(&padctl->lock); return err; } @@ -1819,16 +1851,9 @@ static int tegra210_pcie_phy_power_off(struct phy *phy) struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; int err = 0; - u32 value; mutex_lock(&padctl->lock); - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); - - tegra210_pex_uphy_disable(padctl); - if (tegra_xusb_lane_check(lane, "usb3-ss")) err = tegra210_usb3_phy_power_off(phy); @@ -1838,7 +1863,6 @@ static int tegra210_pcie_phy_power_off(struct phy *phy) static const struct phy_ops tegra210_pcie_phy_ops = { .init = tegra210_pcie_phy_init, - .exit = tegra210_pcie_phy_exit, .power_on = tegra210_pcie_phy_power_on, .power_off = tegra210_pcie_phy_power_off, .owner = THIS_MODULE, @@ -1959,38 +1983,27 @@ static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = { static int tegra210_sata_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; - return tegra210_xusb_padctl_enable(lane->pad->padctl); -} + mutex_lock(&padctl->lock); -static int tegra210_sata_phy_exit(struct phy *phy) -{ - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + tegra210_uphy_init(padctl); - return tegra210_xusb_padctl_disable(lane->pad->padctl); + mutex_unlock(&padctl->lock); + return 0; } static int tegra210_sata_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - u32 value; - int err; + int err = 0; mutex_lock(&padctl->lock); - err = tegra210_sata_uphy_enable(padctl, false); - if (err < 0) - goto unlock; - - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); - if (tegra_xusb_lane_check(lane, "usb3-ss")) err = tegra210_usb3_phy_power_on(phy); -unlock: mutex_unlock(&padctl->lock); return err; } @@ -2000,16 +2013,9 @@ static int tegra210_sata_phy_power_off(struct phy *phy) struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; int err = 0; - u32 value; mutex_lock(&padctl->lock); - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); - - tegra210_sata_uphy_disable(lane->pad->padctl); - if (tegra_xusb_lane_check(lane, "usb3-ss")) err = tegra210_usb3_phy_power_off(phy); @@ -2019,7 +2025,6 @@ static int tegra210_sata_phy_power_off(struct phy *phy) static const struct phy_ops tegra210_sata_phy_ops = { .init = tegra210_sata_phy_init, - .exit = tegra210_sata_phy_exit, .power_on = tegra210_sata_phy_power_on, .power_off = tegra210_sata_phy_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index 37a5550a84ac..ccb5dc9b1220 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -232,7 +232,7 @@ struct tegra_xusb_pcie_pad { struct reset_control *rst; struct clk *pll; - unsigned int enable; + bool enable; }; static inline struct tegra_xusb_pcie_pad * @@ -247,7 +247,7 @@ struct tegra_xusb_sata_pad { struct reset_control *rst; struct clk *pll; - unsigned int enable; + bool enable; }; static inline struct tegra_xusb_sata_pad * From patchwork Thu Mar 25 16:40:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164581 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 BD575C433E0 for ; Thu, 25 Mar 2021 16:41:03 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 3A53B619BF for ; Thu, 25 Mar 2021 16:41:03 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3A53B619BF Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=mtONNv2veFovalGEdUBSM7xwa4pESdM4RABZw97tuVg=; b=e9Sho1OftxJV8vuZ083dvJVco ZR4AK3qj95XwLDwDNNGdo1k+TaPyG8QnS2Uv2DEpUIW6yx6JYEHgw34h/tiNIlLJ7npInye2WFRle uQQRppPkXHPAIE/4BWe57RcIwBGudp1ws7mlEjKlyXvYI91h4HpENfmQCrropO9XcWGhYDyxOulye vMYC2YOGyxGQxbvoGccVRpBLH5OSDiQyn+CZIZuzOXdqUt2H3isiOqg05ZRQjp9/d68vWlUM2V7N5 qjxC0OLgaUPcx9/Gi/QNlXCtT2oSgNFBlcAYW6imyxAPrdkYRap5ZDuvjcr8B0oBhxf9hfEKFC5E7 jNsUI8lVA==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2b-001qLc-PD; Thu, 25 Mar 2021 16:41:01 +0000 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2Y-001qL3-LB for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:00 +0000 Received: by mail-wm1-x32f.google.com with SMTP id f22-20020a7bc8d60000b029010c024a1407so3475576wml.2 for ; Thu, 25 Mar 2021 09:40:58 -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 :mime-version:content-transfer-encoding; bh=EpEbA6CM/P+xQGbvt44AnGB0qjb5n3dM+VQpCNtPqmM=; b=j+tp6SfV0bfa/JnbEw3236v8dfeZo+yaidQwTkurboXKO7+DX08vw1X1aYhciD8fg/ 0mn/95q25Qe1RGhHMrmQOlOPu8A6mYAw4W3RMt9+kMB8aEUOvGry753YlkZLGEnAElVR z05g6AyKoI826aKOtGQUcKq9xNkWxwPjp5wf1iTNDvFu7DIGwy+rSFD5YhGQ7lLwRYoo /RcG4vlCb+AcemL2qPPHCV4T0J5oqcC5P3zMKrFoEMnB7Z2j/VGk5m8sD0nrCcothxyU qBMXCcfYC0f3SdJlVw8m5SpjrAend5fS5KpGna/amlf76rOpcTBy65f7BDRWhuxxpz95 d7yA== 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:mime-version:content-transfer-encoding; bh=EpEbA6CM/P+xQGbvt44AnGB0qjb5n3dM+VQpCNtPqmM=; b=hNlhPm382c3dVESb1Psj8dlbMQfwe2E/DGG1FiEodBqizZl0xEXovzM06ZE60HqnLY 7B9fwnS8eDt4yMAQ/HDnI/Ut4KiNVzddfl2S2mzChRVV0/SRFGUJpioPIcSaYH9hB3CQ IhPb8KwHe1y5v8cyLoEmsdD3szISk2as+sHZXC9xAGado8OhxG5fXoCAGBaGfUJ8aoFl X6Zi5w+TBI7+f4frYmDo87RYcvEMM6UbocdjBAc9BuADpJaJj6zl05XDlO+/PvDZnR0l HqmxPxzYsIwOLTFE2cAxYr3pIP01SMtNA/t9ql+SmP5dZ4CfWc8MkoAf8FFiAPd5CI2k wDOQ== X-Gm-Message-State: AOAM5331CC0l6b9bhpytRA7sCkII3xNLVSY3YEu+ecUfs63Xqx50ckPP Wq06IOwvi0AkrPvxuC2hV88= X-Google-Smtp-Source: ABdhPJwe09p97Asz1de3vmAHa8PX+hcWWVthf2x8ZLCdzwVPwxpOio3pTUB3zoFMKqg2vVcH5vUIbg== X-Received: by 2002:a05:600c:287:: with SMTP id 7mr9345030wmk.23.1616690458100; Thu, 25 Mar 2021 09:40:58 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id d18sm8367429wra.8.2021.03.25.09.40.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:40:57 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 05/13] phy: tegra: xusb: Add Tegra210 lane_iddq operation Date: Thu, 25 Mar 2021 17:40:49 +0100 Message-Id: <20210325164057.793954-6-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164058_780438_67E0401D X-CRM114-Status: GOOD ( 15.98 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo As per Tegra210 TRM, before changing lane assignments, driver should keep lanes in IDDQ and sleep state; after changing lane assignments, driver should bring lanes out of IDDQ. This commit implements the required operations. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding Acked-By: Vinod Koul --- drivers/phy/tegra/xusb-tegra210.c | 82 ++++++++++++++++++++++++++++--- drivers/phy/tegra/xusb.c | 6 +++ drivers/phy/tegra/xusb.h | 6 +++ 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index faacb866cd1f..b038d032fea1 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -198,6 +198,18 @@ #define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18) #define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13) +#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(x) (0x464 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ BIT(0) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD BIT(1) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK (0x3 << 4) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL (0x3 << 4) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD BIT(24) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ BIT(8) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD BIT(9) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK (0x3 << 12) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL (0x3 << 12) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD BIT(25) + #define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860 #define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864 @@ -209,6 +221,7 @@ #define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c #define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960 +#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2 0x964 #define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16 @@ -1640,6 +1653,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad = { .ops = &tegra210_hsic_ops, }; +static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, lane->soc->regs.misc_ctl2); + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL; + padctl_writel(padctl, value, lane->soc->regs.misc_ctl2); +} + +static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, lane->soc->regs.misc_ctl2); + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL; + padctl_writel(padctl, value, lane->soc->regs.misc_ctl2); +} + +#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc) \ + { \ + .name = _name, \ + .offset = _offset, \ + .shift = _shift, \ + .mask = _mask, \ + .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions), \ + .funcs = tegra210_##_type##_functions, \ + .regs.misc_ctl2 = _misc, \ + } + static const char *tegra210_pcie_functions[] = { "pcie-x1", "usb3-ss", @@ -1648,13 +1710,13 @@ static const char *tegra210_pcie_functions[] = { }; static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = { - TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, pcie), - TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, pcie), - TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, pcie), - TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, pcie), - TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, pcie), - TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, pcie), - TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie), + TEGRA210_UPHY_LANE("pcie-0", 0x028, 12, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(0)), + TEGRA210_UPHY_LANE("pcie-1", 0x028, 14, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(1)), + TEGRA210_UPHY_LANE("pcie-2", 0x028, 16, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(2)), + TEGRA210_UPHY_LANE("pcie-3", 0x028, 18, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(3)), + TEGRA210_UPHY_LANE("pcie-4", 0x028, 20, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(4)), + TEGRA210_UPHY_LANE("pcie-5", 0x028, 22, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(5)), + TEGRA210_UPHY_LANE("pcie-6", 0x028, 24, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(6)), }; static struct tegra_xusb_usb3_port * @@ -1815,6 +1877,8 @@ static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = { .probe = tegra210_pcie_lane_probe, .remove = tegra210_pcie_lane_remove, + .iddq_enable = tegra210_uphy_lane_iddq_enable, + .iddq_disable = tegra210_uphy_lane_iddq_disable, }; static int tegra210_pcie_phy_init(struct phy *phy) @@ -1939,7 +2003,7 @@ static const struct tegra_xusb_pad_soc tegra210_pcie_pad = { }; static const struct tegra_xusb_lane_soc tegra210_sata_lanes[] = { - TEGRA210_LANE("sata-0", 0x028, 30, 0x3, pcie), + TEGRA210_UPHY_LANE("sata-0", 0x028, 30, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2), }; static struct tegra_xusb_lane * @@ -1978,6 +2042,8 @@ static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = { .probe = tegra210_sata_lane_probe, .remove = tegra210_sata_lane_remove, + .iddq_enable = tegra210_uphy_lane_iddq_enable, + .iddq_disable = tegra210_uphy_lane_iddq_disable, }; static int tegra210_sata_phy_init(struct phy *phy) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 3110aafa8cf6..a34d304677bb 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) if (soc->num_funcs < 2) return; + if (lane->pad->ops->iddq_enable) + lane->pad->ops->iddq_enable(lane); + /* choose function */ value = padctl_readl(padctl, soc->offset); value &= ~(soc->mask << soc->shift); value |= lane->function << soc->shift; padctl_writel(padctl, value, soc->offset); + + if (lane->pad->ops->iddq_disable) + lane->pad->ops->iddq_disable(lane); } static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index ccb5dc9b1220..e789d5ff4eb8 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -35,6 +35,10 @@ struct tegra_xusb_lane_soc { const char * const *funcs; unsigned int num_funcs; + + struct { + unsigned int misc_ctl2; + } regs; }; struct tegra_xusb_lane { @@ -126,6 +130,8 @@ struct tegra_xusb_lane_ops { struct device_node *np, unsigned int index); void (*remove)(struct tegra_xusb_lane *lane); + void (*iddq_enable)(struct tegra_xusb_lane *lane); + void (*iddq_disable)(struct tegra_xusb_lane *lane); }; bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function); From patchwork Thu Mar 25 16:40:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164583 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, 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 26D1CC433DB for ; Thu, 25 Mar 2021 16:41:10 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9D2D461A25 for ; Thu, 25 Mar 2021 16:41:09 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 9D2D461A25 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=FcLamzRFuX5rGu9klRlhCinSaKTKLJ4pM4OVDmzJgdM=; b=IQyBXlXIlgv4KvSv7Nh+tpsqa wkAupIIi6Gx2NUdZs5fKkVk6SHDOz5I5NZB5Ng54aQRFWIt3l38jj7E/HduBGtZb2WB3Zz/PcB+75 0dd7XA5EUaqhyS96tQ92SfTQths0jBEAD7JRwP59WarHVQDUJv5gsBqG9o+zGcM4cucDCEo6Pd257 1zM7czm+m+c1wVhlNyy2oCy8lKInAgbqIHKydgXGPbtXuCmlriMP063ykWmys3z2O2ZBXgHoUNGqf SAPRfyzXHGZhRFrquKI2npkivS3cdwFjq4C2b1q09q16DJDVhigZZsRWNe1zbbULn+RhmQXI8QMIu 2BK036yiQ==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2i-001qMA-51; Thu, 25 Mar 2021 16:41:08 +0000 Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2d-001qLn-Lq for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:05 +0000 Received: by mail-wr1-x42b.google.com with SMTP id z2so2954563wrl.5 for ; Thu, 25 Mar 2021 09:41:03 -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 :mime-version:content-transfer-encoding; bh=THw8zcgbqLcO16dUqauFb0z8OAhU7380cxSadcHRy9U=; b=IloW8Z33ZDvYuKx2XBziTeFxCxMoDYKa2NhSguqvWJsy76doeS3uEx7rrkXSy+VKzz BWbxBOAjRnb89Bd5P5tQlGnn+/JgzAFTlXVlJL9Z1SUC0lv4+8R3ueo/oPDFUyy8fGHn 1wPjz4s88BAIv0gJFqEeTMCQOJXDx/WPNl2LM4q9EMJCn9306irOl1jnHcWDHgdXQxYd YKUNXJOloW5VBG4dIDj2cdClJzXgFkXshnWRJJjrXkN4pncRX+eseRdyd3xLFQrmDY+V kaM2Hkc3gFw/dYDyePPYYSL30Q99s/fQU00yUbo4RueJl9EiTrlR1i3++nqhkuwaaGft m1TQ== 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:mime-version:content-transfer-encoding; bh=THw8zcgbqLcO16dUqauFb0z8OAhU7380cxSadcHRy9U=; b=JWoEW9hadhNwhhoV0UbXV/lLtXZKala2meW7C4eIbeaLZal9Jz+2zLgasbDreqhjGp 08JL9PXmFDHt3uZjv8p/3dV0ybNO1QQCkr5Lk1vW6SgZZftnQNw6ID20/NE+ronMqYLH mcA5eR7JsGkHfjWYKOMtzDlsDdew+ji0IHHNxkwErlPFgOyy7Rf3S/1NL62c5JWGveFm HR4m6Vbku6twT0ZkwBGFSM3+UEsLbLXcyOPlS+oM86b0d/6Abb/ehy4N3mwnBuE66+Xj mI43dMForCIj2u81thOGMQMYwxQhZGxpGv8y9bvWOOWlYB9tuoaFkNBYk4PDJ9xxbymk c0Wg== X-Gm-Message-State: AOAM531MJCja+o9sMF0LGDlYawMppJ+qwYHFoeEUwzEVqb0H5bituLMH gzvByPM33aNA8ivfp1r2OLkY+JyTJdc= X-Google-Smtp-Source: ABdhPJwS8D3jR/+zm9zeYJjDu4IIIl3Zh0tcS8mq+uGfbnHw8pab8S329eTtv5epyq7ZB+aN8xZozQ== X-Received: by 2002:adf:d236:: with SMTP id k22mr9959080wrh.144.1616690463074; Thu, 25 Mar 2021 09:41:03 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id k11sm7115040wmj.1.2021.03.25.09.40.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:40:59 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 06/13] phy: tegra: xusb: Add sleepwalk and suspend/resume Date: Thu, 25 Mar 2021 17:40:50 +0100 Message-Id: <20210325164057.793954-7-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164103_777832_D437EB1F X-CRM114-Status: GOOD ( 20.33 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo 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 and can respond to USB resume signals when controller is not powered. Sleepwalk can be enabled/disabled for any USB UPHY 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 Signed-off-by: Thierry Reding Acked-By: Vinod Koul --- drivers/phy/tegra/xusb.c | 82 ++++++++++++++++++++++++++++++++++ drivers/phy/tegra/xusb.h | 8 ++++ include/linux/phy/tegra/xusb.h | 10 ++++- 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index a34d304677bb..0aadac678191 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1273,10 +1273,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, @@ -1343,6 +1369,62 @@ 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) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->enable_phy_sleepwalk) + return lane->pad->ops->enable_phy_sleepwalk(lane, speed); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk); + +int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->disable_phy_sleepwalk) + return lane->pad->ops->disable_phy_sleepwalk(lane); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk); + +int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->enable_phy_wake) + return lane->pad->ops->enable_phy_wake(lane); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake); + +int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->disable_phy_wake) + return lane->pad->ops->disable_phy_wake(lane); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake); + +bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->remote_wake_detected) + return lane->pad->ops->remote_wake_detected(lane); + + return false; +} +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 e789d5ff4eb8..034f7a2c28d6 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -132,6 +133,11 @@ struct tegra_xusb_lane_ops { void (*remove)(struct tegra_xusb_lane *lane); void (*iddq_enable)(struct tegra_xusb_lane *lane); void (*iddq_disable)(struct tegra_xusb_lane *lane); + int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed); + int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane); + int (*enable_phy_wake)(struct tegra_xusb_lane *lane); + int (*disable_phy_wake)(struct tegra_xusb_lane *lane); + bool (*remote_wake_detected)(struct tegra_xusb_lane *lane); }; bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function); @@ -396,6 +402,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, diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h index 71d956935405..3a35e74cdc61 100644 --- a/include/linux/phy/tegra/xusb.h +++ b/include/linux/phy/tegra/xusb.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. */ #ifndef 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,11 @@ 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); +bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy); + #endif /* PHY_TEGRA_XUSB_H */ From patchwork Thu Mar 25 16:40:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164585 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=-10.9 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY,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 01FA0C433C1 for ; Thu, 25 Mar 2021 16:41:17 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A515E61A25 for ; Thu, 25 Mar 2021 16:41:16 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org A515E61A25 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=j+R1y5S4x6FpeYXoz3p2ku/nWtcOGSqeI/4iYC+s2oA=; b=XbeXkea2vTssj9qkQjafFHbpG peuvlUKyksh+gZt+vGbX+G5RC2QdmSZ6jP5cnq1TGFX8tAh9vgb0JWc8oZG3VkArCZIQYp6eRWaxt ujE882lw9RURcenr2ErvOMbdvEJw19AmrTCOU38csjuS3jusxa5PmuSZtSy76Lcrn6yOqx1dWSXN0 TIVbbaEiFkhRQ0ZuseIyxjbiQhKe7AnmQaHIQnpA7vABfiIx74TRfWF8I6ICoIaj7wMB8yytNKYaG EkdNMw0siTgz4n6gQgPR9s5wKQSmFulOw8z2SxRJKrHAKFf1yYU42JcnGz/LPbIMIps8eq0o26Vvg rBr+Ay8Vw==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2o-001qMc-P0; Thu, 25 Mar 2021 16:41:14 +0000 Received: from mail-wm1-x329.google.com ([2a00:1450:4864:20::329]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2l-001qMH-6P for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:13 +0000 Received: by mail-wm1-x329.google.com with SMTP id b2-20020a7bc2420000b029010be1081172so1552256wmj.1 for ; Thu, 25 Mar 2021 09:41:10 -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 :mime-version:content-transfer-encoding; bh=tl+/f6WBFGlmAJ8aIGRC63W4tU1XCz5OC6vf2oAyFwc=; b=KclB8RfUv1B0+s/Aq9d6LDmYLJQ+g7chyt+f1uNO4St/2aDKsnSVqKTRICaSRxBOlp LkJHl2Ewy1p+NGPNAe0MpXwjytRJaE7VNtOYZiQvsi17zWehd5gqh7kBHeqZaTxMKnY0 UdYqVVFY6HKsjWVAWfh8dbdtgjTjOP8tHBbF3d7G1RHMZEIoGaMscO61Au2HtM7OwwlH zag/2zuctEP9sTWHz7JBw0GyoCpV0XGzj1X50kvROIsp7cqK4vBTAVpYpjk+CoTOEW96 G7h48vrCxgurglrI4kgnA7RVxn4cdp9tx21ayA4tKH4d7irjJqd02mVKJ/uBFnYD5FTt zZOg== 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:mime-version:content-transfer-encoding; bh=tl+/f6WBFGlmAJ8aIGRC63W4tU1XCz5OC6vf2oAyFwc=; b=TaU1EJOio8umPkFoYCaXdSJwd8palOCppxMC37TgMQIKsO3qXmn1ODSZEI7mWqgoVA wY1IzGbjJZU/avgYh4337Vfg06Nb27AObDcGA9c2w9xnWyQlNHjSOawo1P8ARxYGjE31 9++EdWwhb51mgcOoewVUgGWgyAO96wJJbTXSqFnpyQt5pgXtC6ks6JNK4h88M7YUvfIp 3b3VMp/uoX3mX4GPvkPLo9oYJiAl+ixHH6VCqkMxbG4QgyDt0aFNlw0sa8PEs6hq/qSZ ymWNcJ9dMZTITW7aV/0A8Cl9GUC/D8c6Bk5QfUi9U/6z9GtFxljtrWf4QBNSaV4+KijD N5SA== X-Gm-Message-State: AOAM530sKTsQubhyidpJq9H5lqDYoZdcEcYcsnnFyW9i0LaiNPAK9KIV VtMDFE44Ci7OPrfmlCOQQGw= X-Google-Smtp-Source: ABdhPJy0KdWUyF30nlp+OU7CCK8byVoUZ8t70G/6DvaXQXbqGc9ubh+0Kntdvs/tqca5cRqFQnnVTQ== X-Received: by 2002:a1c:4e07:: with SMTP id g7mr9031309wmh.29.1616690470611; Thu, 25 Mar 2021 09:41:10 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id s83sm7497577wms.16.2021.03.25.09.41.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:04 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 07/13] soc/tegra: pmc: Provide USB sleepwalk register map Date: Thu, 25 Mar 2021 17:40:51 +0100 Message-Id: <20210325164057.793954-8-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164111_337421_70B77A81 X-CRM114-Status: GOOD ( 18.24 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo This commit implements a register map which grants USB (UTMI and HSIC) sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic is in PMC hardware block but USB PHY drivers have the best knowledge of proper programming sequence. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index df9a5ca8c99c..a619a23f9592 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,9 @@ #define PMC_PWR_DET_VALUE 0xe4 +#define PMC_USB_DEBOUNCE_DEL 0xec +#define PMC_USB_AO 0xf0 + #define PMC_SCRATCH41 0x140 #define PMC_WAKE2_MASK 0x160 @@ -133,6 +137,13 @@ #define IO_DPD2_STATUS 0x1c4 #define SEL_DPD_TIM 0x1c8 +#define PMC_UTMIP_UHSIC_TRIGGERS 0x1ec +#define PMC_UTMIP_UHSIC_SAVED_STATE 0x1f0 + +#define PMC_UTMIP_TERM_PAD_CFG 0x1f8 +#define PMC_UTMIP_UHSIC_SLEEP_CFG 0x1fc +#define PMC_UTMIP_UHSIC_FAKE 0x218 + #define PMC_SCRATCH54 0x258 #define PMC_SCRATCH54_DATA_SHIFT 8 #define PMC_SCRATCH54_ADDR_SHIFT 0 @@ -145,8 +156,18 @@ #define PMC_SCRATCH55_CHECKSUM_SHIFT 16 #define PMC_SCRATCH55_I2CSLV1_SHIFT 0 +#define PMC_UTMIP_UHSIC_LINE_WAKEUP 0x26c + +#define PMC_UTMIP_BIAS_MASTER_CNTRL 0x270 +#define PMC_UTMIP_MASTER_CONFIG 0x274 +#define PMC_UTMIP_UHSIC2_TRIGGERS 0x27c +#define PMC_UTMIP_MASTER2_CONFIG 0x29c + #define GPU_RG_CNTRL 0x2d4 +#define PMC_UTMIP_PAD_CFG0 0x4c0 +#define PMC_UTMIP_UHSIC_SLEEP_CFG1 0x4d0 +#define PMC_UTMIP_SLEEPWALK_P3 0x4e0 /* Tegra186 and later */ #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2)) #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3) @@ -334,6 +355,7 @@ struct tegra_pmc_soc { const struct pmc_clk_init_data *pmc_clks_data; unsigned int num_pmc_clks; bool has_blink_output; + bool has_usb_sleepwalk; }; /** @@ -2443,6 +2465,67 @@ static void tegra_pmc_clock_register(struct tegra_pmc *pmc, err); } +static const struct regmap_range pmc_usb_sleepwalk_ranges[] = { + regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO), + regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE), + regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE), + regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, PMC_UTMIP_UHSIC_LINE_WAKEUP), + regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG), + regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG), + regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1), + regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3), +}; + +static const struct regmap_access_table pmc_usb_sleepwalk_table = { + .yes_ranges = pmc_usb_sleepwalk_ranges, + .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges), +}; + +static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned int *value) +{ + struct tegra_pmc *pmc = context; + + *value = tegra_pmc_readl(pmc, offset); + return 0; +} + +static int tegra_pmc_regmap_writel(void *context, unsigned int offset, unsigned int value) +{ + struct tegra_pmc *pmc = context; + + tegra_pmc_writel(pmc, value, offset); + return 0; +} + +static const struct regmap_config usb_sleepwalk_regmap_config = { + .name = "usb_sleepwalk", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, + .rd_table = &pmc_usb_sleepwalk_table, + .wr_table = &pmc_usb_sleepwalk_table, + .reg_read = tegra_pmc_regmap_readl, + .reg_write = tegra_pmc_regmap_writel, +}; + +static int tegra_pmc_regmap_init(struct tegra_pmc *pmc) +{ + struct regmap *regmap; + int err; + + if (pmc->soc->has_usb_sleepwalk) { + regmap = devm_regmap_init(pmc->dev, NULL, pmc, &usb_sleepwalk_regmap_config); + if (IS_ERR(regmap)) { + err = PTR_ERR(regmap); + dev_err(pmc->dev, "failed to allocate register map (%d)\n", err); + return err; + } + } + + return 0; +} + static int tegra_pmc_probe(struct platform_device *pdev) { void __iomem *base; @@ -2548,6 +2631,10 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (err) goto cleanup_restart_handler; + err = tegra_pmc_regmap_init(pmc); + if (err < 0) + goto cleanup_restart_handler; + err = tegra_powergate_init(pmc, pdev->dev.of_node); if (err < 0) goto cleanup_powergates; @@ -2706,6 +2793,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .pmc_clks_data = NULL, .num_pmc_clks = 0, .has_blink_output = true, + .has_usb_sleepwalk = false, }; static const char * const tegra30_powergates[] = { @@ -2764,6 +2852,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .pmc_clks_data = tegra_pmc_clks_data, .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, + .has_usb_sleepwalk = false, }; static const char * const tegra114_powergates[] = { @@ -2818,6 +2907,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .pmc_clks_data = tegra_pmc_clks_data, .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, + .has_usb_sleepwalk = false, }; static const char * const tegra124_powergates[] = { @@ -2932,6 +3022,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .pmc_clks_data = tegra_pmc_clks_data, .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, + .has_usb_sleepwalk = true, }; static const char * const tegra210_powergates[] = { @@ -3059,6 +3150,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .pmc_clks_data = tegra_pmc_clks_data, .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, + .has_usb_sleepwalk = true, }; #define TEGRA186_IO_PAD_TABLE(_pad) \ @@ -3214,6 +3306,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .pmc_clks_data = NULL, .num_pmc_clks = 0, .has_blink_output = false, + .has_usb_sleepwalk = false, }; #define TEGRA194_IO_PAD_TABLE(_pad) \ @@ -3347,6 +3440,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { .pmc_clks_data = NULL, .num_pmc_clks = 0, .has_blink_output = false, + .has_usb_sleepwalk = false, }; static const struct tegra_pmc_regs tegra234_pmc_regs = { From patchwork Thu Mar 25 16:40:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164587 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, 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 13605C433C1 for ; Thu, 25 Mar 2021 16:41:26 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B55CA61A25 for ; Thu, 25 Mar 2021 16:41:25 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B55CA61A25 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=yf9HekjebhA5MtnJhvP9+uzbMtkVhucQ3EPM3SIuGoM=; b=XV97k6fEuW6kaUjWT51zQKrds EOMoq+aLUYZrMdT7FItp3vEX2HzgotRItQzbNw04OIFFaPaMNaZmjTuPU+0DUQ5boYQ/8RM5xV9mC 4cM3rp2yXwiJhOscWowQ6C8orsuo3kGAKQVBSm1/XVxNpfU8Jof0IpsVMtq+gASZeY/zxIfy/USL8 dMiT3NVMS4kBHjDpy9H3TcO74hPD7k3Hrg4Hmz5Ch880rAEmDCHO13HFwX5882WOPuYeJAPXL5lwk LDAFkbi+yj0syNqJdZTRt5ThvXNNOXOnmooYE/qtVzxRKE7IvTn2suC3VhPR130Z5rNHqbUjpY7vX Hb/+0/8Rg==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2y-001qNJ-5q; Thu, 25 Mar 2021 16:41:24 +0000 Received: from mail-wm1-x335.google.com ([2a00:1450:4864:20::335]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2u-001qMm-65 for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:22 +0000 Received: by mail-wm1-x335.google.com with SMTP id g25so1573628wmh.0 for ; Thu, 25 Mar 2021 09:41:19 -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 :mime-version:content-transfer-encoding; bh=OPzdS0BZuVjzz5zpA0akhJTbQna6KzdKZ0ifPMBA74M=; b=G/7ctoLhmSP84RR4q7afIf6il7apjpEgOCwfVjSesqvj7A0TsbK9VcN1MPey8dpg1j lJEDBiCwGbv7aKhE/42F9YUgqqy0Dd9M4qj7ztceD19taQTSU4yWc/Pkdi7ePLojBPiK A+4CcuYywYEZSrCLO8NcVzo0RxXIOjzYYU12tCZ/oLLaV6FYFwsN2qMyEycdCdFUpKC3 yXUeaOMZu9uOhxDYBQV88hvih/WE3A5Cm/km8UTS8qKzqjLpP8PZNt2Pv1SebWINvUtt wwYLewbXulIBLRnS/nkQbsvzE6+pmOteFxLadVuV8U8bw6+mFCa0snuQceMnYc1gKqr0 CVTA== 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:mime-version:content-transfer-encoding; bh=OPzdS0BZuVjzz5zpA0akhJTbQna6KzdKZ0ifPMBA74M=; b=Sj28U/fOUs8N5evZT6oaB+pu/u2zORnxrN2m/NIkV+Vvu0i9T5GoMA+VYV9XO/a0zY t4vVv7Eswe/Z6d4EG+T+SxFilEJViHjJVPSBMGxsV8sRY1E17FjnlSrXaY6Fw+fDsgxZ QWw8h9TzJS8pfSM/r8djxwZVq2R1GfHx09cjJ8hunsGHIb/qJGzWiRmaJr1loABD8gmj P9We4BkphKeC70Zttn2vEQKVsYpaKmgkYM6jVzTwcOdmfe6SJo4gqM0pibVCaBbre3+u FnjrRm6YMlCrYGQEJ3FzrImtE7QkfLKEoyNkBnPjTFSG9z0jmMGv3l9FeIOt0KIcDZtD Hdtg== X-Gm-Message-State: AOAM532YIXQkh6Iw3+k0pyI4X4WmzPEDt2wTJ1+MjalZcMt1dYvCXj1Y QRX06DdbH3ghF84IiOfjjDQ= X-Google-Smtp-Source: ABdhPJybCAx2KsEH5ARujBPMu3ypk9FAutXosozY6u1qfZqL+RTzMIwChPGJ/VQvLGdpRJXNJg+O8A== X-Received: by 2002:a05:600c:378c:: with SMTP id o12mr8970266wmr.69.1616690479608; Thu, 25 Mar 2021 09:41:19 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id q207sm7074912wme.36.2021.03.25.09.41.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:11 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org, Rob Herring Subject: [PATCH v8 08/13] dt-bindings: phy: tegra-xusb: Add nvidia,pmc prop Date: Thu, 25 Mar 2021 17:40:52 +0100 Message-Id: <20210325164057.793954-9-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164120_736973_74C19787 X-CRM114-Status: UNSURE ( 9.11 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo This commit describes the "nvidia,pmc" property for Tegra210 tegra-xusb PHY driver. It is a phandle and specifier referring to the Tegra210 pmc@7000e400 node. Signed-off-by: JC Kuo Acked-by: Rob Herring Signed-off-by: Thierry Reding --- .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt index 38c5fa21f435..b62397d2bb0c 100644 --- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt @@ -54,6 +54,7 @@ For Tegra210: - avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V. - dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V. - hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V. +- nvidia,pmc: phandle and specifier referring to the Tegra210 PMC node. For Tegra186: - avdd-pll-erefeut-supply: UPHY brick and reference clock as well as UTMI PHY From patchwork Thu Mar 25 16:40:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164591 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, 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 A6814C433C1 for ; Thu, 25 Mar 2021 16:41:29 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1482C619BF for ; Thu, 25 Mar 2021 16:41:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1482C619BF Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=hUGQQ7kL3vw0tDrXwtpbjLL8GjohpwCSggQZLYo8LYk=; b=MDuCpyyDUcjoGbaUa9yIsMVkI W7eL7QxWuzSeRqOO9ftn4pjo+2hz0FBpZW82OO8pLPVxngyE12Wmt55GDUQBAX+ANCPdb33YXmcqX dYSe1c1U7ihwrB7mH+wmsCtVI7g8fwQs0or4yyXAGNPzPoTfEZaEVNOkdgdwXOVRk696kiUBTavHe uCJfaAVOvRodH24LCxtb8j5uhx/jUVYEggPUbNJk8Mg4sJVfBTtI7cN9PdHPHtPw4aCIddL13f85n 6y6BY+5FIqaO75oYC2hjQ/jbPlTPIQH6ZEBJj0sMtP4kOThKCy3OpY6A10x1ZXmJqKHlgFqvykoLs ElL3W5gQw==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT31-001qNm-Is; Thu, 25 Mar 2021 16:41:27 +0000 Received: from mail-wr1-x435.google.com ([2a00:1450:4864:20::435]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2w-001qN2-I0 for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:25 +0000 Received: by mail-wr1-x435.google.com with SMTP id e18so2954134wrt.6 for ; Thu, 25 Mar 2021 09:41:22 -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 :mime-version:content-transfer-encoding; bh=MQoxEc3w2v6pmQQ4kNvF8Hq2eWdul3mxmXS3gmEZTbs=; b=O7C93sT5s7lwg7QZs0Vi8UVCN5yYYHLV9zDAqg+u6x7pMfLxXs6SgdvdFfV5jQIPYU NMzOWoovUUzoGKpBLD1tYSk3SWxqUQU4CfBt0DDLA6uiwoO07k9PhZfHqZ6nSJv3/dca tKz97fjkoWMvy7NyxzJ0ZrdZwPTMWQRwhi5iMiCEOsjSvtG44GE/mLdKj3i93zYZSn+k Arb2VURIrHwUz55IsHmRxCBL4TrGAFfbD/J5b/R/cZhle2e8XAW+CSRJnPNTU7nP9k1U z08ih4fyaJnP+SZhR1lL6Osk1Uuw55vIa5I2IhPZPDRttvDjykK47CqAJCZXpUj/S5/D 42Ng== 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:mime-version:content-transfer-encoding; bh=MQoxEc3w2v6pmQQ4kNvF8Hq2eWdul3mxmXS3gmEZTbs=; b=CWfHiwjUoqcnFnUjA61L0M7tp53a6Zwt7OMr0s7Re22MqLBZZ2B5Hs42rEPwAiVBBv dzE49IinyiuCO0owcc7/Zh4VeO19+lC/1Q++l8lwiL3Jj6YDlkt6pQOuJQIXVmbb+uo3 EAWGXUz7AAgEfwfNNuBLoZkjKNmqZf/Gjm7zNBEsEAxhcqT712ZYTpRCt/UVObhV079Z xW1aiL1q6MDEyCWxrkWarhnUI8KiW9vCbBWMFLCRfh2R4dsH6Sn6sH9UfgwnlnzfVV0i A7ay57zqrNl5R8RgdYmQSbSO1W+UZwMUvQjcE5IVpNZ5tJmEdgh9hYBlM/LjO05ddpxC 4vrA== X-Gm-Message-State: AOAM5302HMyddg4pWegoUS/Y3zzT9SDhAgwYugNOEKd/8xvqBpt3qtH2 gOrmgbI4MmFLxCvOhZMwrFexiU9I2tw= X-Google-Smtp-Source: ABdhPJxxRLZQrWc1QNx+cVDagXSMKfZb41M8lvGuvUmw/d4uMq0gzUegUyvI+3EEouK41vlLekMTqA== X-Received: by 2002:adf:9d82:: with SMTP id p2mr10053130wre.234.1616690482004; Thu, 25 Mar 2021 09:41:22 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id 1sm12040506wmj.0.2021.03.25.09.41.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:20 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 09/13] phy: tegra: xusb: Add wake/sleepwalk for Tegra210 Date: Thu, 25 Mar 2021 17:40:53 +0100 Message-Id: <20210325164057.793954-10-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164122_749873_5B463E75 X-CRM114-Status: GOOD ( 16.02 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo 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 Signed-off-by: Thierry Reding --- drivers/phy/tegra/xusb-tegra210.c | 930 ++++++++++++++++++++++++++++++ 1 file changed, 930 insertions(+) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index b038d032fea1..8af73ba78ad7 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 regmap *regmap; struct tegra210_xusb_fuse_calibration fuse; + struct tegra210_xusb_padctl_context context; }; static inline struct tegra210_xusb_padctl * @@ -890,6 +1055,663 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, return 0; } +static int tegra210_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + 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 tegra_xusb_lane *lane) +{ + 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 tegra_xusb_lane *lane) +{ + 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 tegra_xusb_lane *lane) +{ + 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 bool tegra210_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int index = tegra210_usb3_lane_map(lane); + u32 value; + + if (index < 0) + return false; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + +static int tegra210_utmi_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + 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 tegra_xusb_lane *lane) +{ + 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 bool tegra210_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) && + (value & USB2_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + +static int tegra210_hsic_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + 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 tegra_xusb_lane *lane) +{ + 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 bool tegra210_hsic_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index)) && + (value & USB2_HSIC_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + +#define padctl_pmc_readl(_priv, _offset) \ +({ \ + u32 value; \ + WARN(regmap_read(_priv->regmap, _offset, &value), "read %s failed\n", #_offset);\ + value; \ +}) + +#define padctl_pmc_writel(_priv, _value, _offset) \ + WARN(regmap_write(_priv->regmap, _offset, _value), "write %s failed\n", #_offset) + +static int tegra210_pmc_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + 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, tctrl, pctrl, rpd_ctrl; + + if (!priv->regmap) + return -EOPNOTSUPP; + + if (speed > USB_SPEED_HIGH) + return -EINVAL; + + dev_dbg(dev, "phy enable sleepwalk usb2 %d speed %d\n", port, speed); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + tctrl = TCTRL_VALUE(value); + pctrl = PCTRL_VALUE(value); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); + rpd_ctrl = RPD_CTRL_VALUE(value); + + /* ensure sleepwalk logic is disabled */ + 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)); + + /* ensure sleepwalk logics are in low power mode */ + value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + value |= UTMIP_PWR(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + value &= ~UTMIP_LINE_DEB_CNT(~0); + value |= UTMIP_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_UTMIP_UHSIC_FAKE(port)); + value &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) | + UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_FAKE(port)); + + /* ensure wake events of sleepwalk logic are not latched */ + 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); + + /* 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 pad */ + 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); + + /* save state per speed */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + value &= ~SPEED(port, ~0); + if (speed == USB_SPEED_HIGH) + value |= UTMI_HS(port); + else if (speed == USB_SPEED_FULL) + value |= UTMI_FS(port); + else if (speed == USB_SPEED_LOW) + value |= UTMI_LS(port); + else + value |= UTMI_RST(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + + /* enable the trigger of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + value |= UTMIP_LINEVAL_WALK_EN(port); + padctl_pmc_writel(priv, value, 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 + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | + UTMIP_CAP_CFG(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + /* program electrical parameters read from XUSB PADCTL */ + value = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG); + value &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0)); + value |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl)); + padctl_pmc_writel(priv, value, PMC_UTMIP_TERM_PAD_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port)); + value &= ~RPD_CTRL_PX(~0); + value |= RPD_CTRL_PX(rpd_ctrl); + padctl_pmc_writel(priv, value, 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. + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port)); + value = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | + UTMIP_USBOP_RPD_D); + value |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | + UTMIP_USBON_RPD_D); + if (speed == USB_SPEED_UNKNOWN) { + value |= (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 */ + value |= UTMIP_HIGHZ_A; + value |= UTMIP_AP_A; + value |= (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 */ + value |= UTMIP_HIGHZ_A; + value |= UTMIP_AN_A; + value |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D); + } + padctl_pmc_writel(priv, value, PMC_UTMIP_SLEEPWALK_PX(port)); + + /* power up the line state detectors of the pad */ + 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); + + usleep_range(50, 100); + + /* switch the electric control of the USB2.0 pad to PMC */ + 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); + + /* set the wake signaling trigger events */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_WAKE_VAL(port, ~0); + value |= UTMIP_WAKE_VAL_ANY(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* enable 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); + + return 0; +} + +static int tegra210_pmc_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + 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; + + if (!priv->regmap) + return -EOPNOTSUPP; + + 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; +} + +static int tegra210_pmc_hsic_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + 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; + + if (!priv->regmap) + return -EOPNOTSUPP; + + 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; +} + +static int tegra210_pmc_hsic_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + 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; + + if (!priv->regmap) + return -EOPNOTSUPP; + + 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) { @@ -986,6 +1808,11 @@ static void tegra210_usb2_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_usb2_lane_ops = { .probe = tegra210_usb2_lane_probe, .remove = tegra210_usb2_lane_remove, + .enable_phy_sleepwalk = tegra210_pmc_utmi_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_pmc_utmi_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_utmi_enable_phy_wake, + .disable_phy_wake = tegra210_utmi_disable_phy_wake, + .remote_wake_detected = tegra210_utmi_phy_remote_wake_detected, }; static int tegra210_usb2_phy_init(struct phy *phy) @@ -1449,6 +2276,11 @@ static void tegra210_hsic_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_hsic_lane_ops = { .probe = tegra210_hsic_lane_probe, .remove = tegra210_hsic_lane_remove, + .enable_phy_sleepwalk = tegra210_pmc_hsic_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_pmc_hsic_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_hsic_enable_phy_wake, + .disable_phy_wake = tegra210_hsic_disable_phy_wake, + .remote_wake_detected = tegra210_hsic_phy_remote_wake_detected, }; static int tegra210_hsic_phy_init(struct phy *phy) @@ -1879,6 +2711,11 @@ static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = { .remove = tegra210_pcie_lane_remove, .iddq_enable = tegra210_uphy_lane_iddq_enable, .iddq_disable = tegra210_uphy_lane_iddq_disable, + .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_usb3_enable_phy_wake, + .disable_phy_wake = tegra210_usb3_disable_phy_wake, + .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected, }; static int tegra210_pcie_phy_init(struct phy *phy) @@ -2044,6 +2881,11 @@ static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = { .remove = tegra210_sata_lane_remove, .iddq_enable = tegra210_uphy_lane_iddq_enable, .iddq_disable = tegra210_uphy_lane_iddq_disable, + .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_usb3_enable_phy_wake, + .disable_phy_wake = tegra210_usb3_disable_phy_wake, + .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected, }; static int tegra210_sata_phy_init(struct phy *phy) @@ -2293,6 +3135,8 @@ tegra210_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { struct tegra210_xusb_padctl *padctl; + struct device_node *np; + struct platform_device *pdev; int err; padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); @@ -2306,6 +3150,26 @@ tegra210_xusb_padctl_probe(struct device *dev, if (err < 0) return ERR_PTR(err); + np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0); + if (!np) { + dev_warn(dev, "nvidia,pmc property is missing\n"); + goto out; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + dev_warn(dev, "PMC device is not available\n"); + goto out; + } + + if (!device_is_bound(&pdev->dev)) + return ERR_PTR(-EPROBE_DEFER); + + padctl->regmap = dev_get_regmap(&pdev->dev, "usb_sleepwalk"); + if (!padctl->regmap) + dev_info(dev, "pmc regmap is not available.\n"); + +out: return &padctl->base; } @@ -2313,9 +3177,75 @@ 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); + struct tegra_xusb_lane *lane; + + 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); + + list_for_each_entry(lane, &padctl->lanes, list) { + if (lane->pad->ops->iddq_enable) + tegra210_uphy_lane_iddq_enable(lane); + } + + padctl_writel(padctl, priv->context.usb3_pad_mux, + XUSB_PADCTL_USB3_PAD_MUX); + + list_for_each_entry(lane, &padctl->lanes, list) { + if (lane->pad->ops->iddq_disable) + tegra210_uphy_lane_iddq_disable(lane); + } +} + +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, From patchwork Thu Mar 25 16:40:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164589 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 C6B7FC433DB for ; Thu, 25 Mar 2021 16:41:30 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 75E6661A1E for ; Thu, 25 Mar 2021 16:41:30 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 75E6661A1E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=eGTNKawADOt/w+PDa2S1dS3+S4tUPo4ys7UO2/cepZ0=; b=AQRt98WxJ1q3w8WUIu2e2kcY1 pd2aehV6pIK3hmiD0Tk7XLjpSzqfcdKw0sbgeZiTtJgFrXyE++mYFWDK8dEdAzKVqqHNcMcGAXDwh LpBmff0DIPG3LJonEUslNbNeWnNvMFPL/sMwhM8NY02iX7a6q3377u8o4SsS+JukEoXoLCX/f14Sq ndRvXXlTC0pr1WY4XPlXxkORBGtojxw4UxE6YcmHyEHPeQqM+IvSBNNNK4h8XhSwij8RFs31WfuLJ pIAExxqgD7qZRE6A3Rn6J+NQgurpbjxT2sIdyMYoccgfTczHhuw2KoQHHqhR99rSuuYA4wSH1C/kf TKjKSPjQw==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT33-001qNw-0k; Thu, 25 Mar 2021 16:41:29 +0000 Received: from mail-wm1-x335.google.com ([2a00:1450:4864:20::335]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT2z-001qNQ-BR for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:27 +0000 Received: by mail-wm1-x335.google.com with SMTP id n11-20020a05600c4f8bb029010e5cf86347so4665448wmq.1 for ; Thu, 25 Mar 2021 09:41:25 -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 :mime-version:content-transfer-encoding; bh=+mgUd3FfBG5V1WiGiq7Sg4bslUVk/BI3WMScbkkNnvo=; b=HDU9cNu0XKgix9QOGNKr3nLH70tx8Q2u5VwwF7ZMJ3ce5mq0PCs8Pv97HWyNwjeXGR RxBOyzvnY7d3oztATGgWrUCzWHhdHgMJ+eO02W/3xsCZ8QCVf9z9K1nqg/5tpHN9Z4tE t2eoxQLNO+KbxhTu3CArygjVjJ4vxUe/b8whrkEeKE3FqQsS3Hkhyr0u1czxHzAsoWCd hI4u5/bdf4IoTewIn1k2DLFGKhp45dU/3U3y7mBk9+MPcdfCU+oTcCCuRaADPQ8O5/I7 0U50ulzmGzBiBzNQ0V2rtxqpVOakvqQcpwVh07B46hC/mFlFvzX8fqNFADxhUUhQRCF4 qidA== 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:mime-version:content-transfer-encoding; bh=+mgUd3FfBG5V1WiGiq7Sg4bslUVk/BI3WMScbkkNnvo=; b=VaOlG6zi37Fg6/2YwYHDWLWKf9WjIAESe6QItwaNsBJBWzZB639rUrGVGdI7QerroM CjNXSKPtoH3XZaosQxpQIos2SW/+BIqZKjnIwW91FH7/rg7/igRyAp0s0yN88IpJATYE zXnNOlNX3WBgYsnygzCl8sPhqKJaYW7r50wH9essbxNwfssWQ5h1BGt+mOKtDiFhQbW7 AidzzBt7yz2Vlb7DS1MFVhOraxixgF22asl5IemZeQO7wwhAvY3dxp6hNobCZu5eIYsx ba/gEk2yubwVQ1Bon4/lwfSIQ7+Hcp+o6tE44FVous78SHuEh7Rv0fkyMtOSX1JsXFtZ b0Qg== X-Gm-Message-State: AOAM530OpjpfyEiIHrAWHxSsElvxHWr3QI4UEavSmbVgZdlixZpGjQ0l HxSX2/U6CbXHHfq4cP73YIE= X-Google-Smtp-Source: ABdhPJwecxKmDPs8sTv0ICfk8y8FLcz6o1BYpe4/XmBXW2UfZiktm5sFyZ8X94fBst1OiLGjH2miEg== X-Received: by 2002:a1c:f614:: with SMTP id w20mr8746936wmc.70.1616690485017; Thu, 25 Mar 2021 09:41:25 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id n3sm7141949wmi.7.2021.03.25.09.41.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:23 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 10/13] phy: tegra: xusb: Tegra210 host mode VBUS control Date: Thu, 25 Mar 2021 17:40:54 +0100 Message-Id: <20210325164057.793954-11-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164125_608678_6548FDF5 X-CRM114-Status: GOOD ( 14.31 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo To support XUSB host controller ELPG, this commit moves VBUS control .phy_power_on()/.phy_power_off() to .phy_init()/.phy_exit(). When XUSB host controller enters ELPG, host driver invokes .phy_power_off(), VBUS should remain ON so that USB devices will not disconnect. VBUS can be turned OFF when host driver invokes .phy_exit() which indicates disabling a USB port. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding Acked-By: Vinod Koul --- drivers/phy/tegra/xusb-tegra210.c | 52 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 8af73ba78ad7..9d39f812fb43 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -1819,8 +1819,25 @@ 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; + } + + if (port->supply && port->mode == USB_DR_MODE_HOST) { + 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); @@ -1828,11 +1845,30 @@ 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; + struct tegra_xusb_usb2_port *port; + int err; + + port = tegra_xusb_find_usb2_port(padctl, lane->index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", lane->index); + return -ENODEV; + } + + if (port->supply && port->mode == USB_DR_MODE_HOST) { + err = regulator_disable(port->supply); + if (err) + return err; + } + return 0; } @@ -1953,6 +1989,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( @@ -2046,14 +2084,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); @@ -2062,7 +2092,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 << @@ -2094,8 +2124,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; } @@ -2154,7 +2183,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); out: - regulator_disable(port->supply); mutex_unlock(&padctl->lock); return 0; } From patchwork Thu Mar 25 16:40:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164593 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, 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 0761AC433C1 for ; Thu, 25 Mar 2021 16:41:37 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 85AB5619BF for ; Thu, 25 Mar 2021 16:41:36 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 85AB5619BF Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=oUJExz+DDY4ueRUmOCzybBXCWf5iZTf+UlpIiwLz6Ts=; b=iF13D7ZUHwwdHQWhw6r8bEzR7 PV465Aqdcyo/4Fpb8vdhYQH824jwiiuX1ycxUTXhX2um7VKoCkFfh5Gba9gzqInVct8Zni5vJToXS lwmAJioytwdSrYAEDQOXKsyTRdWTMewcE0O2EQ4g0pFdaMzvywSxPR3JRnYj1Uvque7gAFAucHnxz lm2yDPWQFFyWe5RlIi5NIC7GPIg9fGR9P5bso/X1dCWKl8itEQCbLgln5JN20sv2qgwD7qwnutqnC XkbG/PkhL98Ji20t2TO57W7xktlsaa9Ovv4lzfChLqyZquQz8vLgRgooSqG1Kgz8BLOWdwncDJdXh rTGRwCCHg==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT38-001qOP-Gf; Thu, 25 Mar 2021 16:41:34 +0000 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT33-001qO3-Om for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:32 +0000 Received: by mail-wm1-x32f.google.com with SMTP id t5-20020a1c77050000b029010e62cea9deso1566138wmi.0 for ; Thu, 25 Mar 2021 09:41:29 -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 :mime-version:content-transfer-encoding; bh=FzV3zBMv51XDLkh/8s4gze+scJ88wDHClM0XU2WDdxM=; b=d511zFVk8j3ZPT2YMgzSDlAgxvt+KdY70K4GnhOKZF4x8z4a7gEPFPnrMmQ8lqmeRT +/ZR0kohLKObwcN3azYl4nl7NdFuKCnriGiZz6OlzKAcHKZb0yM+mFeXbYuYam3ZdfGx mzV4hcOS7YV0sNGiTrg561mmEObKO7cyCTjkXCLBcBwjtcLMu4z58eDykALWtSvKof3u zIgt7SlYYRHzYqBmRqzs0/Fom8//9OYH3OjCyAN3MqYTqqszfhs2ECLJPZR7DpDKHwRW gpVbtX66LcNrkHowPRuGvnRbiShGWsnVTgAixvZ0YBJ0NBIwR7DdOVqnVVN+xmvCwAiZ 7znw== 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:mime-version:content-transfer-encoding; bh=FzV3zBMv51XDLkh/8s4gze+scJ88wDHClM0XU2WDdxM=; b=SkxnN2Ysqzqa33bfaSmHbsSvO0uBltLW+tFt6zli2gnUHCHZalzqGuLxTnrLsG8nRE OIKo2GmYPgFW0y3+miyMsE6wdojj8MtZ6/V32cr8+f51l6hhShR45j01V/wqVhiBBebG uQ92L0rJ/sjEptmAx9h5rnoRxVfkfKerPGS67pHbYxIUEMc6bsTZsBqeApUbV391Srim gaL7LcNC9KKSGgzAAtEgqxBu6j74rfHKgOJJtbTscRt+9olll08/7fMbrUNAqx1QcWX5 iTAxhft+rFKMR+c+ZLlnmewu1sIKwJiEFcbgVvWV3rvhELTeJPdHzQy/edPmMoDBa66B tiCg== X-Gm-Message-State: AOAM532diRjWGkMNB84+/GZ+IcYnw6Ymdn9Ln15A6kRAaYh0V6uSCals ZDjTYRfQ3ZQOdOkFQ/C9nqa71rKJTNk= X-Google-Smtp-Source: ABdhPJwygLbH8aj/ylrr7WQLf44ym9QnSIYD9c2QPr6eXWsjvTXLkOMfG2zx+3SC47GQTEl5kTtDpQ== X-Received: by 2002:a1c:9a09:: with SMTP id c9mr8655136wme.172.1616690489315; Thu, 25 Mar 2021 09:41:29 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id c2sm7213995wmr.22.2021.03.25.09.41.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:26 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 11/13] phy: tegra: xusb: Add wake/sleepwalk for Tegra186 Date: Thu, 25 Mar 2021 17:40:55 +0100 Message-Id: <20210325164057.793954-12-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164129_901392_DCFC19DD X-CRM114-Status: GOOD ( 21.59 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo This commit implements Tegra186/Tegra194 XUSB PADCTL/AO wake and sleepwalk operations. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding Acked-By: Vinod Koul --- drivers/phy/tegra/xusb-tegra186.c | 558 +++++++++++++++++++++++++++++- 1 file changed, 557 insertions(+), 1 deletion(-) diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c index 5d64f69b39a9..6378bf722745 100644 --- a/drivers/phy/tegra/xusb-tegra186.c +++ b/drivers/phy/tegra/xusb-tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. */ #include @@ -113,6 +113,117 @@ #define ID_OVERRIDE_FLOATING ID_OVERRIDE(8) #define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0) +/* XUSB AO registers */ +#define XUSB_AO_USB_DEBOUNCE_DEL (0x4) +#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4) +#define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf) + +#define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4) +#define CLR_WALK_PTR (1 << 0) +#define CAP_CFG (1 << 1) +#define CLR_WAKE_ALARM (1 << 3) + +#define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4) +#define HSIC_CLR_WALK_PTR (1 << 0) +#define HSIC_CLR_WAKE_ALARM (1 << 3) +#define HSIC_CAP_CFG (1 << 4) + +#define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4) +#define SPEED(x) ((x) & 0x3) +#define UTMI_HS SPEED(0) +#define UTMI_FS SPEED(1) +#define UTMI_LS SPEED(2) +#define UTMI_RST SPEED(3) + +#define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4) +#define MODE(x) ((x) & 0x1) +#define MODE_HS MODE(0) +#define MODE_RST MODE(1) + +#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4) +#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4) +#define FAKE_USBOP_VAL (1 << 0) +#define FAKE_USBON_VAL (1 << 1) +#define FAKE_USBOP_EN (1 << 2) +#define FAKE_USBON_EN (1 << 3) +#define FAKE_STROBE_VAL (1 << 0) +#define FAKE_DATA_VAL (1 << 1) +#define FAKE_STROBE_EN (1 << 2) +#define FAKE_DATA_EN (1 << 3) +#define WAKE_WALK_EN (1 << 14) +#define MASTER_ENABLE (1 << 15) +#define LINEVAL_WALK_EN (1 << 16) +#define WAKE_VAL(x) (((x) & 0xf) << 17) +#define WAKE_VAL_NONE WAKE_VAL(12) +#define WAKE_VAL_ANY WAKE_VAL(15) +#define WAKE_VAL_DS10 WAKE_VAL(2) +#define LINE_WAKEUP_EN (1 << 21) +#define MASTER_CFG_SEL (1 << 22) + +#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4) +/* phase A */ +#define USBOP_RPD_A (1 << 0) +#define USBON_RPD_A (1 << 1) +#define AP_A (1 << 4) +#define AN_A (1 << 5) +#define HIGHZ_A (1 << 6) +/* phase B */ +#define USBOP_RPD_B (1 << 8) +#define USBON_RPD_B (1 << 9) +#define AP_B (1 << 12) +#define AN_B (1 << 13) +#define HIGHZ_B (1 << 14) +/* phase C */ +#define USBOP_RPD_C (1 << 16) +#define USBON_RPD_C (1 << 17) +#define AP_C (1 << 20) +#define AN_C (1 << 21) +#define HIGHZ_C (1 << 22) +/* phase D */ +#define USBOP_RPD_D (1 << 24) +#define USBON_RPD_D (1 << 25) +#define AP_D (1 << 28) +#define AN_D (1 << 29) +#define HIGHZ_D (1 << 30) + +#define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4) +/* phase A */ +#define RPD_STROBE_A (1 << 0) +#define RPD_DATA0_A (1 << 1) +#define RPU_STROBE_A (1 << 2) +#define RPU_DATA0_A (1 << 3) +/* phase B */ +#define RPD_STROBE_B (1 << 8) +#define RPD_DATA0_B (1 << 9) +#define RPU_STROBE_B (1 << 10) +#define RPU_DATA0_B (1 << 11) +/* phase C */ +#define RPD_STROBE_C (1 << 16) +#define RPD_DATA0_C (1 << 17) +#define RPU_STROBE_C (1 << 18) +#define RPU_DATA0_C (1 << 19) +/* phase D */ +#define RPD_STROBE_D (1 << 24) +#define RPD_DATA0_D (1 << 25) +#define RPU_STROBE_D (1 << 26) +#define RPU_DATA0_D (1 << 27) + +#define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4) +#define FSLS_USE_XUSB_AO (1 << 3) +#define TRK_CTRL_USE_XUSB_AO (1 << 4) +#define RPD_CTRL_USE_XUSB_AO (1 << 5) +#define RPU_USE_XUSB_AO (1 << 6) +#define VREG_USE_XUSB_AO (1 << 7) +#define USBOP_VAL_PD (1 << 8) +#define USBON_VAL_PD (1 << 9) +#define E_DPD_OVRD_EN (1 << 10) +#define E_DPD_OVRD_VAL (1 << 11) + +#define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4) +#define STROBE_VAL_PD (1 << 0) +#define DATA0_VAL_PD (1 << 1) +#define USE_XUSB_AO (1 << 4) + #define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \ { \ .name = _name, \ @@ -130,16 +241,41 @@ struct tegra_xusb_fuse_calibration { u32 rpd_ctrl; }; +struct tegra186_xusb_padctl_context { + u32 vbus_id; + u32 usb2_pad_mux; + u32 usb2_port_cap; + u32 ss_port_cap; +}; + struct tegra186_xusb_padctl { struct tegra_xusb_padctl base; + void __iomem *ao_regs; struct tegra_xusb_fuse_calibration calib; /* UTMI bias and tracking */ struct clk *usb2_trk_clk; unsigned int bias_pad_enable; + + /* padctl context */ + struct tegra186_xusb_padctl_context context; }; +static inline void ao_writel(struct tegra186_xusb_padctl *priv, u32 value, unsigned int offset) +{ + dev_dbg(priv->base.dev, "ao %08x < %08x\n", offset, value); + writel(value, priv->ao_regs + offset); +} + +static inline u32 ao_readl(struct tegra186_xusb_padctl *priv, unsigned int offset) +{ + u32 value = readl(priv->ao_regs + offset); + + dev_dbg(priv->base.dev, "ao %08x > %08x\n", offset, value); + return value; +} + static inline struct tegra186_xusb_padctl * to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl) { @@ -180,9 +316,256 @@ static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane) kfree(usb2); } +static int tegra186_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy enable sleepwalk UTMI %d speed %d\n", index, speed); + + mutex_lock(&padctl->lock); + + /* ensure sleepwalk logic is disabled */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~MASTER_ENABLE; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* ensure sleepwalk logics are in low power mode */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value |= MASTER_CFG_SEL; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* set debounce time */ + value = ao_readl(priv, XUSB_AO_USB_DEBOUNCE_DEL); + value &= ~UTMIP_LINE_DEB_CNT(~0); + value |= UTMIP_LINE_DEB_CNT(1); + ao_writel(priv, value, XUSB_AO_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL | + FAKE_USBOP_EN | FAKE_USBON_EN); + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* ensure wake events of sleepwalk logic are not latched */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~LINE_WAKEUP_EN; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* disable wake event triggers of sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~WAKE_VAL(~0); + value |= WAKE_VAL_NONE; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* power down the line state detectors of the pad */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value |= (USBOP_VAL_PD | USBON_VAL_PD); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* save state per speed */ + value = ao_readl(priv, XUSB_AO_UTMIP_SAVED_STATE(index)); + value &= ~SPEED(~0); + if (speed == USB_SPEED_HIGH) + value |= UTMI_HS; + else if (speed == USB_SPEED_FULL) + value |= UTMI_FS; + else if (speed == USB_SPEED_LOW) + value |= UTMI_LS; + else + value |= UTMI_RST; + ao_writel(priv, value, XUSB_AO_UTMIP_SAVED_STATE(index)); + + /* enable the trigger of the sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value |= LINEVAL_WALK_EN; + value &= ~WAKE_WALK_EN; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 pad + */ + value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); + value |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG); + ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); + + /* 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. + */ + value = (USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D); + value |= (USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D); + if (speed == USB_SPEED_UNKNOWN) { + value |= (HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D); + } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) { + /* J state: D+/D- = high/low, K state: D+/D- = low/high */ + value |= HIGHZ_A; + value |= (AP_A); + value |= (AN_B | AN_C | AN_D); + } else if (speed == USB_SPEED_LOW) { + /* J state: D+/D- = low/high, K state: D+/D- = high/low */ + value |= HIGHZ_A; + value |= AN_A; + value |= (AP_B | AP_C | AP_D); + } + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK(index)); + + /* power up the line state detectors of the pad */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value &= ~(USBOP_VAL_PD | USBON_VAL_PD); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + usleep_range(150, 200); + + /* switch the electric control of the USB2.0 pad to XUSB_AO */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value |= (FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | + RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* set the wake signaling trigger events */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~WAKE_VAL(~0); + value |= WAKE_VAL_ANY; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* enable the wake detection */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value |= (MASTER_ENABLE | LINE_WAKEUP_EN); + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy disable sleepwalk UTMI %d\n", index); + + mutex_lock(&padctl->lock); + + /* disable the wake detection */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~(MASTER_ENABLE | LINE_WAKEUP_EN); + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* switch the electric control of the USB2.0 pad to XUSB vcore logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | + RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* disable wake event triggers of sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~WAKE_VAL(~0); + value |= WAKE_VAL_NONE; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* power down the line state detectors of the port */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value |= (USBOP_VAL_PD | USBON_VAL_PD); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* clear alarm of the sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); + value |= CLR_WAKE_ALARM; + ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_utmi_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + 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 UTMI %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_utmi_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + 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 UTMI %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra186_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) && + (value & USB2_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = { .probe = tegra186_usb2_lane_probe, .remove = tegra186_usb2_lane_remove, + .enable_phy_sleepwalk = tegra186_utmi_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra186_utmi_disable_phy_sleepwalk, + .enable_phy_wake = tegra186_utmi_enable_phy_wake, + .disable_phy_wake = tegra186_utmi_disable_phy_wake, + .remote_wake_detected = tegra186_utmi_phy_remote_wake_detected, }; static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl) @@ -656,10 +1039,140 @@ static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane) kfree(usb3); } +static int tegra186_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + 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 sleepwalk USB3 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value |= SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value |= SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + usleep_range(250, 350); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + 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 sleepwalk USB3 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value &= ~SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_usb3_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + 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 USB3 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_usb3_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + 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 USB3 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra186_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = { .probe = tegra186_usb3_lane_probe, .remove = tegra186_usb3_lane_remove, + .enable_phy_sleepwalk = tegra186_usb3_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra186_usb3_disable_phy_sleepwalk, + .enable_phy_wake = tegra186_usb3_enable_phy_wake, + .disable_phy_wake = tegra186_usb3_disable_phy_wake, + .remote_wake_detected = tegra186_usb3_phy_remote_wake_detected, }; + static int tegra186_usb3_port_enable(struct tegra_xusb_port *port) { return 0; @@ -913,7 +1426,9 @@ static struct tegra_xusb_padctl * tegra186_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { + struct platform_device *pdev = to_platform_device(dev); struct tegra186_xusb_padctl *priv; + struct resource *res; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -923,6 +1438,11 @@ tegra186_xusb_padctl_probe(struct device *dev, priv->base.dev = dev; priv->base.soc = soc; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao"); + priv->ao_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ao_regs)) + return priv->ao_regs; + err = tegra186_xusb_read_fuse_calibration(priv); if (err < 0) return ERR_PTR(err); @@ -930,6 +1450,40 @@ tegra186_xusb_padctl_probe(struct device *dev, return &priv->base; } +static void tegra186_xusb_padctl_save(struct tegra_xusb_padctl *padctl) +{ + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + + priv->context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID); + 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_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); +} + +static void tegra186_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) +{ + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + + 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_cap, XUSB_PADCTL_SS_PORT_CAP); + padctl_writel(padctl, priv->context.vbus_id, USB2_VBUS_ID); +} + +static int tegra186_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) +{ + tegra186_xusb_padctl_save(padctl); + + return 0; +} + +static int tegra186_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) +{ + tegra186_xusb_padctl_restore(padctl); + + return 0; +} + static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } @@ -937,6 +1491,8 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = { .probe = tegra186_xusb_padctl_probe, .remove = tegra186_xusb_padctl_remove, + .suspend_noirq = tegra186_xusb_padctl_suspend_noirq, + .resume_noirq = tegra186_xusb_padctl_resume_noirq, .vbus_override = tegra186_xusb_padctl_vbus_override, }; From patchwork Thu Mar 25 16:40:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164595 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,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 1A19BC433DB for ; Thu, 25 Mar 2021 16:41:40 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A07A6619BF for ; Thu, 25 Mar 2021 16:41:39 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org A07A6619BF Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=uZDqoJX0X/nxuevVqv+mcu4Js2NhrTD6vNM8oE50BKI=; b=G1zfku6GLVotxOaywZ/N2+ZOV 6SsCFOW9NV6oVlHR/4/uVSZnObFb9Y/afPTz9OGBnY0nZgSiBr7ErKwkHNMZ0QfB9x6JN5P7uIrCB lm3uOkeyjOz8vpH8JAzJ22pkyZQpm3f4Eu3zl1wDud2R+cOctqewx+hiubCX+Mup3UC6+7U+xCdV0 i6CpLUdxsQRgTCqxwjUeDicXRqTcusaLtgzQECxP0SVS897w6a4DKTF3OYzeTAnjIFRsxzyziUSvT EKWMqB9pAvbmcXta3pkZOg6O2sayS3qj3nZCre1So1w8AvW5bRtsT7aMGgizRZ33NDHCn9Zc67Xzn /1vbiqujQ==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT3C-001qOn-3b; Thu, 25 Mar 2021 16:41:38 +0000 Received: from mail-wm1-x333.google.com ([2a00:1450:4864:20::333]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT38-001qOO-Aw for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:36 +0000 Received: by mail-wm1-x333.google.com with SMTP id m20-20020a7bcb940000b029010cab7e5a9fso3466781wmi.3 for ; Thu, 25 Mar 2021 09:41:34 -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 :mime-version:content-transfer-encoding; bh=M7/JLbQJjUSymQX4uhScOBeXFiJb3pKiltILERb+d5I=; b=M9HnA5WWjzJNuB/7uB5CtL4EUy7cJyJNnYQjmW5dRwzoXBsbP5CP6uoymTGta3ZNzB MRN9pYs17bo81ZqAq15s04ecIIQsQcngvtv9xyOqleoLLcv0kCEW+c1EI6gZdi2qwLGh FioDUR9SkFyk7MPNTKd5sqZLX3WvTZzHqRCIgRcA0jGvp2zGxnCnc6BAbmNsz2CU199L an/ae2YrfJRcXIfqs68iUkA4WRHr1+5ZIxUSHW5bj3aRarW7yzt4cdC+xkI4q5QA0uq+ ra3KLXgLHl8wt7loNiBmjZa7KBQAo8k0xqlvg0SvEkSYUaacr7zIANsuuzQkPgHawfdQ cy2w== 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:mime-version:content-transfer-encoding; bh=M7/JLbQJjUSymQX4uhScOBeXFiJb3pKiltILERb+d5I=; b=ltNzCXzkUd3ddmnzfYiqUhpthwHYsRBdZSi261Xp/zQh5bo6X5zQ4Y68h31CBr20bS Y3PN2zlv99/M/UnzexBLJA36Kj7AFt3IiseQ+o+t+FDYlq5xQyLW7pYfeg4ufzono+N8 9U6HruSAQJuTHPJTBOKXYXbWPYBt8CeLzUdOMHyPGH0N1aLqBMcikM2JMJv3BwRoSqKE aa2TWLit5BbaQ9oVVG3woweiXCeIJ+TCbJCY3H8vU8Ke/I5oB9qotnHC3J1xvrmfdB9i MsAwMWVrgQb+/I3pIBMiQ7sCBs6GP3lVBTJb2N9o0CQnpOlno9wDxr27HfYfgzzSCKMU 6a0Q== X-Gm-Message-State: AOAM533bz8QucX2ovMRlGgv9KJdO82uVC0eyiSRafB5aGG51wZZH7xBj Cx/O2dJoKrQV7sy5nl0fJyvxAsm/vVk= X-Google-Smtp-Source: ABdhPJx8N3Yxnfw/JNUeLdh2tYls8aL3hFOM4UkxY2TDsbWWQblrIYTMQLBAsBfHyNicg6gChAeVyA== X-Received: by 2002:a05:600c:364c:: with SMTP id y12mr8971267wmq.96.1616690493669; Thu, 25 Mar 2021 09:41:33 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id p16sm9055793wrt.54.2021.03.25.09.41.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:30 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 12/13] usb: host: xhci-tegra: Unlink power domain devices Date: Thu, 25 Mar 2021 17:40:56 +0100 Message-Id: <20210325164057.793954-13-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164134_527906_CC31AE13 X-CRM114-Status: GOOD ( 20.93 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo 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 Signed-off-by: Thierry Reding --- drivers/usb/host/xhci-tegra.c | 206 ++++++++++++++++++---------------- 1 file changed, 112 insertions(+), 94 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 50bb91b6a4b8..5b39a739f8f0 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2,7 +2,7 @@ /* * NVIDIA Tegra xHCI host controller driver * - * Copyright (C) 2014 NVIDIA Corporation + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. * Copyright (C) 2014 Google, Inc. */ @@ -249,8 +249,7 @@ 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; + bool use_genpd; struct phy **phys; unsigned int num_phys; @@ -821,36 +820,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 @@ -1026,10 +1001,9 @@ 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 (!tegra->use_genpd) + return; + 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)) @@ -1055,20 +1029,84 @@ 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; + tegra->use_genpd = true; + + return 0; +} + +static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra) +{ + struct device *dev = tegra->dev; + int rc; + + if (tegra->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; + int rc; + + if (tegra->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; @@ -1432,25 +1470,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) @@ -1525,10 +1544,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; } /* @@ -1547,30 +1578,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); @@ -1622,24 +1645,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; @@ -1664,15 +1684,13 @@ 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_powerdomain_remove(&pdev->dev, tegra); - } + tegra_xusb_powergate_partitions(tegra); - tegra_xusb_phy_disable(tegra); + 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; From patchwork Thu Mar 25 16:40:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 12164597 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.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, 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 9386EC433DB for ; Thu, 25 Mar 2021 16:41:45 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1BF4761A25 for ; Thu, 25 Mar 2021 16:41:45 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1BF4761A25 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=WYBxzyGxWo2gbIWaeNn60BTQd0AoZslFS+DJxWopRCA=; b=aZ2yUKdoOzaKdmsiPB8EBVd9l gKjClQAKhMAXfe9YSOEqQtTC5bVzQNAkcbZGQvQ4Egi/7WH45cDJ3UbDBV+gT7LAOhylA8Mlbege5 TmZDcMJE6HzoRHIurBqaDYMvN34wVgJsE1/Xhm4DCZ4HtjbCrPwNhxHvQLmLUcIHkuyVsOopDnHPW Jn2iEZeM+H3f3h6juzxpalqyt3uxQU67+0t3U+SjO7H2vye9W158KUKEk0nyTd2ASXLyL+gxzjb8Z fYVbOc9i91IsVR3JdvI/u9eqtECL+yfM4wvfyxpSao3o30tXTom4j1GFkoDdRuhAQsVqCRkF1j57G WTA+M1L5Q==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lPT3H-001qPJ-J1; Thu, 25 Mar 2021 16:41:43 +0000 Received: from mail-wm1-x32b.google.com ([2a00:1450:4864:20::32b]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lPT3C-001qOo-IV for linux-phy@lists.infradead.org; Thu, 25 Mar 2021 16:41:41 +0000 Received: by mail-wm1-x32b.google.com with SMTP id n11-20020a05600c4f8bb029010e5cf86347so4665832wmq.1 for ; Thu, 25 Mar 2021 09:41:38 -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 :mime-version:content-transfer-encoding; bh=9Q+j1GqCZFi4+qQgCx0LC4bT27WTzGH7CBTvodsUZHM=; b=ojuuu4gBe1k4o/+sr7yukFJHJbsZy5104xR/oYR0V5NV1T6SaK+NHj+bW+gYOlQXUL Y8W56frzfTLS9IyE8nVpfmKxlqSN3D+EIrP0DF8vxd9rnB8qDT12H9yLIEOekpdAKJxv 38cccd5Eucn488MnG5YoI7ET86x3TPypbMDaklHU3gTFyPGdAyKVpq+209NEP2s2sfQU 1agzxCTrdqKcPG9pweyrKJd5eud2Fsgdgl4c+KJ9tlsg9i4dYLNZCiYeiiC/gieNkw6f b1Y1yNa/IOwiprMx3B5nc2vKm7zWqY/U66KhRU5EGzELO8EmF8gy6TFe5Lg07qu7+80v CzGQ== 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:mime-version:content-transfer-encoding; bh=9Q+j1GqCZFi4+qQgCx0LC4bT27WTzGH7CBTvodsUZHM=; b=md7SvSWsymtvza7Gnbr0oZg+C6YzTs7RvjN9yv61oMpaO49P3XN2X1V/ACfl5amte1 wXlK1UKlQ8ssylPN0Xix9nafLaTvGNNuUZUfb18jchKFE0t8E2JX29PXEiqVCzgcS/2l nSx8Y70zW3rv/vyUExhmilUU9EJeEPkOJD0cIOZVLRy3/Ydm7QCK2c5i7olZ42L+dl9h Oq/D2F9lp1+keNlzIosmJ1VEp0eA/bKkwbZbQlh/nKFb7NEvWpxa8kXa2VzrXBSoy+Ur Xb3qfRs/usxW5pEDgaF0f53JAoFJKzrdUg+aBh3PZwN36DetRtsqWPPZqZCph7sSIoKf yc9A== X-Gm-Message-State: AOAM5300WPTF8L4CERIjdqsfmPVHR98p6e1hPfMb6gd2hbdMpPGKFm+5 BJoL/EvA331SKeLjbGLYqrI= X-Google-Smtp-Source: ABdhPJwoDL4en4zYpNM492n/Dxi+3ot61NbBHPTlye3ZpuB2n9TQsemYo/A31qPhpAwZ820G66g1bw== X-Received: by 2002:a1c:f406:: with SMTP id z6mr9130449wma.24.1616690497885; Thu, 25 Mar 2021 09:41:37 -0700 (PDT) Received: from localhost ([62.96.65.119]) by smtp.gmail.com with ESMTPSA id g15sm6440468wmq.31.2021.03.25.09.41.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Mar 2021 09:41:34 -0700 (PDT) From: Thierry Reding To: Vinod Koul , Greg Kroah-Hartman Cc: Kishon Vijay Abraham I , Mathias Nyman , JC Kuo , Jon Hunter , linux-tegra@vger.kernel.org, linux-phy@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v8 13/13] xhci: tegra: Enable ELPG for runtime/system PM Date: Thu, 25 Mar 2021 17:40:57 +0100 Message-Id: <20210325164057.793954-14-thierry.reding@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210325164057.793954-1-thierry.reding@gmail.com> References: <20210325164057.793954-1-thierry.reding@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210325_164138_684861_07F0029C X-CRM114-Status: GOOD ( 26.46 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org From: JC Kuo This commit implements the complete programming sequence for ELPG entry and exit. 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk() and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL sleepwalk and wake detection circuits to maintain USB lines level and respond to wake events (wake-on-connect, wake-on-disconnect, device-initiated-wake). 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk() and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and wake detection circuits. At runtime suspend, XUSB host controller can enter ELPG to reduce power consumption. When XUSB PADCTL wake detection circuit detects a wake event, an interrupt will be raised. xhci-tegra driver then will invoke pm_runtime_resume() for xhci-tegra. Runtime resume could also be triggered by protocol drivers, this is the host-initiated-wake event. At runtime resume, xhci-tegra driver brings XUSB host controller out of ELPG to handle the wake events. The same ELPG enter/exit procedure will be performed for system suspend/resume path so USB devices can remain connected across SC7. Signed-off-by: JC Kuo Signed-off-by: Thierry Reding --- drivers/usb/host/xhci-tegra.c | 407 ++++++++++++++++++++++++++++++---- 1 file changed, 370 insertions(+), 37 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 5b39a739f8f0..ce97ff054c68 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -224,6 +226,7 @@ struct tegra_xusb { int xhci_irq; int mbox_irq; + int padctl_irq; void __iomem *ipfs_base; void __iomem *fpci_base; @@ -269,6 +272,7 @@ struct tegra_xusb { dma_addr_t phys; } fw; + bool suspended; struct tegra_xusb_context context; }; @@ -665,6 +669,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) mutex_lock(&tegra->lock); + if (pm_runtime_suspended(tegra->dev) || tegra->suspended) + goto out; + value = fpci_readl(tegra, tegra->soc->mbox.data_out); tegra_xusb_mbox_unpack(&msg, value); @@ -678,6 +685,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) tegra_xusb_mbox_handle(tegra, &msg); +out: mutex_unlock(&tegra->lock); return IRQ_HANDLED; } @@ -818,16 +826,6 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra) } } -static int tegra_xusb_runtime_suspend(struct device *dev) -{ - return 0; -} - -static int tegra_xusb_runtime_resume(struct device *dev) -{ - return 0; -} - #ifdef CONFIG_PM_SLEEP static int tegra_xusb_init_context(struct tegra_xusb *tegra) { @@ -1128,6 +1126,24 @@ static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) return err; } +static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data) +{ + struct tegra_xusb *tegra = data; + + mutex_lock(&tegra->lock); + + if (tegra->suspended) { + mutex_unlock(&tegra->lock); + return IRQ_HANDLED; + } + + mutex_unlock(&tegra->lock); + + pm_runtime_resume(tegra->dev); + + return IRQ_HANDLED; +} + static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) { int err; @@ -1251,6 +1267,52 @@ static void tegra_xhci_id_work(struct work_struct *work) } } +#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP) +static bool is_usb2_otg_phy(struct tegra_xusb *tegra, unsigned int index) +{ + return (tegra->usbphy[index] != NULL); +} + +static bool is_usb3_otg_phy(struct tegra_xusb *tegra, unsigned int index) +{ + struct tegra_xusb_padctl *padctl = tegra->padctl; + unsigned int i; + int port; + + for (i = 0; i < tegra->num_usb_phys; i++) { + if (is_usb2_otg_phy(tegra, i)) { + port = tegra_xusb_padctl_get_usb3_companion(padctl, i); + if ((port >= 0) && (index == (unsigned int)port)) + return true; + } + } + + return false; +} + +static bool is_host_mode_phy(struct tegra_xusb *tegra, unsigned int phy_type, unsigned int index) +{ + if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0) + return true; + + if (strcmp(tegra->soc->phy_types[phy_type].name, "usb2") == 0) { + if (is_usb2_otg_phy(tegra, index)) + return ((index == tegra->otg_usb2_port) && tegra->host_mode); + else + return true; + } + + if (strcmp(tegra->soc->phy_types[phy_type].name, "usb3") == 0) { + if (is_usb3_otg_phy(tegra, index)) + return ((index == tegra->otg_usb3_port) && tegra->host_mode); + else + return true; + } + + return false; +} +#endif + static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra, struct usb_phy *usbphy) { @@ -1343,6 +1405,7 @@ static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra) static int tegra_xusb_probe(struct platform_device *pdev) { struct tegra_xusb *tegra; + struct device_node *np; struct resource *regs; struct xhci_hcd *xhci; unsigned int i, j, k; @@ -1390,6 +1453,14 @@ static int tegra_xusb_probe(struct platform_device *pdev) if (IS_ERR(tegra->padctl)) return PTR_ERR(tegra->padctl); + np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0); + if (!np) + return -ENODEV; + + tegra->padctl_irq = of_irq_get(np, 0); + if (tegra->padctl_irq <= 0) + return (tegra->padctl_irq == 0) ? -ENODEV : tegra->padctl_irq; + tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host"); if (IS_ERR(tegra->host_clk)) { err = PTR_ERR(tegra->host_clk); @@ -1534,6 +1605,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_powerdomains; } + tegra->hcd->skip_phy_initialization = 1; tegra->hcd->regs = tegra->regs; tegra->hcd->rsrc_start = regs->start; tegra->hcd->rsrc_len = resource_size(regs); @@ -1616,12 +1688,6 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_usb3; } - err = tegra_xusb_enable_firmware_messages(tegra); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable messages: %d\n", err); - goto remove_usb3; - } - err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq, tegra_xusb_mbox_irq, tegra_xusb_mbox_thread, 0, @@ -1631,12 +1697,36 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto remove_usb3; } + err = devm_request_threaded_irq(&pdev->dev, tegra->padctl_irq, NULL, tegra_xusb_padctl_irq, + IRQF_ONESHOT, dev_name(&pdev->dev), tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to request padctl IRQ: %d\n", err); + goto remove_usb3; + } + + err = tegra_xusb_enable_firmware_messages(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable messages: %d\n", err); + goto remove_usb3; + } + err = tegra_xusb_init_usb_phy(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err); goto remove_usb3; } + /* Enable wake for both USB 2.0 and USB 3.0 roothubs */ + device_init_wakeup(&tegra->hcd->self.root_hub->dev, true); + device_init_wakeup(&xhci->shared_hcd->self.root_hub->dev, true); + device_init_wakeup(tegra->dev, true); + + pm_runtime_use_autosuspend(tegra->dev); + pm_runtime_set_autosuspend_delay(tegra->dev, 2000); + pm_runtime_mark_last_busy(tegra->dev); + pm_runtime_set_active(tegra->dev); + pm_runtime_enable(tegra->dev); + return 0; remove_usb3: @@ -1672,6 +1762,7 @@ static int tegra_xusb_remove(struct platform_device *pdev) tegra_xusb_deinit_usb_phy(tegra); + pm_runtime_get_sync(&pdev->dev); usb_remove_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd); xhci->shared_hcd = NULL; @@ -1681,8 +1772,8 @@ static int tegra_xusb_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt, tegra->fw.phys); - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put(&pdev->dev); tegra_xusb_powergate_partitions(tegra); @@ -1696,7 +1787,7 @@ static int tegra_xusb_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP +#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP) static bool xhci_hub_ports_suspended(struct xhci_hub *hub) { struct device *dev = hub->hcd->self.controller; @@ -1722,9 +1813,17 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub) static int tegra_xusb_check_ports(struct tegra_xusb *tegra) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct xhci_bus_state *bus_state = &xhci->usb2_rhub.bus_state; unsigned long flags; int err = 0; + if (bus_state->bus_suspended) { + /* xusb_hub_suspend() has just directed one or more USB2 port(s) + * to U3 state, it takes 3ms to enter U3. + */ + usleep_range(3000, 4000); + } + spin_lock_irqsave(&xhci->lock, flags); if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) || @@ -1770,45 +1869,186 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra) } } -static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup) +static enum usb_device_speed tegra_xhci_portsc_to_speed(struct tegra_xusb *tegra, u32 portsc) +{ + if (DEV_LOWSPEED(portsc)) + return USB_SPEED_LOW; + + if (DEV_HIGHSPEED(portsc)) + return USB_SPEED_HIGH; + + if (DEV_FULLSPEED(portsc)) + return USB_SPEED_FULL; + + if (DEV_SUPERSPEED_ANY(portsc)) + return USB_SPEED_SUPER; + + return USB_SPEED_UNKNOWN; +} + +static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra) { + struct tegra_xusb_padctl *padctl = tegra->padctl; struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + enum usb_device_speed speed; + struct phy *phy; + unsigned int index, offset; + unsigned int i, j, k; + struct xhci_hub *rhub; + u32 portsc; + + for (i = 0, k = 0; i < tegra->soc->num_types; i++) { + if (strcmp(tegra->soc->phy_types[i].name, "usb3") == 0) + rhub = &xhci->usb3_rhub; + else + rhub = &xhci->usb2_rhub; + + if (strcmp(tegra->soc->phy_types[i].name, "hsic") == 0) + offset = tegra->soc->ports.usb2.count; + else + offset = 0; + + for (j = 0; j < tegra->soc->phy_types[i].num; j++) { + phy = tegra->phys[k++]; + + if (!phy) + continue; + + index = j + offset; + + if (index >= rhub->num_ports) + continue; + + if (!is_host_mode_phy(tegra, i, j)) + continue; + + portsc = readl(rhub->ports[index]->addr); + speed = tegra_xhci_portsc_to_speed(tegra, portsc); + tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed); + tegra_xusb_padctl_enable_phy_wake(padctl, phy); + } + } +} + +static void tegra_xhci_disable_phy_wake(struct tegra_xusb *tegra) +{ + struct tegra_xusb_padctl *padctl = tegra->padctl; + unsigned int i; + + for (i = 0; i < tegra->num_phys; i++) { + if (!tegra->phys[i]) + continue; + + tegra_xusb_padctl_disable_phy_wake(padctl, tegra->phys[i]); + } +} + +static void tegra_xhci_disable_phy_sleepwalk(struct tegra_xusb *tegra) +{ + struct tegra_xusb_padctl *padctl = tegra->padctl; + unsigned int i; + + for (i = 0; i < tegra->num_phys; i++) { + if (!tegra->phys[i]) + continue; + + tegra_xusb_padctl_disable_phy_sleepwalk(padctl, tegra->phys[i]); + } +} + +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime) +{ + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct device *dev = tegra->dev; + bool wakeup = runtime ? true : device_may_wakeup(dev); + unsigned int i; int err; + u32 usbcmd; + + dev_dbg(dev, "entering ELPG\n"); + + usbcmd = readl(&xhci->op_regs->command); + usbcmd &= ~CMD_EIE; + writel(usbcmd, &xhci->op_regs->command); err = tegra_xusb_check_ports(tegra); if (err < 0) { dev_err(tegra->dev, "not all ports suspended: %d\n", err); - return err; + goto out; } err = xhci_suspend(xhci, wakeup); if (err < 0) { dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err); - return err; + goto out; } tegra_xusb_save_context(tegra); - tegra_xusb_phy_disable(tegra); + + if (wakeup) + tegra_xhci_enable_phy_sleepwalk_wake(tegra); + + tegra_xusb_powergate_partitions(tegra); + + for (i = 0; i < tegra->num_phys; i++) { + if (!tegra->phys[i]) + continue; + + phy_power_off(tegra->phys[i]); + if (!wakeup) + phy_exit(tegra->phys[i]); + } + tegra_xusb_clk_disable(tegra); - return 0; +out: + if (!err) + dev_dbg(tegra->dev, "entering ELPG done\n"); + else { + usbcmd = readl(&xhci->op_regs->command); + usbcmd |= CMD_EIE; + writel(usbcmd, &xhci->op_regs->command); + + dev_dbg(tegra->dev, "entering ELPG failed\n"); + pm_runtime_mark_last_busy(tegra->dev); + } + + return err; } -static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup) +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct device *dev = tegra->dev; + bool wakeup = runtime ? true : device_may_wakeup(dev); + unsigned int i; + u32 usbcmd; int err; + dev_dbg(dev, "exiting ELPG\n"); + pm_runtime_mark_last_busy(tegra->dev); + err = tegra_xusb_clk_enable(tegra); if (err < 0) { dev_err(tegra->dev, "failed to enable clocks: %d\n", err); - return err; + goto out; } - err = tegra_xusb_phy_enable(tegra); - if (err < 0) { - dev_err(tegra->dev, "failed to enable PHYs: %d\n", err); - goto disable_clk; + err = tegra_xusb_unpowergate_partitions(tegra); + if (err) + goto disable_clks; + + if (wakeup) + tegra_xhci_disable_phy_wake(tegra); + + for (i = 0; i < tegra->num_phys; i++) { + if (!tegra->phys[i]) + continue; + + if (!wakeup) + phy_init(tegra->phys[i]); + + phy_power_on(tegra->phys[i]); } tegra_xusb_config(tegra); @@ -1826,31 +2066,79 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup) goto disable_phy; } - err = xhci_resume(xhci, true); + if (wakeup) + tegra_xhci_disable_phy_sleepwalk(tegra); + + err = xhci_resume(xhci, 0); if (err < 0) { dev_err(tegra->dev, "failed to resume XHCI: %d\n", err); goto disable_phy; } - return 0; + usbcmd = readl(&xhci->op_regs->command); + usbcmd |= CMD_EIE; + writel(usbcmd, &xhci->op_regs->command); + + goto out; disable_phy: - tegra_xusb_phy_disable(tegra); -disable_clk: + for (i = 0; i < tegra->num_phys; i++) { + if (!tegra->phys[i]) + continue; + + phy_power_off(tegra->phys[i]); + if (!wakeup) + phy_exit(tegra->phys[i]); + } + tegra_xusb_powergate_partitions(tegra); +disable_clks: tegra_xusb_clk_disable(tegra); +out: + if (!err) + dev_dbg(dev, "exiting ELPG done\n"); + else + dev_dbg(dev, "exiting ELPG failed\n"); + return err; } static int tegra_xusb_suspend(struct device *dev) { struct tegra_xusb *tegra = dev_get_drvdata(dev); - bool wakeup = device_may_wakeup(dev); int err; synchronize_irq(tegra->mbox_irq); mutex_lock(&tegra->lock); - err = tegra_xusb_enter_elpg(tegra, wakeup); + + if (pm_runtime_suspended(dev)) { + err = tegra_xusb_exit_elpg(tegra, true); + if (err < 0) + goto out; + } + + err = tegra_xusb_enter_elpg(tegra, false); + if (err < 0) { + if (pm_runtime_suspended(dev)) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + goto out; + } + +out: + if (!err) { + tegra->suspended = true; + pm_runtime_disable(dev); + + if (device_may_wakeup(dev)) { + if (enable_irq_wake(tegra->padctl_irq)) + dev_err(dev, "failed to enable padctl wakes\n"); + } + } + mutex_unlock(&tegra->lock); return err; @@ -1859,11 +2147,56 @@ static int tegra_xusb_suspend(struct device *dev) static int tegra_xusb_resume(struct device *dev) { struct tegra_xusb *tegra = dev_get_drvdata(dev); - bool wakeup = device_may_wakeup(dev); int err; mutex_lock(&tegra->lock); - err = tegra_xusb_exit_elpg(tegra, wakeup); + + if (!tegra->suspended) { + mutex_unlock(&tegra->lock); + return 0; + } + + err = tegra_xusb_exit_elpg(tegra, false); + if (err < 0) { + mutex_unlock(&tegra->lock); + return err; + } + + if (device_may_wakeup(dev)) { + if (disable_irq_wake(tegra->padctl_irq)) + dev_err(dev, "failed to disable padctl wakes\n"); + } + tegra->suspended = false; + mutex_unlock(&tegra->lock); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int tegra_xusb_runtime_suspend(struct device *dev) +{ + struct tegra_xusb *tegra = dev_get_drvdata(dev); + int ret; + + synchronize_irq(tegra->mbox_irq); + mutex_lock(&tegra->lock); + ret = tegra_xusb_enter_elpg(tegra, true); + mutex_unlock(&tegra->lock); + + return ret; +} + +static int tegra_xusb_runtime_resume(struct device *dev) +{ + struct tegra_xusb *tegra = dev_get_drvdata(dev); + int err; + + mutex_lock(&tegra->lock); + err = tegra_xusb_exit_elpg(tegra, true); mutex_unlock(&tegra->lock); return err;