new file mode 100644
@@ -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>;
+ };
@@ -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)
@@ -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
new file mode 100644
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/tegra-soc.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/resource.h>
+#include <linux/phy/phy.h>
+#include <linux/usb/tegra_xusb.h>
+
+#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");
new file mode 100644
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 */
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 <abrestic@chromium.org> --- .../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