From patchwork Thu May 15 00:33:03 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Bresticker X-Patchwork-Id: 4178941 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id E3E01BFF02 for ; Thu, 15 May 2014 00:58:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 3AFB920265 for ; Thu, 15 May 2014 00:58:21 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7F02D2020E for ; Thu, 15 May 2014 00:58:18 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WkjxN-0001JB-B6; Thu, 15 May 2014 00:55:33 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WkjxH-0001IT-9u for linux-arm-kernel@bombadil.infradead.org; Thu, 15 May 2014 00:55:27 +0000 Received: from mail-oa0-f74.google.com ([209.85.219.74]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WkjcC-0005R7-2p for linux-arm-kernel@lists.infradead.org; Thu, 15 May 2014 00:33:49 +0000 Received: by mail-oa0-f74.google.com with SMTP id m1so88577oag.3 for ; Wed, 14 May 2014 17:33:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=1mmWf7XGwaNFHFpHwm7itGOmJE8lCSD071uXhmWMcZM=; b=A+yhrWJEOpbFlRcoXhjaLh1M3iqSbOWW9Ea/cEc6UF0TqsqpWI7aYjW8RUZKL3eNn/ rXI4zvb4StRLBzspzQo2Mg+7ZZUiwVB9Nr8RhdZhH4fo2oSIkYlHsb4H0mWXxIswlUNL X8kFddSu+fp1u2kSNrhefmyXTzAAOEGh+0/Fw5cZORvNiLJSYEIGpoGaVwh5oEZcKfjk OD9PTzIMoNahiFEonD70Zk6cY3drSKszjQSGN2QuriOaox5zED0X5yXiLmdkGQY3s3ML qqRHD0O2PfeJyuThV3JcIaJN0s3Bf/lsV3iRmY8AjvoHFJYTGYWk8Uy2R8IraNIE+fm4 YReQ== X-Gm-Message-State: ALoCoQnzjq9khGHHqxX20AyIPUwRTRcNh3r6ZSGBpfnrMi1gC2c88XWthClNYkK3rX+bvHdrf4pD X-Received: by 10.182.120.129 with SMTP id lc1mr3496977obb.21.1400113997499; Wed, 14 May 2014 17:33:17 -0700 (PDT) Received: from corp2gmr1-2.hot.corp.google.com (corp2gmr1-2.hot.corp.google.com [172.24.189.93]) by gmr-mx.google.com with ESMTPS id y50si167004yhk.4.2014.05.14.17.33.17 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 May 2014 17:33:17 -0700 (PDT) Received: from abrestic.mtv.corp.google.com (abrestic.mtv.corp.google.com [172.22.72.111]) by corp2gmr1-2.hot.corp.google.com (Postfix) with ESMTP id 250ED5A41E5; Wed, 14 May 2014 17:33:17 -0700 (PDT) Received: by abrestic.mtv.corp.google.com (Postfix, from userid 137652) id DD9FF2206A7; Wed, 14 May 2014 17:33:16 -0700 (PDT) From: Andrew Bresticker To: linux-tegra@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org Subject: [RFC PATCH 07/10] phy: Add Tegra XUSB PHY driver Date: Wed, 14 May 2014 17:33:03 -0700 Message-Id: <1400113986-339-8-git-send-email-abrestic@chromium.org> X-Mailer: git-send-email 1.9.1.423.g4596e3a In-Reply-To: <1400113986-339-1-git-send-email-abrestic@chromium.org> References: <1400113986-339-1-git-send-email-abrestic@chromium.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140515_013340_576769_5D964BE4 X-CRM114-Status: GOOD ( 25.90 ) X-Spam-Score: -3.3 (---) Cc: Mark Rutland , Prashant Gaikwad , Russell King , Mathias Nyman , Pawel Moll , Stephen Warren , Andrew Bresticker , Greg Kroah-Hartman , Peter De Schrijver , Ian Campbell , Kishon Vijay Abraham I , Rob Herring , Thierry Reding , Randy Dunlap , Kumar Gala , Grant Likely , Mike Turquette X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add support for the Tegra XUSB PHY present on Tegra114 and Tegra124 SoCs. This provides all PHY functionality for the Tegra XHCI host-controller driver and supports UTMI, HSIC, and SuperSpeed interfaces. While this PHY driver currently only handles USB, the SATA and PCIe PHYs are programmed in a similar way using the same register set. The driver could be extended in the future to support these PHY types as well. Signed-off-by: Andrew Bresticker --- .../bindings/phy/nvidia,tegra-xusb-phy.txt | 76 ++ drivers/phy/Kconfig | 11 + drivers/phy/Makefile | 1 + drivers/phy/phy-tegra-xusb.c | 1171 ++++++++++++++++++++ drivers/phy/phy-tegra-xusb.h | 270 +++++ 5 files changed, 1529 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra-xusb-phy.txt create mode 100644 drivers/phy/phy-tegra-xusb.c create mode 100644 drivers/phy/phy-tegra-xusb.h diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra-xusb-phy.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra-xusb-phy.txt new file mode 100644 index 0000000..6d819f9 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra-xusb-phy.txt @@ -0,0 +1,76 @@ +NVIDIA Tegra XUSB PHY + +The device node for the Tegra XUSB PHY present on Tegra114 and later SoCs. + +Required properties: + - compatible: Should be "nvidia,tegra114-xusb-phy" or + "nvidia,tegra124-xusb-phy". + - reg: Address and length of the XUSB_PADCTL register set. + - clocks: Handles to XUSB SS, SS source, HS source, FS source, PLL_U_480M, + CLK_M, and PLLE. Note that PLL_U_480M and CLK_M are only necessary + on Tegra124. + - clock-names: Should be "xusb_ss", "xusb_ss_src", "xusb_hs_src", + "xusb_fs_src", "pll_u_480M", "clk_m",and "pll_e" respectively. + - resets: Handle to the XUSB SS reset. + - reset-names: Should be "xusb_ss". + - nvidia,clkrst: Handle to the clock-reset module syscon node. + - nvidia,ss-pads: Bitmap of enabled SuperSpeed pads: + bit 0 - SuperSpeed port 0 + bit 1 - SuperSpeed port 1 + - nvidia,utmi-pads: Bitmap of enabled USB2.0 UTMI pads: + bit 0 - UTMI port 0 + bit 1 - UTMI port 1 + bit 2 - UTMI port 2 (Tegra124 only) + - nvidia,hsic-pads: Bitmap of enabled USB2.0 HSIC pads: + bit 0 - HSIC port 0 + bit 1 - HSIC port 1 + - nvidia,hsic{0,1}-config: byte array with 9 elements specifiying the + configuration for the corresponding HISC port: + byte 0 - rx_strobe_trim + byte 1 - rx_data_trim + byte 2 - tx_rtune_n + byte 3 - tx_rtune_p + byte 4 - tx_slew_n + byte 5 - tx_slew_p + byte 6 - auto_term_en + byte 7 - strb_trim_val + byte 8 - pretend_connect + Only required for enabled HISC ports. + - nvidia,ss-port{0,1}-map: Mapping from SS port to its corresponding USB2.0 + port. Both fields have valid values from 0 to 2 (USB2.0 ports 0, 1, 2). + Only required for enabled SS pads. + - vbus{1,2,3}-supply: VBUS regulator for the corresponding UTMI pad. + Only required when the respective UTMI pad is enabled. + - vddio-hsic-supply: HSIC supply regulator. Only required when HSIC ports + are enabled. +Optional properties: + - nvidia,use-sata-lane: Should be set if SS port 1 is mapped to the SATA + lane. Only applicable on Tegra124. + +Example: + phy@0,7009f000 { + compatible = "nvidia,tegra124-xusb-phy"; + reg = <0x0 0x7009f000 0x0 0x1000>; + #phy-cells = <0>; + clocks = <&tegra_car TEGRA124_CLK_XUSB_SS>, + <&tegra_car TEGRA124_CLK_XUSB_SS_SRC>, + <&tegra_car TEGRA124_CLK_XUSB_HS_SRC>, + <&tegra_car TEGRA124_CLK_XUSB_FS_SRC>, + <&tegra_car TEGRA124_CLK_PLL_U_480M>, + <&tegra_car TEGRA124_CLK_CLK_M>, + <&tegra_car TEGRA124_CLK_PLL_E>; + clock-names = "xusb_ss", "xusb_ss_src", "xusb_hs_src", + "xusb_fs_src", "pll_u_480M", "clk_m", + "pll_e"; + resets = <&tegra_car 156>; + reset-names = "xusb_ss"; + nvidia,clkrst = <&tegra_car>; + nvidia,ss-pads = <0x3>; + nvidia,hsic-pads = <0x0>; + nvidia,utmi-pads = <0x7>; + nvidia,ss-port0-map = <0>; + nvidia,ss-port1-map = <2>; + vbus1-supply = <&vdd_usb1_vbus>; + vbus2-supply = <&vdd_run_cam>; + vbus3-supply = <&vdd_usb3_vbus>; + }; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 3bb05f1..8eb65ae 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -159,6 +159,17 @@ config PHY_EXYNOS5250_USB2 particular SoC is compiled in the driver. In case of Exynos 5250 four phys are available - device, host, HSIC0 and HSIC. +config PHY_TEGRA_XUSB + tristate "Support for NVIDIA Tegra XUSB PHY" + depends on USB_XHCI_TEGRA + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the XUSB PHY present on NVIDIA Tegra114 + and later SoCs. This driver supports UTMI, HSIC, and SuperSpeed + interfaces and handles all host PHY functionality for the Tegra + XHCI host-controller driver. + config PHY_XGENE tristate "APM X-Gene 15Gbps PHY support" depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST) diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 2faf78e..f682a8e 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-samsung-usb2.o obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o obj-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o obj-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o +obj-$(CONFIG_PHY_TEGRA_XUSB) += phy-tegra-xusb.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o diff --git a/drivers/phy/phy-tegra-xusb.c b/drivers/phy/phy-tegra-xusb.c new file mode 100644 index 0000000..78d2398 --- /dev/null +++ b/drivers/phy/phy-tegra-xusb.c @@ -0,0 +1,1171 @@ +/* + * NVIDIA Tegra XUSB PHY driver + * + * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-tegra-xusb.h" + +struct tegra_xusb_hsic_config { + u8 rx_strobe_trim; + u8 rx_data_trim; + u8 tx_rtune_n; + u8 tx_rtune_p; + u8 tx_slew_n; + u8 tx_slew_p; + u8 auto_term_en; + u8 strb_trim_val; + u8 pretend_connect; +}; + +struct tegra_xusb_phy_calib_data { + u32 hs_curr_level_pad[TEGRA_XUSB_UTMI_COUNT]; + u32 hs_iref_cap; + u32 hs_term_range_adj; + u32 hs_squelch_level; +}; + +struct tegra_xusb_phy_board_data { + unsigned long utmi_pads; + unsigned long hsic_pads; + unsigned long ss_pads; + bool use_sata_lane; + int ss_portmap[TEGRA_XUSB_SS_COUNT]; + struct tegra_xusb_hsic_config hsic[TEGRA_XUSB_HSIC_COUNT]; +}; + +struct tegra_xusb_phy_soc_config { + bool ss_idle_mode_ovrd; + bool has_ctle; + bool scale_ss_clk; + int num_utmi_pads; + u32 rx_wander; + u32 rx_eq; + u32 cdr_cntl; + u32 dfe_cntl; + u32 hs_slew; + u32 ls_rslew_pad[TEGRA_XUSB_UTMI_COUNT]; + u32 hs_disc_lvl; + u32 spare_in; + int hsic_port_offset; + const struct tegra_xusb_padctl_regs *padctl_offsets; +}; + +struct tegra_xusb_phy { + struct device *dev; + + void __iomem *padctl_regs; + struct regmap *clkrst_regs; + + struct notifier_block mbox_nb; + + struct clk *ss_src_clk; + struct clk *hs_src_clk; + struct clk *fs_src_clk; + struct clk *ss_clk; + struct clk *pll_u_480M; + struct clk *clk_m; + struct clk *plle; + struct reset_control *ss_rst; + + struct regulator *utmi_vbus[TEGRA_XUSB_UTMI_COUNT]; + struct regulator *vddio_hsic; + + /* DFE and CTLE context */ + bool ss_ctx_saved[TEGRA_XUSB_SS_COUNT]; + u8 tap1_val[TEGRA_XUSB_SS_COUNT]; + u8 amp_val[TEGRA_XUSB_SS_COUNT]; + u8 ctle_z_val[TEGRA_XUSB_SS_COUNT]; + u8 ctle_g_val[TEGRA_XUSB_SS_COUNT]; + + struct tegra_xusb_phy_board_data board_data; + struct tegra_xusb_phy_calib_data calib_data; + const struct tegra_xusb_phy_soc_config *soc_config; +}; + +static inline u32 padctl_readl(struct tegra_xusb_phy *tegra, u32 reg) +{ + BUG_ON(reg == PADCTL_REG_NONE); + return readl(tegra->padctl_regs + reg); +} + +static inline void padctl_writel(struct tegra_xusb_phy *tegra, u32 val, u32 reg) +{ + BUG_ON(reg == PADCTL_REG_NONE); + writel(val, tegra->padctl_regs + reg); +} + +static inline u32 clkrst_readl(struct tegra_xusb_phy *tegra, u32 reg) +{ + u32 val; + + regmap_read(tegra->clkrst_regs, reg, &val); + return val; +} + +static inline void clkrst_writel(struct tegra_xusb_phy *tegra, u32 val, u32 reg) +{ + regmap_write(tegra->clkrst_regs, reg, val); +} + +static void hsic_pad_init(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + struct tegra_xusb_hsic_config *hsic = &tegra->board_data.hsic[pad]; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][2]); + reg &= ~(USB2_HSIC_RX_STROBE_TRIM(~0) | USB2_HSIC_RX_DATA_TRIM(~0)); + reg |= USB2_HSIC_RX_STROBE_TRIM(hsic->rx_strobe_trim) | + USB2_HSIC_RX_DATA_TRIM(hsic->rx_data_trim); + padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][2]); + + reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][0]); + reg &= ~(USB2_HSIC_TX_RTUNEP(~0) | USB2_HSIC_TX_RTUNEN(~0) | + USB2_HSIC_TX_SLEWP(~0) | USB2_HSIC_TX_SLEWN(~0)); + reg |= USB2_HSIC_TX_RTUNEP(hsic->tx_rtune_p) | + USB2_HSIC_TX_RTUNEN(hsic->tx_rtune_n) | + USB2_HSIC_TX_SLEWP(hsic->tx_slew_p) | + USB2_HSIC_TX_SLEWN(hsic->tx_slew_n); + padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][0]); + + reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]); + if (hsic->auto_term_en) + reg |= USB2_HSIC_AUTO_TERM_EN; + else + reg &= ~USB2_HSIC_AUTO_TERM_EN; + padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]); + + reg = padctl_readl(tegra, padregs->hsic_strb_trim_ctl0); + reg &= ~HSIC_STRB_TRIM(~0); + reg |= HSIC_STRB_TRIM(hsic->strb_trim_val); + padctl_writel(tegra, reg, padregs->hsic_strb_trim_ctl0); + + reg = padctl_readl(tegra, padregs->usb2_pad_mux_0); + reg |= USB2_HSIC_PAD_PORT(pad); + padctl_writel(tegra, reg, padregs->usb2_pad_mux_0); +} + +static void hsic_pad_enable(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + + reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]); + reg &= ~(USB2_HSIC_RPD_DATA | USB2_HSIC_RPD_STROBE | + USB2_HSIC_RPU_DATA | USB2_HSIC_RPU_STROBE | + USB2_HSIC_PD_RX | USB2_HSIC_PD_ZI | + USB2_HSIC_PD_TRX | USB2_HSIC_PD_TX); + /* Keep HSIC in IDLE */ + reg |= USB2_HSIC_RPD_DATA | USB2_HSIC_RPU_STROBE; + padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]); +} + +static void hsic_pad_disable(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + + reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]); + reg |= (USB2_HSIC_PD_RX | USB2_HSIC_PD_ZI | USB2_HSIC_PD_TRX | + USB2_HSIC_PD_TX); + padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]); +} + +static void hsic_pad_set_idle(struct tegra_xusb_phy *tegra, int pad, bool set) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + + reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]); + reg &= ~(USB2_HSIC_RPD_DATA | USB2_HSIC_RPD_STROBE | + USB2_HSIC_RPU_DATA | USB2_HSIC_RPU_STROBE); + if (set) + reg |= USB2_HSIC_RPD_DATA | USB2_HSIC_RPU_STROBE; + padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]); +} + +static void utmi_pad_init(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + + reg = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]); + reg &= ~(USB2_BIAS_HS_SQUELCH_LEVEL(~0) | + USB2_BIAS_HS_DISCON_LEVEL(~0)); + reg |= USB2_BIAS_HS_SQUELCH_LEVEL(tegra->calib_data.hs_squelch_level) | + USB2_BIAS_HS_DISCON_LEVEL(tegra->soc_config->hs_disc_lvl); + padctl_writel(tegra, reg, padregs->usb2_bias_pad_ctlY_0[0]); + + reg = padctl_readl(tegra, padregs->usb2_pad_mux_0); + reg &= ~USB2_OTG_PAD_PORT_MASK(pad); + reg |= USB2_OTG_PAD_PORT_OWNER_XUSB(pad); + padctl_writel(tegra, reg, padregs->usb2_pad_mux_0); + + reg = padctl_readl(tegra, padregs->usb2_port_cap_0); + reg &= ~USB2_PORT_CAP_MASK(pad); + reg |= USB2_PORT_CAP_HOST(pad); + padctl_writel(tegra, reg, padregs->usb2_port_cap_0); + + reg = padctl_readl(tegra, padregs->usb2_otg_padX_ctlY_0[pad][0]); + reg &= ~(USB2_OTG_HS_CURR_LVL(~0) | USB2_OTG_HS_SLEW(~0) | + USB2_OTG_FS_SLEW(~0) | USB2_OTG_LS_RSLEW(~0) | + USB2_OTG_PD | USB2_OTG_PD2 | USB2_OTG_PD_ZI); + reg |= USB2_OTG_HS_SLEW(tegra->soc_config->hs_slew) | + USB2_OTG_LS_RSLEW(tegra->soc_config->ls_rslew_pad[pad]) | + USB2_OTG_HS_CURR_LVL(tegra->calib_data.hs_curr_level_pad[pad]); + padctl_writel(tegra, reg, padregs->usb2_otg_padX_ctlY_0[pad][0]); + + reg = padctl_readl(tegra, padregs->usb2_otg_padX_ctlY_0[pad][1]); + reg &= ~(USB2_OTG_TERM_RANGE_AD(~0) | USB2_OTG_HS_IREF_CAP(~0) | + USB2_OTG_PD_DR | USB2_OTG_PD_CHRP_FORCE_POWERUP | + USB2_OTG_PD_DISC_FORCE_POWERUP); + reg |= USB2_OTG_HS_IREF_CAP(tegra->calib_data.hs_iref_cap) | + USB2_OTG_TERM_RANGE_AD(tegra->calib_data.hs_term_range_adj); + padctl_writel(tegra, reg, padregs->usb2_otg_padX_ctlY_0[pad][1]); +} + +static void utmi_pads_enable(struct tegra_xusb_phy *tegra) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 val; + + padregs = tegra->soc_config->padctl_offsets; + + val = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]); + val &= ~USB2_BIAS_PD; + padctl_writel(tegra, val, padregs->usb2_bias_pad_ctlY_0[0]); +} + +static void utmi_pads_disable(struct tegra_xusb_phy *tegra) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 val; + + padregs = tegra->soc_config->padctl_offsets; + + val = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]); + val |= USB2_BIAS_PD; + padctl_writel(tegra, val, padregs->usb2_bias_pad_ctlY_0[0]); +} + +static void ss_save_context(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 offset; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + tegra->ss_ctx_saved[pad] = true; + + if (pad == 1 && tegra->board_data.use_sata_lane) + offset = padregs->iophy_misc_pad_s0_ctlY_0[5]; + else + offset = padregs->iophy_misc_pad_pX_ctlY_0[pad][5]; + + reg = padctl_readl(tegra, offset); + reg &= ~IOPHY_MISC_OUT_SEL(~0); + reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_TAP); + padctl_writel(tegra, reg, offset); + + reg = padctl_readl(tegra, offset); + tegra->tap1_val[pad] = IOPHY_MISC_OUT_TAP_VAL(reg); + + reg = padctl_readl(tegra, offset); + reg &= ~IOPHY_MISC_OUT_SEL(~0); + reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_AMP); + padctl_writel(tegra, reg, offset); + + reg = padctl_readl(tegra, offset); + tegra->amp_val[pad] = IOPHY_MISC_OUT_AMP_VAL(reg); + + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][3]); + reg &= ~IOPHY_USB3_DFE_CNTL_TAP(~0); + reg |= IOPHY_USB3_DFE_CNTL_TAP(tegra->tap1_val[pad]); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][3]); + + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][3]); + reg &= ~IOPHY_USB3_DFE_CNTL_AMP(~0); + reg |= IOPHY_USB3_DFE_CNTL_AMP(tegra->amp_val[pad]); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][3]); + + if (!tegra->soc_config->has_ctle) + return; + + reg = padctl_readl(tegra, offset); + reg &= ~IOPHY_MISC_OUT_SEL(~0); + reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_LATCH_G_Z); + padctl_writel(tegra, reg, offset); + + reg = padctl_readl(tegra, offset); + reg &= ~IOPHY_MISC_OUT_SEL(~0); + reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_G_Z); + padctl_writel(tegra, reg, offset); + + reg = padctl_readl(tegra, offset); + tegra->ctle_g_val[pad] = IOPHY_MISC_OUT_G_Z_VAL(reg); + + reg = padctl_readl(tegra, offset); + reg &= ~IOPHY_MISC_OUT_SEL(~0); + reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_CTLE_Z); + padctl_writel(tegra, reg, offset); + + reg = padctl_readl(tegra, offset); + tegra->ctle_z_val[pad] = IOPHY_MISC_OUT_G_Z_VAL(reg); + + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][1]); + reg &= ~IOPHY_USB3_RX_EQ_Z(~0); + reg |= IOPHY_USB3_RX_EQ_Z(tegra->ctle_z_val[pad]); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][1]); + + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][1]); + reg &= ~IOPHY_USB3_RX_EQ_G(~0); + reg |= IOPHY_USB3_RX_EQ_G(tegra->ctle_g_val[pad]); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][1]); +} + +static void ss_restore_context(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg; + + if (!tegra->ss_ctx_saved[pad]) + return; + + padregs = tegra->soc_config->padctl_offsets; + + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][3]); + reg &= ~(IOPHY_USB3_DFE_CNTL_AMP(~0) | + IOPHY_USB3_DFE_CNTL_TAP(~0)); + reg |= IOPHY_USB3_DFE_CNTL_AMP(tegra->amp_val[pad]) | + IOPHY_USB3_DFE_CNTL_TAP(tegra->tap1_val[pad]); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][3]); + + if (!tegra->soc_config->has_ctle) + return; + + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][1]); + reg &= ~(IOPHY_USB3_RX_EQ_Z(~0) | IOPHY_USB3_RX_EQ_G(~0)); + reg |= (IOPHY_USB3_RX_EQ_Z(tegra->ctle_z_val[pad]) | + IOPHY_USB3_RX_EQ_G(tegra->ctle_g_val[pad])); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][1]); +} + +/* Disable/enable SS wake logic */ +static void ss_set_wake(struct tegra_xusb_phy *tegra, bool on) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 val; + int i; + + padregs = tegra->soc_config->padctl_offsets; + val = padctl_readl(tegra, padregs->elpg_program_0); + for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) { + if (on) + val |= SSP_ELPG_CLAMP_EN_EARLY(i); + else + val &= ~SSP_ELPG_CLAMP_EN_EARLY(i); + } + padctl_writel(tegra, val, padregs->elpg_program_0); + + /* + * There needs to be a delay between writes to CLAMP_EN_EARLY and + * CLAMP_EN + */ + udelay(100); + + val = padctl_readl(tegra, padregs->elpg_program_0); + for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) { + if (on) + val |= SSP_ELPG_CLAMP_EN(i); + else + val &= ~SSP_ELPG_CLAMP_EN(i); + } + padctl_writel(tegra, val, padregs->elpg_program_0); + + /* Wait for 250us for CLAMP_EN to propagate */ + if (on) + udelay(250); + + val = padctl_readl(tegra, padregs->elpg_program_0); + for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) { + if (on) + val |= SSP_ELPG_VCORE_DOWN(i); + else + val &= ~SSP_ELPG_VCORE_DOWN(i); + } + padctl_writel(tegra, val, padregs->elpg_program_0); +} + +static int ss_set_clock_rate(struct tegra_xusb_phy *tegra, unsigned int rate) +{ + unsigned int new_parent_rate, old_parent_rate, div; + int ret; + struct clk *ss_clk = tegra->ss_src_clk; + + if (clk_get_rate(ss_clk) == rate) + return 0; + + switch (rate) { + case SS_CLK_HIGH_SPEED: + /* Reparent to PLLU_480M. Set div first to avoid overclocking */ + old_parent_rate = clk_get_rate(clk_get_parent(ss_clk)); + new_parent_rate = clk_get_rate(tegra->pll_u_480M); + div = new_parent_rate / rate; + ret = clk_set_rate(ss_clk, old_parent_rate / div); + if (ret) { + dev_err(tegra->dev, "Failed to set SS rate: %d\n", + ret); + return ret; + } + ret = clk_set_parent(ss_clk, tegra->pll_u_480M); + if (ret) { + dev_err(tegra->dev, "Failed to set SS parent: %d\n", + ret); + return ret; + } + break; + case SS_CLK_LOW_SPEED: + /* Reparent to CLK_M */ + ret = clk_set_parent(ss_clk, tegra->clk_m); + if (ret) { + dev_err(tegra->dev, "Failed to set SS parent: %d\n", + ret); + return ret; + } + ret = clk_set_rate(ss_clk, rate); + if (ret) { + dev_err(tegra->dev, "Failed to set SS rate: %d\n", + ret); + return ret; + } + break; + default: + dev_err(tegra->dev, "Invalid SS rate: %u\n", rate); + return -EINVAL; + } + + if (clk_get_rate(ss_clk) != rate) { + dev_err(tegra->dev, "SS clock doesn't match requested rate\n"); + return -EINVAL; + } + + return 0; +} + +static void ss_pad_init(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg; + + padregs = tegra->soc_config->padctl_offsets; + reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[pad][1]); + reg &= ~(IOPHY_USB3_RX_WANDER(~0) | IOPHY_USB3_RX_EQ(~0) | + IOPHY_USB3_CDR_CNTL(~0)); + reg |= IOPHY_USB3_RX_WANDER(tegra->soc_config->rx_wander) | + IOPHY_USB3_RX_EQ(tegra->soc_config->rx_eq) | + IOPHY_USB3_CDR_CNTL(tegra->soc_config->cdr_cntl); + padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[pad][1]); + + padctl_writel(tegra, tegra->soc_config->dfe_cntl, + padregs->iophy_usb3_padX_ctlY_0[pad][3]); + + if (pad == 1 && tegra->board_data.use_sata_lane) { + reg = padctl_readl(tegra, padregs->iophy_misc_pad_s0_ctlY_0[4]); + reg |= IOPHY_RX_QEYE_EN; + padctl_writel(tegra, reg, padregs->iophy_misc_pad_s0_ctlY_0[4]); + + reg = padctl_readl(tegra, padregs->iophy_misc_pad_s0_ctlY_0[1]); + reg &= ~IOPHY_SPARE_IN(~0); + reg |= IOPHY_SPARE_IN(tegra->soc_config->spare_in); + padctl_writel(tegra, reg, padregs->iophy_misc_pad_s0_ctlY_0[1]); + } else { + reg = padctl_readl(tegra, + padregs->iophy_misc_pad_pX_ctlY_0[pad][4]); + reg |= IOPHY_RX_QEYE_EN; + padctl_writel(tegra, reg, + padregs->iophy_misc_pad_pX_ctlY_0[pad][4]); + + reg = padctl_readl(tegra, + padregs->iophy_misc_pad_pX_ctlY_0[pad][1]); + reg &= ~IOPHY_SPARE_IN(~0); + reg |= IOPHY_SPARE_IN(tegra->soc_config->spare_in); + padctl_writel(tegra, reg, + padregs->iophy_misc_pad_pX_ctlY_0[pad][1]); + } + + reg = padctl_readl(tegra, padregs->ss_port_map_0); + reg &= ~SS_PORT_MAP_MASK(pad); + reg |= (tegra->board_data.ss_portmap[pad] << SS_PORT_MAP_SHIFT(pad)) & + SS_PORT_MAP_MASK(pad); + padctl_writel(tegra, reg, padregs->ss_port_map_0); + + ss_restore_context(tegra, pad); +} + +static void ss_pad_rx_idle_mode_override(struct tegra_xusb_phy *tegra, int pad) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 reg, offset; + + /* Set RX_IDLE_MODE_OVRD to save power on unused pads */ + padregs = tegra->soc_config->padctl_offsets; + if (pad == 1 && tegra->board_data.use_sata_lane) + offset = padregs->iophy_misc_pad_s0_ctlY_0[2]; + else + offset = padregs->iophy_misc_pad_pX_ctlY_0[pad][2]; + reg = padctl_readl(tegra, offset); + reg &= ~IOPHY_RX_IDLE_MODE; + reg |= IOPHY_RX_IDLE_MODE_OVRD; + padctl_writel(tegra, reg, offset); +} + +static void ss_lanes_init(struct tegra_xusb_phy *tegra) +{ + const struct tegra_xusb_padctl_regs *padregs; + u32 val; + + padregs = tegra->soc_config->padctl_offsets; + if ((tegra->board_data.ss_pads & BIT(1)) && + tegra->board_data.use_sata_lane) { + /* Program SATA pad phy */ + val = padctl_readl(tegra, padregs->iophy_pll_s0_ctlY_0[0]); + val &= ~IOPHY_PLL_PLL0_REFCLK_NDIV(~0); + val |= IOPHY_PLL_PLL0_REFCLK_NDIV(0x2); + padctl_writel(tegra, val, padregs->iophy_pll_s0_ctlY_0[0]); + + val = padctl_readl(tegra, padregs->iophy_pll_s0_ctlY_0[1]); + val &= ~(IOPHY_PLL_XDIGCLK_SEL(~0) | IOPHY_PLL_TXCLKREF_SEL | + IOPHY_PLL_TCLKOUT_EN | IOPHY_PLL_PLL0_CP_CNTL(~0) | + IOPHY_PLL_PLL1_CP_CNTL(~0)); + val |= IOPHY_PLL_XDIGCLK_SEL(0x7) | IOPHY_PLL_TXCLKREF_SEL | + IOPHY_PLL_PLL0_CP_CNTL(0x8) | + IOPHY_PLL_PLL1_CP_CNTL(0x8); + padctl_writel(tegra, val, padregs->iophy_pll_s0_ctlY_0[1]); + + val = padctl_readl(tegra, padregs->iophy_pll_s0_ctlY_0[2]); + val &= ~IOPHY_PLL_RCAL_BYPASS; + padctl_writel(tegra, val, padregs->iophy_pll_s0_ctlY_0[2]); + + /* Enable SATA PADPLL clocks */ + val = clkrst_readl(tegra, SATA_PLL_CFG0_0); + val &= ~SATA_PADPLL_RESET_SWCTL; + val |= SATA_PADPLL_USE_LOCKDET | SATA_SEQ_START_STATE; + clkrst_writel(tegra, val, SATA_PLL_CFG0_0); + + udelay(1); + + val = clkrst_readl(tegra, SATA_PLL_CFG0_0); + val |= SATA_SEQ_ENABLE; + clkrst_writel(tegra, val, SATA_PLL_CFG0_0); + } + + /* Program ownerhsip of lanes */ + val = padctl_readl(tegra, padregs->usb3_pad_mux_0); + if (tegra->board_data.ss_pads & BIT(0)) { + /* Port 0 is always mapped to PCIe lane0 */ + val &= ~USB3_PCIE_PAD_LANE_OWNER(0, ~0); + val |= USB3_PCIE_PAD_LANE_OWNER(0, USB3_LANE_OWNER_USB3_SS); + } + if (tegra->board_data.ss_pads & BIT(1)) { + /* Port 1 can either be mapped to SATA lane or PCIe lane1 */ + if (tegra->board_data.use_sata_lane) { + val &= ~USB3_SATA_PAD_LANE_OWNER(~0); + val |= USB3_SATA_PAD_LANE_OWNER(USB3_LANE_OWNER_USB3_SS); + } else { + val &= ~USB3_PCIE_PAD_LANE_OWNER(1, ~0); + val |= USB3_PCIE_PAD_LANE_OWNER(1, USB3_LANE_OWNER_USB3_SS); + } + } + padctl_writel(tegra, val, padregs->usb3_pad_mux_0); + + /* Bring enabled lane out of IDDQ */ + val = padctl_readl(tegra, padregs->usb3_pad_mux_0); + if (tegra->board_data.ss_pads & BIT(0)) + val |= USB3_FORCE_PCIE_PAD_IDDQ_DISABLE_MASK(0); + if (tegra->board_data.ss_pads & BIT(1)) { + if (tegra->board_data.use_sata_lane) + val |= USB3_FORCE_SATA_PAD_IDDQ_DISABLE_MASK; + else + val |= USB3_FORCE_PCIE_PAD_IDDQ_DISABLE_MASK(1); + } + padctl_writel(tegra, val, padregs->usb3_pad_mux_0); + + udelay(1); + + /* Release pad muxing logic state latching */ + val = padctl_readl(tegra, padregs->elpg_program_0); + val &= ~AUX_MUX_LP0_CLAMP_EN; + padctl_writel(tegra, val, padregs->elpg_program_0); + + udelay(100); + + val &= ~AUX_MUX_LP0_CLAMP_EN_EARLY; + padctl_writel(tegra, val, padregs->elpg_program_0); + + udelay(100); + + val &= ~AUX_MUX_LP0_VCORE_DOWN; + padctl_writel(tegra, val, padregs->elpg_program_0); +} + +static int tegra_xusb_phy_mbox_notifier(struct notifier_block *nb, + unsigned long event, void *p) +{ + struct tegra_xusb_phy *tegra = container_of(nb, struct tegra_xusb_phy, + mbox_nb); + struct mbox_notifier_data *data = (struct mbox_notifier_data *)p; + unsigned long ports; + int i, pad, ret = 0; + + switch (event) { + case MBOX_CMD_INC_SSPI_CLOCK: + case MBOX_CMD_DEC_SSPI_CLOCK: + if (!tegra->soc_config->scale_ss_clk) { + data->resp_cmd = MBOX_CMD_ACK; + data->resp_data = data->msg_data; + return NOTIFY_STOP; + } + ret = ss_set_clock_rate(tegra, data->msg_data * 1000); + data->resp_data = clk_get_rate(tegra->ss_src_clk) / 1000; + if (ret) + data->resp_cmd = MBOX_CMD_NAK; + else + data->resp_cmd = MBOX_CMD_ACK; + return NOTIFY_STOP; + case MBOX_CMD_SAVE_DFE_CTLE_CTX: + data->resp_data = data->msg_data; + if (data->msg_data > TEGRA_XUSB_SS_COUNT) { + data->resp_cmd = MBOX_CMD_NAK; + } else { + ss_save_context(tegra, data->msg_data); + data->resp_cmd = MBOX_CMD_ACK; + } + return NOTIFY_STOP; + case MBOX_CMD_START_HSIC_IDLE: + case MBOX_CMD_STOP_HSIC_IDLE: + ports = data->msg_data; + data->resp_data = ports; + for_each_set_bit(i, &ports, BITS_PER_LONG) { + pad = i - 1 - tegra->soc_config->hsic_port_offset; + if (pad > TEGRA_XUSB_HSIC_COUNT) { + ret = -EINVAL; + break; + } + if (event == MBOX_CMD_START_HSIC_IDLE) + hsic_pad_set_idle(tegra, pad, true); + else + hsic_pad_set_idle(tegra, pad, false); + } + if (ret) + data->resp_cmd = MBOX_CMD_NAK; + else + data->resp_cmd = MBOX_CMD_ACK; + return NOTIFY_STOP; + default: + return NOTIFY_DONE; + } +} + +static int tegra_xusb_phy_init(struct phy *phy) +{ + struct tegra_xusb_phy *tegra = phy_get_drvdata(phy); + int ret, i; + + tegra->mbox_nb.notifier_call = tegra_xusb_phy_mbox_notifier; + ret = tegra_xhci_register_mbox_notifier(&tegra->mbox_nb); + if (ret) { + dev_err(tegra->dev, "Failed to register handler\n"); + return ret; + } + + for_each_set_bit(i, &tegra->board_data.utmi_pads, TEGRA_XUSB_UTMI_COUNT) + utmi_pad_init(tegra, i); + + for_each_set_bit(i, &tegra->board_data.hsic_pads, TEGRA_XUSB_HSIC_COUNT) + hsic_pad_init(tegra, i); + + for (i = 0; i < TEGRA_XUSB_SS_COUNT; i++) { + if (tegra->board_data.ss_pads & BIT(i)) + ss_pad_init(tegra, i); + else + ss_pad_rx_idle_mode_override(tegra, i); + } + + ss_lanes_init(tegra); + + return 0; +} + +static int tegra_xusb_phy_exit(struct phy *phy) +{ + struct tegra_xusb_phy *tegra = phy_get_drvdata(phy); + + tegra_xhci_unregister_mbox_notifier(&tegra->mbox_nb); + + return 0; +} + +static int tegra_xusb_phy_power_on(struct phy *phy) +{ + struct tegra_xusb_phy *tegra = phy_get_drvdata(phy); + int i, ret; + + for_each_set_bit(i, &tegra->board_data.utmi_pads, + TEGRA_XUSB_UTMI_COUNT) { + ret = regulator_enable(tegra->utmi_vbus[i]); + if (ret) { + dev_err(tegra->dev, "Failed to enable vbus%d\n", i); + return ret; + } + } + if (tegra->board_data.hsic_pads) { + ret = regulator_enable(tegra->vddio_hsic); + if (ret) { + dev_err(tegra->dev, "Failed to enable vddio-hsic\n"); + return ret; + } + } + + clk_prepare_enable(tegra->plle); + clk_prepare_enable(tegra->ss_clk); + clk_prepare_enable(tegra->hs_src_clk); + clk_prepare_enable(tegra->fs_src_clk); + if (tegra->soc_config->scale_ss_clk) { + ret = ss_set_clock_rate(tegra, SS_CLK_HIGH_SPEED); + if (ret) { + dev_err(tegra->dev, "Failed to set xusb_ss rate\n"); + return ret; + } + } + + ss_set_wake(tegra, false); + + utmi_pads_enable(tegra); + + for_each_set_bit(i, &tegra->board_data.hsic_pads, TEGRA_XUSB_HSIC_COUNT) + hsic_pad_enable(tegra, i); + + return 0; +} + +static int tegra_xusb_phy_power_off(struct phy *phy) +{ + struct tegra_xusb_phy *tegra = phy_get_drvdata(phy); + int i; + + for_each_set_bit(i, &tegra->board_data.hsic_pads, TEGRA_XUSB_HSIC_COUNT) + hsic_pad_disable(tegra, i); + utmi_pads_disable(tegra); + + clk_disable_unprepare(tegra->plle); + clk_disable_unprepare(tegra->ss_clk); + clk_disable_unprepare(tegra->hs_src_clk); + clk_disable_unprepare(tegra->fs_src_clk); + + for_each_set_bit(i, &tegra->board_data.utmi_pads, TEGRA_XUSB_UTMI_COUNT) + regulator_disable(tegra->utmi_vbus[i]); + if (tegra->board_data.hsic_pads) + regulator_disable(tegra->vddio_hsic); + + return 0; +} + +static const struct tegra_xusb_padctl_regs tegra114_padctl_offsets = { + .boot_media_0 = 0x0, + .usb2_pad_mux_0 = 0x4, + .usb2_port_cap_0 = 0x8, + .snps_oc_map_0 = 0xc, + .usb2_oc_map_0 = 0x10, + .ss_port_map_0 = 0x14, + .oc_det_0 = 0x18, + .elpg_program_0 = 0x1c, + .usb2_bchrg_otgpadX_ctlY_0 = { + {0x20, PADCTL_REG_NONE}, + {0x24, PADCTL_REG_NONE}, + {PADCTL_REG_NONE, PADCTL_REG_NONE} + }, + .usb2_bchrg_bias_pad_0 = 0x28, + .usb2_bchrg_tdcd_dbnc_timer_0 = 0x2c, + .iophy_pll_p0_ctlY_0 = {0x30, 0x34, 0x38, 0x3c}, + .iophy_usb3_padX_ctlY_0 = { + {0x40, 0x48, 0x50, 0x58}, + {0x44, 0x4c, 0x54, 0x5c} + }, + .iophy_misc_pad_pX_ctlY_0 = { + {0x60, 0x68, 0x70, 0x78, 0x80, 0x88}, + {0x64, 0x6c, 0x74, 0x7c, 0x84, 0x8c}, + {PADCTL_REG_NONE, PADCTL_REG_NONE, PADCTL_REG_NONE, + PADCTL_REG_NONE, PADCTL_REG_NONE}, + {PADCTL_REG_NONE, PADCTL_REG_NONE, PADCTL_REG_NONE, + PADCTL_REG_NONE, PADCTL_REG_NONE}, + {PADCTL_REG_NONE, PADCTL_REG_NONE, PADCTL_REG_NONE, + PADCTL_REG_NONE, PADCTL_REG_NONE} + }, + .usb2_otg_padX_ctlY_0 = { + {0x90, 0x98}, + {0x94, 0x9c}, + {PADCTL_REG_NONE, PADCTL_REG_NONE} + }, + .usb2_bias_pad_ctlY_0 = {0xa0, 0xa4}, + .usb2_hsic_padX_ctlY_0 = { + {0xa8, 0xb0, 0xb8}, + {0xac, 0xb4, 0xbc} + }, + .ulpi_link_trim_ctl0 = 0xc0, + .ulpi_null_clk_trim_ctl0 = 0xc4, + .hsic_strb_trim_ctl0 = 0xc8, + .wake_ctl0 = 0xcc, + .pm_spare0 = 0xd0, + .usb3_pad_mux_0 = PADCTL_REG_NONE, + .iophy_pll_s0_ctlY_0 = {PADCTL_REG_NONE, PADCTL_REG_NONE, + PADCTL_REG_NONE, PADCTL_REG_NONE}, + .iophy_misc_pad_s0_ctlY_0 = {PADCTL_REG_NONE, PADCTL_REG_NONE, + PADCTL_REG_NONE, PADCTL_REG_NONE, + PADCTL_REG_NONE, PADCTL_REG_NONE}, +}; + +static const struct tegra_xusb_padctl_regs tegra124_padctl_offsets = { + .boot_media_0 = 0x0, + .usb2_pad_mux_0 = 0x4, + .usb2_port_cap_0 = 0x8, + .snps_oc_map_0 = 0xc, + .usb2_oc_map_0 = 0x10, + .ss_port_map_0 = 0x14, + .oc_det_0 = 0x18, + .elpg_program_0 = 0x1c, + .usb2_bchrg_otgpadX_ctlY_0 = { + {0x20, 0x24}, + {0x28, 0x2c}, + {0x30, 0x34} + }, + .usb2_bchrg_bias_pad_0 = 0x38, + .usb2_bchrg_tdcd_dbnc_timer_0 = 0x3c, + .iophy_pll_p0_ctlY_0 = {0x40, 0x44, 0x48, 0x4c}, + .iophy_usb3_padX_ctlY_0 = { + {0x50, 0x58, 0x60, 0x68}, + {0x54, 0x5c, 0x64, 0x6c} + }, + .iophy_misc_pad_pX_ctlY_0 = { + {0x70, 0x78, 0x80, 0x88, 0x90, 0x98}, + {0x74, 0x7c, 0x84, 0x8c, 0x94, 0x9c}, + {0xec, 0xf8, 0x104, 0x110, 0x11c, 0x128}, + {0xf0, 0xfc, 0x108, 0x114, 0x120, 0x12c}, + {0xf4, 0x100, 0x10c, 0x118, 0x124, 0x130} + }, + .usb2_otg_padX_ctlY_0 = { + {0xa0, 0xac}, + {0xa4, 0xb0}, + {0xa8, 0xb4} + }, + .usb2_bias_pad_ctlY_0 = {0xb8, 0xbc}, + .usb2_hsic_padX_ctlY_0 = { + {0xc0, 0xc8, 0xd0}, + {0xc4, 0xcc, 0xd4} + }, + .ulpi_link_trim_ctl0 = 0xd8, + .ulpi_null_clk_trim_ctl0 = 0xdc, + .hsic_strb_trim_ctl0 = 0xe0, + .wake_ctl0 = 0xe4, + .pm_spare0 = 0xe8, + .usb3_pad_mux_0 = 0x134, + .iophy_pll_s0_ctlY_0 = {0x138, 0x13c, 0x140, 0x144}, + .iophy_misc_pad_s0_ctlY_0 = {0x148, 0x14c, 0x150, 0x154, + 0x158, 0x15c}, +}; + +static const struct tegra_xusb_phy_soc_config tegra114_soc_config = { + .has_ctle = false, + .scale_ss_clk = false, + .num_utmi_pads = 2, + .rx_wander = 0x3, + .rx_eq = 0x3928, + .cdr_cntl = 0x26, + .dfe_cntl = 0x002008ee, + .hs_slew = 0xe, + .ls_rslew_pad = {0x3, 0x0, 0x0}, + .hs_disc_lvl = 0x5, + .spare_in = 0x0, + .hsic_port_offset = 5, + .padctl_offsets = &tegra114_padctl_offsets, +}; + +static const struct tegra_xusb_phy_soc_config tegra124_soc_config = { + .has_ctle = true, + .scale_ss_clk = true, + .num_utmi_pads = 3, + .rx_wander = 0xf, + .rx_eq = 0xf070, + .cdr_cntl = 0x24, + .dfe_cntl = 0x002008ee, + .hs_slew = 0xe, + .ls_rslew_pad = {0x3, 0x0, 0x0}, + .hs_disc_lvl = 0x5, + .spare_in = 0x1, + .hsic_port_offset = 6, + .padctl_offsets = &tegra124_padctl_offsets, +}; + +static struct phy_ops tegra_xusb_phy_ops = { + .init = tegra_xusb_phy_init, + .exit = tegra_xusb_phy_exit, + .power_on = tegra_xusb_phy_power_on, + .power_off = tegra_xusb_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct of_device_id tegra_xusb_phy_id_table[] = { + { + .compatible = "nvidia,tegra114-xusb-phy", + .data = &tegra114_soc_config, + }, + { + .compatible = "nvidia,tegra124-xusb-phy", + .data = &tegra124_soc_config, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_xusb_phy_id_table); + +static int tegra_xusb_phy_parse_dt(struct tegra_xusb_phy *tegra) +{ + struct tegra_xusb_phy_board_data *bdata = &tegra->board_data; + struct device_node *np = tegra->dev->of_node; + u32 val; + int ret, i; + + if (of_property_read_u32(np, "nvidia,ss-pads", &val)) { + dev_err(tegra->dev, "Missing SS pad map\n"); + return -EINVAL; + } + bdata->ss_pads = val; + for_each_set_bit(i, &bdata->ss_pads, TEGRA_XUSB_SS_COUNT) { + char prop[sizeof("nvidia,ss-portN-map")]; + + sprintf(prop, "nvidia,ss-port%d-map", i); + ret = of_property_read_u32(np, prop, &val); + if (ret) { + dev_err(tegra->dev, "Missing SS port %d mapping\n", i); + return -EINVAL; + } + bdata->ss_portmap[i] = val; + } + bdata->use_sata_lane = of_property_read_bool(np, + "nvidia,use-sata-lane"); + if (of_property_read_u32(np, "nvidia,hsic-pads", &val)) { + dev_err(tegra->dev, "Missing HSIC pad map\n"); + return -EINVAL; + } + bdata->hsic_pads = val; + for_each_set_bit(i, &bdata->hsic_pads, TEGRA_XUSB_HSIC_COUNT) { + char prop[sizeof("nvidia,hsicN-config")]; + + sprintf(prop, "nvidia,hsic%d-config", i); + ret = of_property_read_u8_array(np, prop, + (u8 *)&bdata->hsic[i], + sizeof(bdata->hsic[i])); + if (ret) { + dev_err(tegra->dev, "Missing hsic %d config\n", i); + return -EINVAL; + } + } + if (of_property_read_u32(np, "nvidia,utmi-pads", &val)) { + dev_err(tegra->dev, "Missing UTMI pad map\n"); + return -EINVAL; + } + bdata->utmi_pads = val; + + return 0; +} + +static void tegra_xusb_phy_read_calib_data(struct tegra_xusb_phy *tegra) +{ + int i; + u32 val; + + val = tegra_read_usb_calibration_data(); + for (i = 0; i < tegra->soc_config->num_utmi_pads; i++) { + tegra->calib_data.hs_curr_level_pad[i] = + USB_CALIB_HS_CURR_LVL_PAD(val, i); + } + tegra->calib_data.hs_term_range_adj = USB_CALIB_HS_TERM_RANGE_ADJ(val); + tegra->calib_data.hs_squelch_level = USB_CALIB_HS_SQUELCH_LVL(val); + tegra->calib_data.hs_iref_cap = USB_CALIB_HS_IREF_CAP(val); +} + +static int tegra_xusb_phy_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct resource *res; + struct tegra_xusb_phy *tegra; + struct phy *phy; + struct phy_provider *phy_provider; + int err, i; + + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return -ENOMEM; + tegra->dev = &pdev->dev; + + match = of_match_device(tegra_xusb_phy_id_table, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "No matching device found\n"); + return -ENODEV; + } + tegra->soc_config = match->data; + + tegra_xusb_phy_read_calib_data(tegra); + err = tegra_xusb_phy_parse_dt(tegra); + if (err) + return err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tegra->padctl_regs = devm_ioremap_resource(&pdev->dev, res); + if (!tegra->padctl_regs) { + dev_err(&pdev->dev, "Failed to map padctl regs\n"); + return -ENOMEM; + } + + tegra->clkrst_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "nvidia,clkrst"); + if (IS_ERR(tegra->clkrst_regs)) { + dev_err(&pdev->dev, "Failed to get clkrst regs\n"); + return PTR_ERR(tegra->clkrst_regs); + } + + if (tegra->board_data.hsic_pads) { + tegra->vddio_hsic = devm_regulator_get(&pdev->dev, + "vddio-hsic"); + if (IS_ERR(tegra->vddio_hsic)) { + dev_err(&pdev->dev, + "Failed to get vddio-hsic regulator\n"); + return PTR_ERR(tegra->vddio_hsic); + } + } + + for_each_set_bit(i, &tegra->board_data.utmi_pads, + TEGRA_XUSB_UTMI_COUNT) { + char reg_name[sizeof("vbusN")]; + + sprintf(reg_name, "vbus%d", i + 1); + tegra->utmi_vbus[i] = devm_regulator_get(&pdev->dev, reg_name); + if (IS_ERR(tegra->utmi_vbus[i])) { + dev_err(&pdev->dev, "Failed to get %s regulator\n", + reg_name); + return PTR_ERR(tegra->utmi_vbus[i]); + } + } + + tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss"); + if (IS_ERR(tegra->ss_rst)) { + dev_err(&pdev->dev, "Failed to get SS reset\n"); + return PTR_ERR(tegra->ss_rst); + } + + tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src"); + if (IS_ERR(tegra->ss_src_clk)) { + dev_err(&pdev->dev, "Failed to get SS source clock\n"); + return PTR_ERR(tegra->ss_src_clk); + } + tegra->hs_src_clk = devm_clk_get(&pdev->dev, "xusb_hs_src"); + if (IS_ERR(tegra->hs_src_clk)) { + dev_err(&pdev->dev, "Failed to get HS source clock\n"); + return PTR_ERR(tegra->hs_src_clk); + } + tegra->fs_src_clk = devm_clk_get(&pdev->dev, "xusb_fs_src"); + if (IS_ERR(tegra->fs_src_clk)) { + dev_err(&pdev->dev, "Failed to get FS source clock\n"); + return PTR_ERR(tegra->fs_src_clk); + } + tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss"); + if (IS_ERR(tegra->ss_clk)) { + dev_err(&pdev->dev, "Failed to get SS clock\n"); + return PTR_ERR(tegra->ss_clk); + } + if (tegra->soc_config->scale_ss_clk) { + tegra->pll_u_480M = devm_clk_get(&pdev->dev, "pll_u_480M"); + if (IS_ERR(tegra->pll_u_480M)) { + dev_err(&pdev->dev, "Failed to get PLL_U_480M\n"); + return PTR_ERR(tegra->pll_u_480M); + } + tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m"); + if (IS_ERR(tegra->clk_m)) { + dev_err(&pdev->dev, "Failed to get clk_m\n"); + return PTR_ERR(tegra->clk_m); + } + } + tegra->plle = devm_clk_get(&pdev->dev, "pll_e"); + if (IS_ERR(tegra->plle)) { + dev_err(&pdev->dev, "Failed to get PLLE\n"); + return PTR_ERR(tegra->plle); + } + + phy = devm_phy_create(&pdev->dev, &tegra_xusb_phy_ops, NULL); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "Failed to create PHY\n"); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, tegra); + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + dev_err(&pdev->dev, "Failed to register PHY provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static struct platform_driver tegra_xusb_phy_driver = { + .probe = tegra_xusb_phy_probe, + .driver = { + .name = "tegra-xusb-phy", + .of_match_table = of_match_ptr(tegra_xusb_phy_id_table), + }, +}; +module_platform_driver(tegra_xusb_phy_driver); + +MODULE_DESCRIPTION("Tegra XUSB PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tegra-xusb-phy"); diff --git a/drivers/phy/phy-tegra-xusb.h b/drivers/phy/phy-tegra-xusb.h new file mode 100644 index 0000000..4cfc98b --- /dev/null +++ b/drivers/phy/phy-tegra-xusb.h @@ -0,0 +1,270 @@ +/* + * NVIDIA Tegra XUSB PHY driver + * + * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __PHY_TEGRA_XUSB_H +#define __PHY_TEGRA_XUSB_H + +/* SS pads */ +#define TEGRA_XUSB_SS_P0 BIT(0) +#define TEGRA_XUSB_SS_P1 BIT(1) +#define TEGRA_XUSB_SS_COUNT 2 + +/* UTMI pads */ +#define TEGRA_XUSB_UTMI_P0 BIT(0) +#define TEGRA_XUSB_UTMI_P1 BIT(1) +#define TEGRA_XUSB_UTMI_P2 BIT(2) +#define TEGRA_XUSB_UTMI_COUNT 3 + +/* HSIC pads */ +#define TEGRA_XUSB_HSIC_P0 BIT(0) +#define TEGRA_XUSB_HSIC_P1 BIT(1) +#define TEGRA_XUSB_HSIC_COUNT 2 + +#define SS_CLK_HIGH_SPEED 120000000 +#define SS_CLK_LOW_SPEED 12000000 + +/* FUSE_SKU_USB_CALIB_0 bits */ +#define USB_CALIB_HS_CURR_LVL_PAD(reg, p) \ + (((p) ? (reg) >> 15 : (reg)) & 0x3f) +#define USB_CALIB_HS_TERM_RANGE_ADJ(reg) (((reg) >> 7) & 0xf) +#define USB_CALIB_HS_SQUELCH_LVL(reg) (((reg) >> 11) & 0x3) +#define USB_CALIB_HS_IREF_CAP(reg) (((reg) >> 13) & 0x3) + +/* CLKRST bits */ +#define SATA_PLL_CFG0_0 0x490 +#define SATA_PADPLL_USE_LOCKDET BIT(2) +#define SATA_PADPLL_RESET_SWCTL BIT(0) +#define SATA_SEQ_ENABLE BIT(24) +#define SATA_SEQ_START_STATE BIT(25) + +/* XUSB_PADCTL bits */ +/* USB2_PAD_MUX_0 */ +#define USB2_OTG_PAD_PORT_MASK(p) (0x3 << (2 * (p))) +#define USB2_OTG_PAD_PORT_OWNER_SNPS(p) (0x0 << (2 * (p))) +#define USB2_OTG_PAD_PORT_OWNER_XUSB(p) (0x1 << (2 * (p))) +#define USB2_OTG_PAD_PORT_OWNER_UART(p) (0x2 << (2 * (p))) +#define USB2_ULPI_PAD_PORT (0x1 << 12) +#define USB2_ULPI_PAD_PORT_OWNER_SNPS (0x0 << 12) +#define USB2_ULPI_PAD_PORT_OWNER_XUSB (0x1 << 12) +#define USB2_HSIC_PAD_PORT(p) (0x1 << (14 + (p))) +#define USB2_HSIC_PAD_PORT_OWNER_SNPS(p) (0x0 << (14 + (p))) +#define USB2_HSIC_PAD_PORT_OWNER_XUSB(p) (0x1 << (14 + (p))) +/* USB2_PORT_CAP_0 */ +#define USB2_PORT_CAP_MASK(p) (0x3 << (4 * (p))) +#define USB2_PORT_CAP_HOST(p) (0x1 << (4 * (p))) +#define USB2_ULPI_PORT_CAP (0x1 << 24) +/* USB2_OC_MAP_0 */ +#define SNPS_OC_MAP_CTRL1 (0x7 << 0) +#define SNPS_OC_MAP_CTRL2 (0x7 << 3) +#define SNPS_OC_MAP_CTRL3 (0x7 << 6) +#define USB2_OC_MAP_PORT0 (0x7 << 0) +#define USB2_OC_MAP_PORT1 (0x7 << 3) +#define USB2_OC_MAP_PORT2 (0x7 << 6) +#define USB2_OC_MAP_PORT(p) (0x7 << ((p) * 3)) +#define USB2_OC_MAP_PORT0_OC_DETECTED_VBUS_PAD0 (0x4 << 0) +#define USB2_OC_MAP_PORT1_OC_DETECTED_VBUS_PAD1 (0x5 << 3) +/* OC_DET_0 */ +#define OC_DET_VBUS_ENABLE0_OC_MAP (0x7 << 10) +#define OC_DET_VBUS_ENABLE1_OC_MAP (0x7 << 13) +#define OC_DET_VBUS_ENABLE2_OC_MAP (0x7 << 5) +#define OC_DET_VBUS_ENABLE_OC_MAP(p) \ + ((p) == 2 ? OC_DET_VBUS_ENABLE2_OC_MAP : \ + (p) ? OC_DET_VBUS_ENABLE1_OC_MAP : \ + OC_DET_VBUS_ENABLE0_OC_MAP) +#define OC_DET_VBUS_EN0_OC_DETECTED_VBUS_PAD0 (0x4 << 10) +#define OC_DET_VBUS_EN1_OC_DETECTED_VBUS_PAD1 (0x5 << 13) +#define OC_DET_VBUS_EN2_OC_DETECTED_VBUS_PAD2 (0x6 << 5) +#define OC_DET_VBUS_EN_OC_DETECTED_VBUS_PAD(p) \ + ((p) == 2 ? OC_DET_VBUS_EN2_OC_DETECTED_VBUS_PAD2 : \ + (p) ? OC_DET_VBUS_EN1_OC_DETECTED_VBUS_PAD1 : \ + OC_DET_VBUS_EN0_OC_DETECTED_VBUS_PAD0) +#define OC_DET_OC_DETECTED_VBUS_PAD0 BIT(20) +#define OC_DET_OC_DETECTED_VBUS_PAD1 BIT(21) +#define OC_DET_OC_DETECTED_VBUS_PAD2 BIT(22) +#define OC_DET_OC_DETECTED_VBUS_PAD(p) BIT(20 + (p)) +/* SS_PORT_MAP_0 */ +#define SS_PORT_MAP_SHIFT(p) (4 * (p)) +#define SS_PORT_MAP_MASK(p) (0xf << SS_PORT_MAP_SHIFT(p)) +#define SS_PORT_MAP_USB2_PORT0 0x0 +#define SS_PORT_MAP_USB2_PORT1 0x1 +#define SS_PORT_MAP_USB2_PORT2 0x2 +/* USB2_OTG_PAD_CTL0_0 */ +#define USB2_OTG_HS_CURR_LVL(x) ((x) & 0x3f) +#define USB2_OTG_HS_SLEW(x) (((x) & 0x3f) << 6) +#define USB2_OTG_FS_SLEW(x) (((x) & 0x3) << 12) +#define USB2_OTG_LS_RSLEW(x) (((x) & 0x3) << 14) +#define USB2_OTG_LS_FSLEW(x) (((x) & 0x3) << 16) +#define USB2_OTG_PD BIT(19) +#define USB2_OTG_PD2 BIT(20) +#define USB2_OTG_PD_ZI BIT(21) +/* USB2_OTG_PAD_CTL1_0 */ +#define USB2_OTG_PD_CHRP_FORCE_POWERUP BIT(0) +#define USB2_OTG_PD_DISC_FORCE_POWERUP BIT(1) +#define USB2_OTG_PD_DR BIT(2) +#define USB2_OTG_TERM_RANGE_AD(x) (((x) & 0xf) << 3) +#define USB2_OTG_HS_IREF_CAP(x) (((x) & 0x3) << 9) +/* USB2_BIAS_PAD_CTL0_0 */ +#define USB2_BIAS_HS_SQUELCH_LEVEL(x) ((x) & 0x3) +#define USB2_BIAS_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2) +#define USB2_BIAS_PD BIT(12) +#define USB2_BIAS_PD_TRK BIT(13) +/* USB2_BIAS_PAD_CTL1_0 */ +#define USB2_BIAS_RCTRL_VAL(reg) ((reg) & 0xffff) +#define USB2_BIAS_TCTRL_VAL(reg) (((reg) >> 16) & 0xffff) +/* USB2_HSIC_PAD_CTL0_0 */ +#define USB2_HSIC_TX_RTUNEP(x) (((x) & 0xf) << 0) +#define USB2_HSIC_TX_RTUNEN(x) (((x) & 0xf) << 4) +#define USB2_HSIC_TX_SLEWP(x) (((x) & 0xf) << 8) +#define USB2_HSIC_TX_SLEWN(x) (((x) & 0xf) << 12) +#define USB2_HSIC_OPT(x) (((x) & 0xf) << 16) +/* USB2_HSIC_PAD_CTL1_0 */ +#define USB2_HSIC_AUTO_TERM_EN BIT(0) +#define USB2_HSIC_IDDQ BIT(1) +#define USB2_HSIC_PD_TX BIT(2) +#define USB2_HSIC_PD_TRX BIT(3) +#define USB2_HSIC_PD_RX BIT(4) +#define USB2_HSIC_PD_ZI BIT(5) +#define USB2_HISC_LPBK BIT(6) +#define USB2_HSIC_RPD_DATA BIT(7) +#define USB2_HSIC_RPD_STROBE BIT(8) +#define USB2_HSIC_RPU_DATA BIT(9) +#define USB2_HSIC_RPU_STROBE BIT(10) +/* USB2_HSIC_PAD_CTL2_0 */ +#define USB2_HSIC_RX_DATA_TRIM(x) (((x) & 0xf) << 0) +#define USB2_HSIC_RX_STROBE_TRIM(x) (((x) & 0xf) << 4) +#define USB2_HSIC_CALIOUT(x) (((x) & 0xffff) << 16) +/* HSIC_STRB_TRIM_CONTROL_0 */ +#define HSIC_STRB_TRIM(x) ((x) & 0x3f) +/* IOPHY_MISC_PAD_CTL2_0 */ +#define IOPHY_SPARE_IN(x) (((x) & 0x3) << 28) +/* IOPHY_MISC_PAD_CTL3_0 */ +#define IOPHY_MISC_CNTL(x) (((x) & 0xf) << 0) +#define IOPHY_TX_SEL_LOAD(x) (((x) & 0xf) << 8) +#define IOPHY_TX_RDET_T(x) (((x) & 0x3) << 12) +#define IOPHY_RX_IDLE_T(x) (((x) & 0x3) << 14) +#define IOPHY_TX_RDET_BYP BIT(16) +#define IOPHY_RX_IDLE_BYP BIT(17) +#define IOPHY_RX_IDLE_MODE BIT(18) +#define IOPHY_RX_IDLE_MODE_OVRD BIT(19) +#define IOPHY_CDR_TEST(x) (((x) & 0xfff) << 20) +/* IOPHY_MISC_PAD_CTL5_0 */ +#define IOPHY_RX_QEYE_EN BIT(8) +/* IOPHY_MISC_PAD_CTL6_0 */ +#define IOPHY_MISC_OUT_SEL(x) (((x) & 0xff) << 16) +#define IOPHY_MISC_OUT_SEL_TAP 0x32 +#define IOPHY_MISC_OUT_SEL_AMP 0x33 +#define IOPHY_MISC_OUT_SEL_LATCH_G_Z 0xa1 +#define IOPHY_MISC_OUT_SEL_G_Z 0x21 +#define IOPHY_MISC_OUT_SEL_CTLE_Z 0x48 +#define IOPHY_MISC_OUT_TAP_VAL(reg) (((reg) >> 24) & 0x1f) +#define IOPHY_MISC_OUT_AMP_VAL(reg) (((reg) >> 24) & 0x7f) +#define IOPHY_MISC_OUT_G_Z_VAL(reg) (((reg) >> 24) & 0x3f) +/* IOPHY_USB3_PAD_CTL2_0 */ +#define IOPHY_USB3_RX_WANDER(x) (((x) & 0xf) << 4) +#define IOPHY_USB3_RX_EQ_G(x) (((x) & 0x3f) << 8) +#define IOPHY_USB3_RX_EQ_Z(x) (((x) & 0x3f) << 16) +#define IOPHY_USB3_RX_EQ(x) (((x) & 0xffff) << 8) +#define IOPHY_USB3_CDR_CNTL(x) (((x) & 0xff) << 24) +/* IOPHY_USB3_PAD_CTL4_0 */ +#define IOPHY_USB3_DFE_CNTL_TAP(x) (((x) & 0x1f) << 24) +#define IOPHY_USB3_DFE_CNTL_AMP(x) (((x) & 0x7f) << 16) +/* IOPHY_PLL_CTL1_0 */ +#define IOPHY_PLL_PLL0_REFCLK_NDIV(x) (((x) & 0x3) << 20) +/* IOPHY_PLL_CTL2_0 */ +#define IOPHY_PLL_XDIGCLK_SEL(x) ((x) & 0x7) +#define IOPHY_PLL_TXCLKREF_SEL BIT(4) +#define IOPHY_PLL_TCLKOUT_EN BIT(12) +#define IOPHY_PLL_PLL0_CP_CNTL(x) (((x) & 0xf) << 16) +#define IOPHY_PLL_PLL1_CP_CNTL(x) (((x) & 0xf) << 20) +/* IOPHY_PLL_CTL3_0 */ +#define IOPHY_PLL_RCAL_BYPASS BIT(7) +/* USB3_PAD_MUX_0 */ +#define USB3_FORCE_PCIE_PAD_IDDQ_DISABLE_MASK(l) BIT(1 + (l)) +#define USB3_FORCE_SATA_PAD_IDDQ_DISABLE_MASK BIT(6) +#define USB3_PCIE_PAD_LANE_OWNER(l, p) (((p) & 0x3) << (16 + 2 * (l))) +#define USB3_SATA_PAD_LANE_OWNER(p) (((p) & 0x3) << 26) +#define USB3_LANE_OWNER_PCIE 0x0 +#define USB3_LANE_OWNER_USB3_SS 0x1 +#define USB3_LANE_OWNER_SATA 0x2 +/* ELPG_PROGRAM_0 */ +#define USB2_PORT0_WAKE_INTERRUPT_ENABLE BIT(0) +#define USB2_PORT1_WAKE_INTERRUPT_ENABLE BIT(1) +#define USB2_PORT2_WAKE_INTERRUPT_ENABLE BIT(2) +#define USB2_PORT_WAKE_INTERRUPT_ENABLE(p) BIT(p) +#define USB2_HSIC_PORT0_WAKE_INTERRUPT_ENABLE BIT(3) +#define USB2_HSIC_PORT1_WAKE_INTERRUPT_ENABLE BIT(4) +#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(p) BIT(3 + (p)) +#define SS_PORT0_WAKE_INTERRUPT_ENABLE BIT(6) +#define SS_PORT1_WAKE_INTERRUPT_ENABLE BIT(7) +#define SS_PORT_WAKE_INTERRUPT_ENABLE(p) BIT(6 + (p)) +#define USB2_PORT0_WAKEUP_EVENT BIT(8) +#define USB2_PORT1_WAKEUP_EVENT BIT(9) +#define USB2_PORT2_WAKEUP_EVENT BIT(10) +#define USB2_PORT_WAKEUP_EVENT(p) BIT(8 + (p)) +#define USB2_HSIC_PORT0_WAKEUP_EVENT BIT(11) +#define USB2_HSIC_PORT1_WAKEUP_EVENT BIT(12) +#define USB2_HSIC_PORT_WAKEUP_EVENT(p) BIT(11 + (p)) +#define SS_PORT0_WAKEUP_EVENT BIT(14) +#define SS_PORT1_WAKEUP_EVENT BIT(15) +#define SS_PORT_WAKEUP_EVENT(p) BIT(14 + (p)) +#define WAKEUP_EVENT_MASK (0xdf << 8) +#define SSP0_ELPG_CLAMP_EN BIT(16) +#define SSP0_ELPG_CLAMP_EN_EARLY BIT(17) +#define SSP0_ELPG_VCORE_DOWN BIT(18) +#define SSP1_ELPG_CLAMP_EN BIT(20) +#define SSP1_ELPG_CLAMP_EN_EARLY BIT(21) +#define SSP1_ELPG_VCORE_DOWN BIT(22) +#define SSP_ELPG_CLAMP_EN(p) BIT(16 + 4 * (p)) +#define SSP_ELPG_CLAMP_EN_EARLY(p) BIT(17 + 4 * (p)) +#define SSP_ELPG_VCORE_DOWN(p) BIT(18 + 4 * (p)) +#define AUX_MUX_LP0_CLAMP_EN BIT(24) +#define AUX_MUX_LP0_CLAMP_EN_EARLY BIT(25) +#define AUX_MUX_LP0_VCORE_DOWN BIT(26) + +#define PADCTL_REG_NONE ((u32)-1) + +struct tegra_xusb_padctl_regs { + u32 boot_media_0; + u32 usb2_pad_mux_0; + u32 usb2_port_cap_0; + u32 snps_oc_map_0; + u32 usb2_oc_map_0; + u32 ss_port_map_0; + u32 oc_det_0; + u32 elpg_program_0; + u32 usb2_bchrg_otgpadX_ctlY_0[3][2]; + u32 usb2_bchrg_bias_pad_0; + u32 usb2_bchrg_tdcd_dbnc_timer_0; + u32 iophy_pll_p0_ctlY_0[4]; + u32 iophy_usb3_padX_ctlY_0[2][4]; + u32 iophy_misc_pad_pX_ctlY_0[5][6]; + u32 usb2_otg_padX_ctlY_0[3][2]; + u32 usb2_bias_pad_ctlY_0[2]; + u32 usb2_hsic_padX_ctlY_0[2][3]; + u32 ulpi_link_trim_ctl0; + u32 ulpi_null_clk_trim_ctl0; + u32 hsic_strb_trim_ctl0; + u32 wake_ctl0; + u32 pm_spare0; + u32 usb3_pad_mux_0; + u32 iophy_pll_s0_ctlY_0[4]; + u32 iophy_misc_pad_s0_ctlY_0[6]; +}; + +#endif /* __PHY_TEGRA_XUSB_H */