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.

Signed-off-by: Andrew Bresticker <abres...@chromium.org>
---
 drivers/pinctrl/pinctrl-tegra-xusb.c | 1106 +++++++++++++++++++++++++++++++++-
 1 file changed, 1089 insertions(+), 17 deletions(-)

diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c 
b/drivers/pinctrl/pinctrl-tegra-xusb.c
index 2405646..67056ab 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -14,22 +14,55 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/notifier.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/tegra-soc.h>
+#include <linux/tegra-xusb-mbox.h>
 
 #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
 
 #include "core.h"
 #include "pinctrl-utils.h"
 
+#define TEGRA_XUSB_PADCTL_USB3_PORTS 2
+#define TEGRA_XUSB_PADCTL_UTMI_PORTS 3
+#define TEGRA_XUSB_PADCTL_HSIC_PORTS 2
+
+#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,6 +74,104 @@
 #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)
@@ -52,6 +183,12 @@
 #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 +209,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_PADCTL_UTMI_PORTS];
+       u32 hs_discon_level;
+       u32 spare_in;
+       int hsic_port_offset;
 };
 
 struct tegra_xusb_padctl_lane {
@@ -86,6 +233,22 @@ struct tegra_xusb_padctl_lane {
        unsigned int num_funcs;
 };
 
+struct tegra_xusb_fuse_calibration {
+       u32 hs_curr_level[TEGRA_XUSB_PADCTL_UTMI_PORTS];
+       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 +256,22 @@ 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[9];
 
        unsigned int enable;
+
+       struct tegra_xusb_mbox *mbox;
+       struct notifier_block mbox_nb;
+
+       struct tegra_xusb_usb3_port usb3_ports[TEGRA_XUSB_PADCTL_USB3_PORTS];
+       unsigned int utmi_enable;
+       struct regulator *vbus[TEGRA_XUSB_PADCTL_UTMI_PORTS];
+       struct regulator *vddio_hsic;
 };
 
 static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
@@ -114,6 +286,42 @@ static inline u32 padctl_readl(struct tegra_xusb_padctl 
*padctl,
        return readl(padctl->regs + offset);
 }
 
+#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 is_hsic_lane(unsigned int lane)
+{
+       return lane >= PIN_HSIC_0 && lane <= PIN_HSIC_1;
+}
+
+static inline bool is_pcie_sata_lane(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)
+{
+       int i;
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_USB3_PORTS; i++) {
+               if (padctl->usb3_ports[i].lane == lane)
+                       return i;
+       }
+
+       return -1;
+}
+
 static int tegra_xusb_padctl_get_groups_count(struct pinctrl_dev *pinctrl)
 {
        struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
@@ -131,6 +339,16 @@ 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_NUM,
+       TEGRA_XUSB_PADCTL_USB2_PORT_NUM,
+       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,
 };
 
 static const struct tegra_xusb_padctl_property {
@@ -138,6 +356,16 @@ static const struct tegra_xusb_padctl_property {
        enum tegra_xusb_padctl_param param;
 } properties[] = {
        { "nvidia,iddq", TEGRA_XUSB_PADCTL_IDDQ },
+       { "nvidia,usb3-port-num", TEGRA_XUSB_PADCTL_USB3_PORT_NUM },
+       { "nvidia,usb2-port-num", TEGRA_XUSB_PADCTL_USB2_PORT_NUM },
+       { "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 },
 };
 
 #define TEGRA_XUSB_PADCTL_PACK(param, value) ((param) << 16 | (value))
@@ -321,6 +549,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct 
pinctrl_dev *pinctrl,
        struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
        const struct tegra_xusb_padctl_lane *lane;
        enum tegra_xusb_padctl_param param;
+       int port;
        u32 value;
 
        param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(*config);
@@ -330,7 +559,125 @@ static int tegra_xusb_padctl_pinconf_group_get(struct 
pinctrl_dev *pinctrl,
        case TEGRA_XUSB_PADCTL_IDDQ:
                value = padctl_readl(padctl, lane->offset);
                value = (value >> lane->iddq) & 0x1;
-               *config = TEGRA_XUSB_PADCTL_PACK(param, value);
+               break;
+
+       case TEGRA_XUSB_PADCTL_USB3_PORT_NUM:
+               value = lane_to_usb3_port(padctl, group);
+               if (value < 0) {
+                       dev_err(padctl->dev,
+                               "Lane %d not mapped to USB3 port\n", group);
+                       return -EINVAL;
+               }
+               break;
+
+       case TEGRA_XUSB_PADCTL_USB2_PORT_NUM:
+               port = lane_to_usb3_port(padctl, group);
+               if (port < 0) {
+                       dev_err(padctl->dev,
+                               "Lane %d not mapped to USB2 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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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;
 
        default:
@@ -339,6 +686,7 @@ static int tegra_xusb_padctl_pinconf_group_get(struct 
pinctrl_dev *pinctrl,
                return -ENOTSUPP;
        }
 
+       *config = TEGRA_XUSB_PADCTL_PACK(param, value);
        return 0;
 }
 
@@ -351,7 +699,7 @@ static int tegra_xusb_padctl_pinconf_group_set(struct 
pinctrl_dev *pinctrl,
        const struct tegra_xusb_padctl_lane *lane;
        enum tegra_xusb_padctl_param param;
        unsigned long value;
-       unsigned int i;
+       unsigned int i, port;
        u32 regval;
 
        lane = &padctl->soc->lanes[group];
@@ -372,6 +720,193 @@ 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_NUM:
+                       if (value >= TEGRA_XUSB_PADCTL_USB3_PORTS) {
+                               dev_err(padctl->dev, "Invalid USB3 port: %lu\n",
+                                       value);
+                               return -EINVAL;
+                       }
+                       if (!is_pcie_sata_lane(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_NUM:
+                       if (value >= TEGRA_XUSB_PADCTL_UTMI_PORTS) {
+                               dev_err(padctl->dev, "Invalid USB2 port: %lu\n",
+                                       value);
+                               return -EINVAL;
+                       }
+                       if (!is_pcie_sata_lane(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);
+                               break;
+                       }
+
+                       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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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 (!is_hsic_lane(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;
+
                default:
                        dev_err(padctl->dev,
                                "invalid configuration parameter: %04x\n",
@@ -595,6 +1130,459 @@ 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);
+       int i;
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_USB3_PORTS; i++) {
+               if (phy == padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i])
+                       break;
+       }
+       BUG_ON(i == TEGRA_XUSB_PADCTL_USB3_PORTS);
+
+       return i;
+}
+
+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);
+       int lane = padctl->usb3_ports[port].lane;
+       u32 value, offset;
+
+       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);
+
+       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;
+
+       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 void usb3_phy_save_context(struct tegra_xusb_padctl *padctl, int port)
+{
+       int lane = padctl->usb3_ports[port].lane;
+       u32 value, offset;
+
+       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));
+}
+
+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);
+       int i;
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_UTMI_PORTS; i++) {
+               if (phy == padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i])
+                       break;
+       }
+       BUG_ON(i == TEGRA_XUSB_PADCTL_UTMI_PORTS);
+
+       return i;
+}
+
+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 ret;
+       u32 value;
+
+       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] <<
+               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));
+
+       ret = regulator_enable(padctl->vbus[port]);
+       if (ret)
+               return ret;
+
+       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;
+
+       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);
+       int i;
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_HSIC_PORTS; i++) {
+               if (phy == padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i])
+                       break;
+       }
+       BUG_ON(i == TEGRA_XUSB_PADCTL_HSIC_PORTS);
+
+       return i;
+}
+
+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 ret;
+       u32 value;
+
+       ret = regulator_enable(padctl->vddio_hsic);
+       if (ret)
+               return ret;
+
+       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;
+
+       regulator_disable(padctl->vddio_hsic);
+
+       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));
+
+       return 0;
+}
+
+static void hsic_phy_set_idle(struct tegra_xusb_padctl *padctl, int port,
+                             bool set)
+{
+       u32 value;
+
+       value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(port));
+       if (set)
+               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 int tegra_xusb_phy_mbox_notifier(struct notifier_block *nb,
+                                       unsigned long event, void *p)
+{
+       struct tegra_xusb_padctl *padctl = container_of(nb,
+                                               struct tegra_xusb_padctl,
+                                               mbox_nb);
+       struct tegra_xusb_mbox_msg *msg = (struct tegra_xusb_mbox_msg *)p;
+       u32 ports;
+       int i;
+
+       switch (event) {
+       case MBOX_CMD_SAVE_DFE_CTLE_CTX:
+               msg->data_out = msg->data_in;
+               if (msg->data_in > TEGRA_XUSB_PADCTL_USB3_PORTS) {
+                       msg->cmd_out = MBOX_CMD_NAK;
+               } else {
+                       usb3_phy_save_context(padctl, msg->data_in);
+                       msg->cmd_out = MBOX_CMD_ACK;
+               }
+               return NOTIFY_STOP;
+       case MBOX_CMD_START_HSIC_IDLE:
+       case MBOX_CMD_STOP_HSIC_IDLE:
+               ports = msg->data_in >> (padctl->soc->hsic_port_offset + 1);
+               msg->data_out = msg->data_in;
+               for (i = 0; i < TEGRA_XUSB_PADCTL_HSIC_PORTS; i++) {
+                       if (!(ports & BIT(i)))
+                               continue;
+                       if (event == MBOX_CMD_START_HSIC_IDLE)
+                               hsic_phy_set_idle(padctl, i, true);
+                       else
+                               hsic_phy_set_idle(padctl, i, false);
+               }
+               msg->cmd_out = MBOX_CMD_ACK;
+               return NOTIFY_STOP;
+       default:
+               return NOTIFY_DONE;
+       }
+}
+
 static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
                                           struct of_phandle_args *args)
 {
@@ -610,19 +1598,6 @@ static struct phy *tegra_xusb_padctl_xlate(struct device 
*dev,
        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"),
@@ -780,6 +1755,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[] = {
@@ -788,13 +1772,40 @@ 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)
+{
+       int i, ret;
+       u32 value;
+
+       ret = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_UTMI_PORTS; 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_padctl_probe(struct platform_device *pdev)
 {
        struct tegra_xusb_padctl *padctl;
        const struct of_device_id *match;
        struct resource *res;
        struct phy *phy;
-       int err;
+       int err, i;
 
        padctl = devm_kzalloc(&pdev->dev, sizeof(*padctl), GFP_KERNEL);
        if (!padctl)
@@ -812,6 +1823,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);
@@ -852,6 +1867,54 @@ static int tegra_xusb_padctl_probe(struct platform_device 
*pdev)
        padctl->phys[TEGRA_XUSB_PADCTL_SATA] = phy;
        phy_set_drvdata(phy, padctl);
 
+       for (i = 0; i < TEGRA_XUSB_PADCTL_USB3_PORTS; i++) {
+               phy = devm_phy_create(&pdev->dev, &usb3_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       err = PTR_ERR(phy);
+                       goto unregister;
+               }
+
+               padctl->phys[TEGRA_XUSB_PADCTL_USB3_P0 + i] = phy;
+               phy_set_drvdata(phy, padctl);
+       }
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_UTMI_PORTS; i++) {
+               char reg_name[sizeof("vbus-otg-N")];
+
+               sprintf(reg_name, "vbus-otg-%d", i);
+               padctl->vbus[i] = devm_regulator_get(&pdev->dev, reg_name);
+               if (IS_ERR(padctl->vbus[i])) {
+                       err = PTR_ERR(padctl->vbus[i]);
+                       goto unregister;
+               }
+
+               phy = devm_phy_create(&pdev->dev, &utmi_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       err = PTR_ERR(phy);
+                       goto unregister;
+               }
+
+               padctl->phys[TEGRA_XUSB_PADCTL_UTMI_P0 + i] = phy;
+               phy_set_drvdata(phy, padctl);
+       }
+
+       padctl->vddio_hsic = devm_regulator_get(&pdev->dev, "vddio-hsic");
+       if (IS_ERR(padctl->vddio_hsic)) {
+               err = PTR_ERR(padctl->vddio_hsic);
+               goto unregister;
+       }
+
+       for (i = 0; i < TEGRA_XUSB_PADCTL_HSIC_PORTS; i++) {
+               phy = devm_phy_create(&pdev->dev, &hsic_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       err = PTR_ERR(phy);
+                       goto unregister;
+               }
+
+               padctl->phys[TEGRA_XUSB_PADCTL_HSIC_P0 + i] = phy;
+               phy_set_drvdata(phy, padctl);
+       }
+
        padctl->provider = devm_of_phy_provider_register(&pdev->dev,
                                                         
tegra_xusb_padctl_xlate);
        if (err < 0) {
@@ -859,6 +1922,13 @@ static int tegra_xusb_padctl_probe(struct platform_device 
*pdev)
                goto unregister;
        }
 
+       padctl->mbox = tegra_xusb_mbox_lookup_by_phandle(pdev->dev.of_node,
+                                                        "nvidia,xusb-mbox");
+       if (IS_ERR(padctl->mbox))
+               return PTR_ERR(padctl->mbox);
+       padctl->mbox_nb.notifier_call = tegra_xusb_phy_mbox_notifier;
+       tegra_xusb_mbox_register_notifier(padctl->mbox, &padctl->mbox_nb);
+
        return 0;
 
 unregister:
@@ -873,6 +1943,8 @@ static int tegra_xusb_padctl_remove(struct platform_device 
*pdev)
        struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
        int err;
 
+       tegra_xusb_mbox_unregister_notifier(padctl->mbox, &padctl->mbox_nb);
+
        pinctrl_unregister(padctl->pinctrl);
 
        err = reset_control_assert(padctl->rst);
-- 
2.0.0.526.g5318336

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to