[PATCH V6 07/12] pinctrl: tegra-xusb: Add USB PHY support

Kishon Vijay Abraham I kishon at ti.com
Tue Nov 25 05:49:31 PST 2014


Hi,

On Tuesday 25 November 2014 05:47 AM, Andrew Bresticker wrote:
> In addition to the PCIe and SATA PHYs, the XUSB pad controller also
> supports 3 UTMI, 2 HSIC, and 2 USB3 PHYs.  Each USB3 PHY uses a single
> PCIe or SATA lane and is mapped to one of the three UTMI ports.
> 
> The xHCI controller will also send messages intended for the PHY driver,
> so request and listen for messages on the mailbox's PHY channel.
> 
> Signed-off-by: Andrew Bresticker <abrestic at chromium.org>
> Acked-by: Linus Walleij <linus.walleij at linaro.org>
> Reviewed-by: Stephen Warren <swarren at nvidia.com>
> ---
> No changes from v5.
> Changes from v4:
>  - Disabled USB support on missing mailbox channel instead of failing
>    to probe.
>  - Made usb3-port a pinconfig property.
>  - Addressed review comments from Thierry.
> No changes from v3.
> Changes from v2:
>  - Added support for nvidia,otg-hs-curr-level-offset property.
>  - Moved mailbox request handling to workqueue.
>  - Added filtering out of non-PHY mailbox messages.
>  - Dropped "-otg" from VBUS supplies.
> Changes from v1:
>  - Updated to use common mailbox API.
>  - Added SATA PHY enable sequence for USB3 ports using the SATA lane.
>  - Made USB3 port-to-lane mappins a top-level binding rather than a pinconfig
>    binding.
> ---
>  drivers/pinctrl/Kconfig              |    1 +
>  drivers/pinctrl/pinctrl-tegra-xusb.c | 1262 +++++++++++++++++++++++++++++++++-
>  include/soc/tegra/xusb.h             |    7 +
>  3 files changed, 1254 insertions(+), 16 deletions(-)

The devm_phy_create() API has changed (see linux-phy next) and the patch that
modified the existing devm_phy_create() in pinctrl-tegra-xusb.c has also been
merged in linux-phy tree.

Thanks
Kishon
> 
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index c6a66de..b2a96f3 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -160,6 +160,7 @@ config PINCTRL_TEGRA124
>  
>  config PINCTRL_TEGRA_XUSB
>  	def_bool y if ARCH_TEGRA
> +	depends on MAILBOX
>  	select GENERIC_PHY
>  	select PINCONF
>  	select PINMUX
> diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
> index 1631ec9..37ad84e 100644
> --- a/drivers/pinctrl/pinctrl-tegra-xusb.c
> +++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
> @@ -13,23 +13,54 @@
>  
>  #include <linux/delay.h>
>  #include <linux/io.h>
> +#include <linux/mailbox_client.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/phy/phy.h>
>  #include <linux/pinctrl/pinctrl.h>
>  #include <linux/pinctrl/pinmux.h>
>  #include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
>  #include <linux/reset.h>
> +#include <linux/workqueue.h>
> +
> +#include <soc/tegra/fuse.h>
> +#include <soc/tegra/xusb.h>
>  
>  #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
>  
>  #include "core.h"
>  #include "pinctrl-utils.h"
>  
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? 15 : 0)
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT 13
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT 11
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
> +
> +#define XUSB_PADCTL_USB2_PORT_CAP 0x008
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_PORT_CAP_DISABLED 0x0
> +#define XUSB_PADCTL_USB2_PORT_CAP_HOST 0x1
> +#define XUSB_PADCTL_USB2_PORT_CAP_DEVICE 0x2
> +#define XUSB_PADCTL_USB2_PORT_CAP_OTG 0x3
> +
> +#define XUSB_PADCTL_SS_PORT_MAP 0x014
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_SS_PORT_MAP_PORT_MASK 0x7
> +
>  #define XUSB_PADCTL_ELPG_PROGRAM 0x01c
>  #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
>  #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
>  #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(x) \
> +							(1 << (17 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
>  
>  #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
>  #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
> @@ -41,17 +72,136 @@
>  #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
>  #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
>  
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(x) (0x058 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK 0xffff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0x7
> +
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(x) (0x068 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK 0x7f
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(x) ((x) < 2 ? 0x078 + (x) * 4 : \
> +					       0x0f8 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT 28
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK 0x3
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(x) ((x) < 2 ? 0x090 + (x) * 4 : \
> +					       0x11c + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN (1 << 8)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(x) ((x) < 2 ? 0x098 + (x) * 4 : \
> +					       0x128 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK 0x7f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z 0x21
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP 0x32
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP 0x33
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z 0x48
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z 0xa1
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x0a0 + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 20)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 19)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT 14
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT 6
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK 0x3f
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x0ac + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT 9
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0x7
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
> +
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0b8
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 12)
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 2
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x3
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x0c0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT 12
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT 8
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x0c8 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE (1 << 10)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA (1 << 9)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE (1 << 8)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA (1 << 7)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI (1 << 5)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX (1 << 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX (1 << 3)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX (1 << 2)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN (1 << 0)
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x0d0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x0e0
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK 0x1f
> +
>  #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
>  #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
>  #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK 0x3
>  #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
>  #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
>  #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
>  
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13c
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN (1 << 12)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL (1 << 4)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT 0
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK 0x7
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS (1 << 7)
> +
>  #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
>  #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
>  #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
>  
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14c
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15c
> +
>  struct tegra_xusb_padctl_function {
>  	const char *name;
>  	const char * const *groups;
> @@ -72,6 +222,16 @@ struct tegra_xusb_padctl_soc {
>  
>  	const struct tegra_xusb_padctl_lane *lanes;
>  	unsigned int num_lanes;
> +
> +	u32 rx_wander;
> +	u32 rx_eq;
> +	u32 cdr_cntl;
> +	u32 dfe_cntl;
> +	u32 hs_slew;
> +	u32 ls_rslew[TEGRA_XUSB_UTMI_PHYS];
> +	u32 hs_discon_level;
> +	u32 spare_in;
> +	unsigned int hsic_port_offset;
>  };
>  
>  struct tegra_xusb_padctl_lane {
> @@ -86,6 +246,22 @@ struct tegra_xusb_padctl_lane {
>  	unsigned int num_funcs;
>  };
>  
> +struct tegra_xusb_fuse_calibration {
> +	u32 hs_curr_level[TEGRA_XUSB_UTMI_PHYS];
> +	u32 hs_iref_cap;
> +	u32 hs_term_range_adj;
> +	u32 hs_squelch_level;
> +};
> +
> +struct tegra_xusb_usb3_port {
> +	unsigned int lane;
> +	bool context_saved;
> +	u32 tap1_val;
> +	u32 amp_val;
> +	u32 ctle_z_val;
> +	u32 ctle_g_val;
> +};
> +
>  struct tegra_xusb_padctl {
>  	struct device *dev;
>  	void __iomem *regs;
> @@ -93,13 +269,25 @@ struct tegra_xusb_padctl {
>  	struct reset_control *rst;
>  
>  	const struct tegra_xusb_padctl_soc *soc;
> +	struct tegra_xusb_fuse_calibration calib;
>  	struct pinctrl_dev *pinctrl;
>  	struct pinctrl_desc desc;
>  
>  	struct phy_provider *provider;
> -	struct phy *phys[2];
> +	struct phy *phys[TEGRA_XUSB_NUM_PHYS];
>  
>  	unsigned int enable;
> +
> +	struct work_struct mbox_req_work;
> +	struct tegra_xusb_mbox_msg mbox_req;
> +	struct mbox_client mbox_client;
> +	struct mbox_chan *mbox_chan;
> +
> +	struct tegra_xusb_usb3_port usb3_ports[TEGRA_XUSB_USB3_PHYS];
> +	unsigned int utmi_enable;
> +	unsigned int hs_curr_level_offset[TEGRA_XUSB_UTMI_PHYS];
> +	struct regulator *vbus[TEGRA_XUSB_UTMI_PHYS];
> +	struct regulator *vddio_hsic;
>  };
>  
>  static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
> @@ -114,6 +302,53 @@ static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
>  	return readl(padctl->regs + offset);
>  }
>  
> +static inline struct tegra_xusb_padctl *
> +mbox_work_to_padctl(struct work_struct *work)
> +{
> +	return container_of(work, struct tegra_xusb_padctl, mbox_req_work);
> +}
> +
> +#define PIN_OTG_0   0
> +#define PIN_OTG_1   1
> +#define PIN_OTG_2   2
> +#define PIN_ULPI_0  3
> +#define PIN_HSIC_0  4
> +#define PIN_HSIC_1  5
> +#define PIN_PCIE_0  6
> +#define PIN_PCIE_1  7
> +#define PIN_PCIE_2  8
> +#define PIN_PCIE_3  9
> +#define PIN_PCIE_4 10
> +#define PIN_SATA_0 11
> +
> +static inline bool lane_is_otg(unsigned int lane)
> +{
> +	return lane >= PIN_OTG_0 && lane <= PIN_OTG_2;
> +}
> +
> +static inline bool lane_is_hsic(unsigned int lane)
> +{
> +	return lane >= PIN_HSIC_0 && lane <= PIN_HSIC_1;
> +}
> +
> +static inline bool lane_is_pcie_or_sata(unsigned int lane)
> +{
> +	return lane >= PIN_PCIE_0 && lane <= PIN_SATA_0;
> +}
> +
> +static int lane_to_usb3_port(struct tegra_xusb_padctl *padctl,
> +			     unsigned int lane)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
> +		if (padctl->usb3_ports[i].lane == lane)
> +			return i;
> +	}
> +
> +	return -EINVAL;
> +}
> +
>  static int tegra_xusb_padctl_get_groups_count(struct pinctrl_dev *pinctrl)
>  {
>  	struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
> @@ -131,6 +366,17 @@ static const char *tegra_xusb_padctl_get_group_name(struct pinctrl_dev *pinctrl,
>  
>  enum tegra_xusb_padctl_param {
>  	TEGRA_XUSB_PADCTL_IDDQ,
> +	TEGRA_XUSB_PADCTL_USB3_PORT,
> +	TEGRA_XUSB_PADCTL_USB2_PORT,
> +	TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM,
> +	TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM,
> +	TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM,
> +	TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN,
> +	TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP,
> +	TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN,
> +	TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP,
> +	TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM,
> +	TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET,
>  };
>  
>  static const struct tegra_xusb_padctl_property {
> @@ -138,6 +384,18 @@ static const struct tegra_xusb_padctl_property {
>  	enum tegra_xusb_padctl_param param;
>  } properties[] = {
>  	{ "nvidia,iddq", TEGRA_XUSB_PADCTL_IDDQ },
> +	{ "nvidia,usb3-port", TEGRA_XUSB_PADCTL_USB3_PORT },
> +	{ "nvidia,usb2-port", TEGRA_XUSB_PADCTL_USB2_PORT },
> +	{ "nvidia,hsic-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM },
> +	{ "nvidia,hsic-rx-strobe-trim", TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM },
> +	{ "nvidia,hsic-rx-data-trim", TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM },
> +	{ "nvidia,hsic-tx-rtune-n", TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN },
> +	{ "nvidia,hsic-tx-rtune-p", TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP },
> +	{ "nvidia,hsic-tx-rslew-n", TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN },
> +	{ "nvidia,hsic-tx-rslew-p", TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP },
> +	{ "nvidia,hsic-auto-term", TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM },
> +	{ "nvidia,otg-hs-curr-level-offset",
> +	  TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET },
>  };
>  
>  #define TEGRA_XUSB_PADCTL_PACK(param, value) ((param) << 16 | (value))
> @@ -322,6 +580,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
>  	const struct tegra_xusb_padctl_lane *lane;
>  	enum tegra_xusb_padctl_param param;
>  	u32 value;
> +	int port;
>  
>  	param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(*config);
>  	lane = &padctl->soc->lanes[group];
> @@ -338,8 +597,136 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
>  			value = 0;
>  		else
>  			value = 1;
> +		break;
>  
> -		*config = TEGRA_XUSB_PADCTL_PACK(param, value);
> +	case TEGRA_XUSB_PADCTL_USB3_PORT:
> +		value = lane_to_usb3_port(padctl, group);
> +		if (value < 0) {
> +			dev_err(padctl->dev,
> +				"Pin %d not mapped to USB3 port\n", group);
> +			return -EINVAL;
> +		}
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_USB2_PORT:
> +		port = lane_to_usb3_port(padctl, group);
> +		if (port < 0) {
> +			dev_err(padctl->dev,
> +				"Pin %d not mapped to USB3 port\n", group);
> +			return -EINVAL;
> +		}
> +
> +		value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP) >>
> +			XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
> +		value &= XUSB_PADCTL_SS_PORT_MAP_PORT_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		value = padctl_readl(padctl,
> +				     XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
> +		value &= XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(port)) >>
> +			XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT;
> +		value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(port)) >>
> +			XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT;
> +		value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> +			XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT;
> +		value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> +			XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT;
> +		value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> +			XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT;
> +		value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)) >>
> +			XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT;
> +		value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM:
> +		if (!lane_is_hsic(group)) {
> +			dev_err(padctl->dev, "Pin %d not an HSIC\n", group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_HSIC_0;
> +		value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +		if (value & XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN)
> +			value = 1;
> +		else
> +			value = 0;
> +		break;
> +
> +	case TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET:
> +		if (!lane_is_otg(group)) {
> +			dev_err(padctl->dev, "Pin %d is not an OTG pad\n",
> +				group);
> +			return -EINVAL;
> +		}
> +
> +		port = group - PIN_OTG_0;
> +		value = padctl->hs_curr_level_offset[port];
>  		break;
>  
>  	default:
> @@ -348,6 +735,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
>  		return -ENOTSUPP;
>  	}
>  
> +	*config = TEGRA_XUSB_PADCTL_PACK(param, value);
>  	return 0;
>  }
>  
> @@ -362,6 +750,7 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
>  	unsigned long value;
>  	unsigned int i;
>  	u32 regval;
> +	int port;
>  
>  	lane = &padctl->soc->lanes[group];
>  
> @@ -385,6 +774,206 @@ static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
>  			padctl_writel(padctl, regval, lane->offset);
>  			break;
>  
> +		case TEGRA_XUSB_PADCTL_USB3_PORT:
> +			if (value >= TEGRA_XUSB_USB3_PHYS) {
> +				dev_err(padctl->dev, "Invalid USB3 port: %lu\n",
> +					value);
> +				return -EINVAL;
> +			}
> +			if (!lane_is_pcie_or_sata(group)) {
> +				dev_err(padctl->dev,
> +					"USB3 port not applicable for pin %d\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			padctl->usb3_ports[value].lane = group;
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_USB2_PORT:
> +			if (value >= TEGRA_XUSB_UTMI_PHYS) {
> +				dev_err(padctl->dev, "Invalid USB2 port: %lu\n",
> +					value);
> +				return -EINVAL;
> +			}
> +			if (!lane_is_pcie_or_sata(group)) {
> +				dev_err(padctl->dev,
> +					"USB2 port not applicable for pin %d\n",
> +					group);
> +				return -EINVAL;
> +			}
> +			port = lane_to_usb3_port(padctl, group);
> +			if (port < 0) {
> +				dev_err(padctl->dev,
> +					"Pin %d not mapped to USB3 port\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			regval = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
> +			regval &= ~(XUSB_PADCTL_SS_PORT_MAP_PORT_MASK <<
> +				    XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port));
> +			regval |= value <<
> +				XUSB_PADCTL_SS_PORT_MAP_PORTX_SHIFT(port);
> +			padctl_writel(padctl, regval, XUSB_PADCTL_SS_PORT_MAP);
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_STROBE_TRIM:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			value &= XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK;
> +			padctl_writel(padctl, value,
> +				      XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_RX_STROBE_TRIM:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL2(port));
> +			regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK <<
> +				    XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT);
> +			regval |= value <<
> +				XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL2(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_RX_DATA_TRIM:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			value &= XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL2(port));
> +			regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK <<
> +				    XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT);
> +			regval |= value <<
> +				XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL2(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEN:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK <<
> +				    XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT);
> +			regval |= value <<
> +				XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_TX_RTUNEP:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK <<
> +				    XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT);
> +			regval |= value <<
> +				XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWN:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK <<
> +				    XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT);
> +			regval |= value <<
> +				XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_TX_RSLEWP:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			value &= XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			regval &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK <<
> +				    XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT);
> +			regval |= value <<
> +				XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL0(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_HSIC_AUTO_TERM:
> +			if (!lane_is_hsic(group)) {
> +				dev_err(padctl->dev, "Pin %d not an HSIC\n",
> +					group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_HSIC_0;
> +			regval = padctl_readl(padctl,
> +					      XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +			if (!value)
> +				regval &= ~XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
> +			else
> +				regval |= XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
> +			padctl_writel(padctl, regval,
> +				      XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +			break;
> +
> +		case TEGRA_XUSB_PADCTL_OTG_HS_CURR_LEVEL_OFFSET:
> +			if (!lane_is_otg(group)) {
> +				dev_err(padctl->dev,
> +					"Pin %d is not an OTG pad\n", group);
> +				return -EINVAL;
> +			}
> +
> +			port = group - PIN_OTG_0;
> +			value &= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK;
> +			padctl->hs_curr_level_offset[port] = value;
> +			break;
> +
>  		default:
>  			dev_err(padctl->dev,
>  				"invalid configuration parameter: %04x\n",
> @@ -671,6 +1260,548 @@ static const struct phy_ops sata_phy_ops = {
>  	.owner = THIS_MODULE,
>  };
>  
> +static int usb3_phy_to_port(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	unsigned int i;
> +
> +	for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
> +		if (phy == padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i])
> +			return i;
> +	}
> +	WARN_ON(1);
> +
> +	return -EINVAL;
> +}
> +
> +static int usb3_phy_power_on(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	int port = usb3_phy_to_port(phy);
> +	unsigned int lane;
> +	u32 value, offset;
> +
> +	if (port < 0)
> +		return port;
> +
> +	lane = padctl->usb3_ports[port].lane;
> +	if (!lane_is_pcie_or_sata(lane)) {
> +		dev_err(padctl->dev, "USB3 PHY %d mapped to invalid lane: %d\n",
> +			port, lane);
> +		return -EINVAL;
> +	}
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> +	value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
> +		   (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT) |
> +		   (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT));
> +	value |= (padctl->soc->rx_wander <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
> +		 (padctl->soc->cdr_cntl <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) |
> +		 (padctl->soc->rx_eq <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT);
> +	if (padctl->usb3_ports[port].context_saved) {
> +		value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
> +			    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> +			   (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
> +			    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
> +		value |= (padctl->usb3_ports[port].ctle_g_val <<
> +			  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> +			 (padctl->usb3_ports[port].ctle_z_val <<
> +			  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
> +	}
> +	padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> +
> +	value = padctl->soc->dfe_cntl;
> +	if (padctl->usb3_ports[port].context_saved) {
> +		value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
> +			    XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> +			   (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
> +			    XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
> +		value |= (padctl->usb3_ports[port].tap1_val <<
> +			  XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> +			 (padctl->usb3_ports[port].amp_val <<
> +			  XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
> +	}
> +	padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
> +
> +	offset = (lane == PIN_SATA_0) ?
> +		XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 :
> +		XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane - PIN_PCIE_0);
> +	value = padctl_readl(padctl, offset);
> +	value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK <<
> +		   XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT);
> +	value |= padctl->soc->spare_in <<
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT;
> +	padctl_writel(padctl, value, offset);
> +
> +	offset = (lane == PIN_SATA_0) ?
> +		XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 :
> +		XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane - PIN_PCIE_0);
> +	value = padctl_readl(padctl, offset);
> +	value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN;
> +	padctl_writel(padctl, value, offset);
> +
> +	/* Enable SATA PHY when SATA lane is used */
> +	if (lane == PIN_SATA_0) {
> +		value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +		value &= ~(XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK <<
> +			   XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT);
> +		value |= 0x2 <<
> +			XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT;
> +		padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> +		value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
> +		value &= ~((XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK <<
> +			    XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
> +			   (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK <<
> +			    XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
> +			   (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK <<
> +			    XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
> +			   XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN);
> +		value |= (0x7 <<
> +			  XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
> +			 (0x8 <<
> +			  XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
> +			 (0x8 <<
> +			  XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
> +			 XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL;
> +		padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
> +
> +		value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
> +		value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS;
> +		padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
> +	}
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> +	value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +	usleep_range(100, 200);
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> +	value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> +	value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +	return 0;
> +}
> +
> +static int usb3_phy_power_off(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	int port = usb3_phy_to_port(phy);
> +	u32 value;
> +
> +	if (port < 0)
> +		return port;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> +	value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +	usleep_range(100, 200);
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> +	value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +	usleep_range(250, 350);
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> +	value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +	return 0;
> +}
> +
> +static int usb3_phy_save_context(struct tegra_xusb_padctl *padctl,
> +				 unsigned int port)
> +{
> +	unsigned int lane = padctl->usb3_ports[port].lane;
> +	u32 value, offset;
> +
> +	if (port >= TEGRA_XUSB_USB3_PHYS)
> +		return -EINVAL;
> +
> +	padctl->usb3_ports[port].context_saved = true;
> +
> +	offset = (lane == PIN_SATA_0) ?
> +		XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 :
> +		XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane - PIN_PCIE_0);
> +
> +	value = padctl_readl(padctl, offset);
> +	value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> +		   XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> +	value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP <<
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> +	padctl_writel(padctl, value, offset);
> +
> +	value = padctl_readl(padctl, offset) >>
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> +	padctl->usb3_ports[port].tap1_val = value &
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;
> +
> +	value = padctl_readl(padctl, offset);
> +	value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> +		   XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> +	value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP <<
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> +	padctl_writel(padctl, value, offset);
> +
> +	value = padctl_readl(padctl, offset) >>
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> +	padctl->usb3_ports[port].amp_val = value &
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
> +	value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> +		   (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
> +	value |= (padctl->usb3_ports[port].tap1_val <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> +		 (padctl->usb3_ports[port].amp_val <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
> +	padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(port));
> +
> +	value = padctl_readl(padctl, offset);
> +	value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> +		   XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> +	value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z <<
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> +	padctl_writel(padctl, value, offset);
> +
> +	value = padctl_readl(padctl, offset);
> +	value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> +		   XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> +	value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z <<
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> +	padctl_writel(padctl, value, offset);
> +
> +	value = padctl_readl(padctl, offset) >>
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> +	padctl->usb3_ports[port].ctle_g_val = value &
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
> +
> +	value = padctl_readl(padctl, offset);
> +	value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> +		   XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> +	value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z <<
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> +	padctl_writel(padctl, value, offset);
> +
> +	value = padctl_readl(padctl, offset) >>
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> +	padctl->usb3_ports[port].ctle_z_val = value &
> +		XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> +	value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> +		   (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
> +		    XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
> +	value |= (padctl->usb3_ports[port].ctle_g_val <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> +		 (padctl->usb3_ports[port].ctle_z_val <<
> +		  XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
> +	padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(port));
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops usb3_phy_ops = {
> +	.init = tegra_xusb_phy_init,
> +	.exit = tegra_xusb_phy_exit,
> +	.power_on = usb3_phy_power_on,
> +	.power_off = usb3_phy_power_off,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int utmi_phy_to_port(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	unsigned int i;
> +
> +	for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
> +		if (phy == padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i])
> +			return i;
> +	}
> +	WARN_ON(1);
> +
> +	return -EINVAL;
> +}
> +
> +static int utmi_phy_power_on(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	int port = utmi_phy_to_port(phy);
> +	int err;
> +	u32 value;
> +
> +	if (port < 0)
> +		return port;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +	value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
> +		    XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
> +		   (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK <<
> +		    XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT));
> +	value |= (padctl->calib.hs_squelch_level <<
> +		  XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
> +		 (padctl->soc->hs_discon_level <<
> +		  XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
> +	value &= ~(XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK <<
> +		   XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(port));
> +	value |= XUSB_PADCTL_USB2_PORT_CAP_HOST <<
> +		XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(port);
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port));
> +	value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK <<
> +		    XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) |
> +		   (XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK <<
> +		    XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT) |
> +		   (XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK <<
> +		    XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT) |
> +		   XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD |
> +		   XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
> +		   XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
> +	value |= (padctl->calib.hs_curr_level[port] +
> +		  padctl->hs_curr_level_offset[port]) <<
> +		XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
> +	value |= padctl->soc->hs_slew <<
> +		XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT;
> +	value |= padctl->soc->ls_rslew[port] <<
> +		XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT;
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port));
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
> +	value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK <<
> +		    XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
> +		   (XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK <<
> +		    XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT) |
> +		   XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR |
> +		   XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP |
> +		   XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP);
> +	value |= (padctl->calib.hs_term_range_adj <<
> +		  XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
> +		 (padctl->calib.hs_iref_cap <<
> +		  XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT);
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
> +
> +	err = regulator_enable(padctl->vbus[port]);
> +	if (err)
> +		return err;
> +
> +	mutex_lock(&padctl->lock);
> +
> +	if (padctl->utmi_enable++ > 0)
> +		goto out;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +	value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +out:
> +	mutex_unlock(&padctl->lock);
> +	return 0;
> +}
> +
> +static int utmi_phy_power_off(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	int port = utmi_phy_to_port(phy);
> +	u32 value;
> +
> +	if (port < 0)
> +		return port;
> +
> +	regulator_disable(padctl->vbus[port]);
> +
> +	mutex_lock(&padctl->lock);
> +
> +	if (WARN_ON(padctl->utmi_enable == 0))
> +		goto out;
> +
> +	if (--padctl->utmi_enable > 0)
> +		goto out;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +	value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
> +	padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +out:
> +	mutex_unlock(&padctl->lock);
> +	return 0;
> +}
> +
> +static const struct phy_ops utmi_phy_ops = {
> +	.init = tegra_xusb_phy_init,
> +	.exit = tegra_xusb_phy_exit,
> +	.power_on = utmi_phy_power_on,
> +	.power_off = utmi_phy_power_off,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int hsic_phy_to_port(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	unsigned int i;
> +
> +	for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
> +		if (phy == padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i])
> +			return i;
> +	}
> +	WARN_ON(1);
> +
> +	return -EINVAL;
> +}
> +
> +static int hsic_phy_power_on(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	int port = hsic_phy_to_port(phy);
> +	int err;
> +	u32 value;
> +
> +	if (port < 0)
> +		return port;
> +
> +	err = regulator_enable(padctl->vddio_hsic);
> +	if (err)
> +		return err;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +	value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE |
> +		   XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA |
> +		   XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
> +		   XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
> +		   XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
> +		   XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX);
> +	value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> +		 XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
> +	padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +
> +	return 0;
> +}
> +
> +static int hsic_phy_power_off(struct phy *phy)
> +{
> +	struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
> +	int port = hsic_phy_to_port(phy);
> +	u32 value;
> +
> +	if (port < 0)
> +		return port;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +	value |= XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
> +		 XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
> +		 XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
> +		 XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX;
> +	padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +
> +	regulator_disable(padctl->vddio_hsic);
> +
> +	return 0;
> +}
> +
> +static void hsic_phy_set_idle(struct tegra_xusb_padctl *padctl,
> +			      unsigned int port, bool idle)
> +{
> +	u32 value;
> +
> +	value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +	if (idle)
> +		value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> +			 XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
> +	else
> +		value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> +			   XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE);
> +	padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(port));
> +}
> +
> +static const struct phy_ops hsic_phy_ops = {
> +	.init = tegra_xusb_phy_init,
> +	.exit = tegra_xusb_phy_exit,
> +	.power_on = hsic_phy_power_on,
> +	.power_off = hsic_phy_power_off,
> +	.owner = THIS_MODULE,
> +};
> +
> +static void tegra_xusb_phy_mbox_work(struct work_struct *work)
> +{
> +	struct tegra_xusb_padctl *padctl = mbox_work_to_padctl(work);
> +	struct tegra_xusb_mbox_msg *msg = &padctl->mbox_req;
> +	struct tegra_xusb_mbox_msg resp;
> +	unsigned int i;
> +	u32 ports;
> +
> +	resp.cmd = 0;
> +	switch (msg->cmd) {
> +	case MBOX_CMD_SAVE_DFE_CTLE_CTX:
> +		resp.data = msg->data;
> +		if (usb3_phy_save_context(padctl, msg->data) < 0)
> +			resp.cmd = MBOX_CMD_NAK;
> +		else
> +			resp.cmd = MBOX_CMD_ACK;
> +		break;
> +	case MBOX_CMD_START_HSIC_IDLE:
> +	case MBOX_CMD_STOP_HSIC_IDLE:
> +		ports = msg->data >> (padctl->soc->hsic_port_offset + 1);
> +		resp.data = msg->data;
> +		resp.cmd = MBOX_CMD_ACK;
> +		for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
> +			if (!(ports & BIT(i)))
> +				continue;
> +			if (msg->cmd == MBOX_CMD_START_HSIC_IDLE)
> +				hsic_phy_set_idle(padctl, i, true);
> +			else
> +				hsic_phy_set_idle(padctl, i, false);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (resp.cmd)
> +		mbox_send_message(padctl->mbox_chan, &resp);
> +}
> +
> +static bool is_phy_mbox_message(u32 cmd)
> +{
> +	switch (cmd) {
> +	case MBOX_CMD_SAVE_DFE_CTLE_CTX:
> +	case MBOX_CMD_START_HSIC_IDLE:
> +	case MBOX_CMD_STOP_HSIC_IDLE:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static void tegra_xusb_phy_mbox_rx(struct mbox_client *cl, void *data)
> +{
> +	struct tegra_xusb_padctl *padctl = dev_get_drvdata(cl->dev);
> +	struct tegra_xusb_mbox_msg *msg = data;
> +
> +	if (is_phy_mbox_message(msg->cmd)) {
> +		padctl->mbox_req = *msg;
> +		schedule_work(&padctl->mbox_req_work);
> +	}
> +}
> +
>  static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
>  					   struct of_phandle_args *args)
>  {
> @@ -680,25 +1811,12 @@ static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
>  	if (args->args_count <= 0)
>  		return ERR_PTR(-EINVAL);
>  
> -	if (index >= ARRAY_SIZE(padctl->phys))
> +	if (index >= ARRAY_SIZE(padctl->phys) && !padctl->phys[index])
>  		return ERR_PTR(-EINVAL);
>  
>  	return padctl->phys[index];
>  }
>  
> -#define PIN_OTG_0   0
> -#define PIN_OTG_1   1
> -#define PIN_OTG_2   2
> -#define PIN_ULPI_0  3
> -#define PIN_HSIC_0  4
> -#define PIN_HSIC_1  5
> -#define PIN_PCIE_0  6
> -#define PIN_PCIE_1  7
> -#define PIN_PCIE_2  8
> -#define PIN_PCIE_3  9
> -#define PIN_PCIE_4 10
> -#define PIN_SATA_0 11
> -
>  static const struct pinctrl_pin_desc tegra124_pins[] = {
>  	PINCTRL_PIN(PIN_OTG_0,  "otg-0"),
>  	PINCTRL_PIN(PIN_OTG_1,  "otg-1"),
> @@ -856,6 +1974,15 @@ static const struct tegra_xusb_padctl_soc tegra124_soc = {
>  	.functions = tegra124_functions,
>  	.num_lanes = ARRAY_SIZE(tegra124_lanes),
>  	.lanes = tegra124_lanes,
> +	.rx_wander = 0xf,
> +	.rx_eq = 0xf070,
> +	.cdr_cntl = 0x24,
> +	.dfe_cntl = 0x002008ee,
> +	.hs_slew = 0xe,
> +	.ls_rslew = {0x3, 0x0, 0x0},
> +	.hs_discon_level = 0x5,
> +	.spare_in = 0x1,
> +	.hsic_port_offset = 6,
>  };
>  
>  static const struct of_device_id tegra_xusb_padctl_of_match[] = {
> @@ -864,6 +1991,80 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
>  };
>  MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
>  
> +static int tegra_xusb_read_fuse_calibration(struct tegra_xusb_padctl *padctl)
> +{
> +	unsigned int i;
> +	int err;
> +	u32 value;
> +
> +	err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
> +	if (err < 0)
> +		return err;
> +
> +	for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
> +		padctl->calib.hs_curr_level[i] =
> +			(value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) &
> +			FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK;
> +	}
> +	padctl->calib.hs_iref_cap =
> +		(value >> FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT) &
> +		FUSE_SKU_CALIB_HS_IREF_CAP_MASK;
> +	padctl->calib.hs_term_range_adj =
> +		(value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) &
> +		FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK;
> +	padctl->calib.hs_squelch_level =
> +		(value >> FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT) &
> +		FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK;
> +
> +	return 0;
> +}
> +
> +static int tegra_xusb_setup_usb(struct tegra_xusb_padctl *padctl)
> +{
> +	struct phy *phy;
> +	unsigned int i;
> +
> +	for (i = 0; i < TEGRA_XUSB_USB3_PHYS; i++) {
> +		phy = devm_phy_create(padctl->dev, NULL, &usb3_phy_ops, NULL);
> +		if (IS_ERR(phy))
> +			return PTR_ERR(phy);
> +
> +		padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i] = phy;
> +		phy_set_drvdata(phy, padctl);
> +	}
> +
> +	for (i = 0; i < TEGRA_XUSB_UTMI_PHYS; i++) {
> +		char reg_name[sizeof("vbus-N")];
> +
> +		sprintf(reg_name, "vbus-%d", i);
> +		padctl->vbus[i] = devm_regulator_get(padctl->dev, reg_name);
> +		if (IS_ERR(padctl->vbus[i]))
> +			return PTR_ERR(padctl->vbus[i]);
> +
> +		phy = devm_phy_create(padctl->dev, NULL, &utmi_phy_ops, NULL);
> +		if (IS_ERR(phy))
> +			return PTR_ERR(phy);
> +
> +		padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i] = phy;
> +		phy_set_drvdata(phy, padctl);
> +	}
> +
> +	padctl->vddio_hsic = devm_regulator_get(padctl->dev, "vddio-hsic");
> +	if (IS_ERR(padctl->vddio_hsic))
> +		return PTR_ERR(padctl->vddio_hsic);
> +
> +	for (i = 0; i < TEGRA_XUSB_HSIC_PHYS; i++) {
> +		phy = devm_phy_create(padctl->dev, NULL, &hsic_phy_ops, NULL);
> +		if (IS_ERR(phy))
> +			return PTR_ERR(phy);
> +
> +		padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i] = phy;
> +		phy_set_drvdata(phy, padctl);
> +	}
> +
> +	return 0;
> +}
> +
>  static int tegra_xusb_padctl_probe(struct platform_device *pdev)
>  {
>  	struct tegra_xusb_padctl *padctl;
> @@ -888,6 +2089,10 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
>  	if (IS_ERR(padctl->regs))
>  		return PTR_ERR(padctl->regs);
>  
> +	err = tegra_xusb_read_fuse_calibration(padctl);
> +	if (err < 0)
> +		return err;
> +
>  	padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
>  	if (IS_ERR(padctl->rst))
>  		return PTR_ERR(padctl->rst);
> @@ -928,6 +2133,26 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
>  	padctl->phys[TEGRA_XUSB_PADCTL_SATA] = phy;
>  	phy_set_drvdata(phy, padctl);
>  
> +	INIT_WORK(&padctl->mbox_req_work, tegra_xusb_phy_mbox_work);
> +	padctl->mbox_client.dev = &pdev->dev;
> +	padctl->mbox_client.tx_block = true;
> +	padctl->mbox_client.tx_tout = 0;
> +	padctl->mbox_client.rx_callback = tegra_xusb_phy_mbox_rx;
> +	padctl->mbox_chan = mbox_request_channel(&padctl->mbox_client, 0);
> +	if (IS_ERR(padctl->mbox_chan)) {
> +		err = PTR_ERR(padctl->mbox_chan);
> +		if (err == -EPROBE_DEFER) {
> +			goto unregister;
> +		} else {
> +			dev_warn(&pdev->dev,
> +				 "failed to get mailbox, USB support disabled");
> +		}
> +	} else {
> +		err = tegra_xusb_setup_usb(padctl);
> +		if (err)
> +			goto unregister;
> +	}
> +
>  	padctl->provider = devm_of_phy_provider_register(&pdev->dev,
>  							 tegra_xusb_padctl_xlate);
>  	if (IS_ERR(padctl->provider)) {
> @@ -950,6 +2175,11 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
>  	struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
>  	int err;
>  
> +	if (!IS_ERR(padctl->mbox_chan)) {
> +		cancel_work_sync(&padctl->mbox_req_work);
> +		mbox_free_channel(padctl->mbox_chan);
> +	}
> +
>  	pinctrl_unregister(padctl->pinctrl);
>  
>  	err = reset_control_assert(padctl->rst);
> diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h
> index 5ce5e12..0136dc1 100644
> --- a/include/soc/tegra/xusb.h
> +++ b/include/soc/tegra/xusb.h
> @@ -10,6 +10,13 @@
>  #ifndef __SOC_TEGRA_XUSB_H__
>  #define __SOC_TEGRA_XUSB_H__
>  
> +#define TEGRA_XUSB_USB3_PHYS 2
> +#define TEGRA_XUSB_UTMI_PHYS 3
> +#define TEGRA_XUSB_HSIC_PHYS 2
> +#define TEGRA_XUSB_NUM_USB_PHYS (TEGRA_XUSB_USB3_PHYS + TEGRA_XUSB_UTMI_PHYS + \
> +				 TEGRA_XUSB_HSIC_PHYS)
> +#define TEGRA_XUSB_NUM_PHYS (TEGRA_XUSB_NUM_USB_PHYS + 2) /* + SATA & PCIe */
> +
>  /* Command requests from the firmware */
>  enum tegra_xusb_mbox_cmd {
>  	MBOX_CMD_MSG_ENABLED = 1,
> 



More information about the linux-arm-kernel mailing list