Add support for XUSB device mode controller on Tegra210.
Update PADCTL driver to set port cap based on DT config.
Add code to handle property "nvidia,usb3-port-fake"
Provide API's to control vbus override and utmi pad power control

Signed-off-by: Nagarjuna Kristam <nkris...@nvidia.com>
---
 drivers/phy/tegra/xusb-tegra210.c | 297 +++++++++++++++++++++++++++++++++++++-
 drivers/phy/tegra/xusb.c          |  75 +++++++++-
 drivers/phy/tegra/xusb.h          |  16 +-
 include/linux/phy/tegra/xusb.h    |   7 +-
 4 files changed, 386 insertions(+), 9 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c 
b/drivers/phy/tegra/xusb-tegra210.c
index 05bee32..0289e10 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2018, NVIDIA CORPORATION.  All rights reserved.
  * Copyright (C) 2015 Google, Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -47,7 +47,10 @@
 #define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB 0x1
 
 #define XUSB_PADCTL_USB2_PORT_CAP 0x008
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DISABLED(x) (0x0 << ((x) * 4))
 #define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(x) (0x1 << ((x) * 4))
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DEVICE(x) (0x2 << ((x) * 4))
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_OTG(x) (0x3 << ((x) * 4))
 #define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(x) (0x3 << ((x) * 4))
 
 #define XUSB_PADCTL_SS_PORT_MAP 0x014
@@ -55,6 +58,7 @@
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 5)
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 5))
 #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
+#define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
 
 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024
 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
@@ -69,9 +73,14 @@
 #define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
 #define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x)))
 
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL0(x) (0x080 + (x) * 0x40)
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIP (1 << 18)
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIN (1 << 22)
+
 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x084 + (x) * 0x40)
 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT 7
 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL 0x1
 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6)
 
 #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x088 + (x) * 0x40)
@@ -230,6 +239,12 @@
 #define XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(x) (0xa74 + (x) * 0x40)
 #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL 0xfcf01368
 
+#define XUSB_PADCTL_USB2_VBUS_ID 0xc60
+#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON (1 << 14)
+#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT 18
+#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK 0xf
+#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VAL 8
+
 struct tegra210_xusb_fuse_calibration {
        u32 hs_curr_level[4];
        u32 hs_term_range_adj;
@@ -905,6 +920,161 @@ static const struct tegra_xusb_lane_ops 
tegra210_usb2_lane_ops = {
        .remove = tegra210_usb2_lane_remove,
 };
 
+/* must be called under padctl->lock */
+static void tegra210_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl,
+                               struct tegra_xusb_usb2_pad *pad)
+{
+       u32 reg;
+
+       if (pad->enable++ > 0)
+               return;
+
+       dev_dbg(padctl->dev, "power on BIAS PAD & USB2 tracking\n");
+
+       if (clk_prepare_enable(pad->clk))
+               dev_warn(padctl->dev, "failed to enable BIAS PAD & USB2 
tracking\n");
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+       reg &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
+                   XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) |
+                  (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK <<
+                   XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT));
+       reg |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL <<
+                 XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) |
+                (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL <<
+                 XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT);
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+       reg &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+
+       reg &= ~((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));
+       reg |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL <<
+                 XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
+
+       if (tegra_sku_info.revision < TEGRA_REVISION_A02)
+               reg |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL <<
+                       XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT);
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+       udelay(1);
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+       reg &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+}
+
+/* must be called under padctl->lock */
+static void tegra210_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl,
+                               struct tegra_xusb_usb2_pad *pad)
+{
+       u32 reg;
+
+       if (WARN_ON(pad->enable == 0))
+               return;
+
+       if (--pad->enable > 0)
+               return;
+
+       dev_dbg(padctl->dev, "power off USB2 tracking\n");
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+       reg |= XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+       clk_disable_unprepare(pad->clk);
+}
+
+void tegra210_utmi_pad_power_on(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane;
+       struct tegra_xusb_usb2_lane *usb2;
+       struct tegra_xusb_usb2_pad *pad;
+       struct tegra_xusb_padctl *padctl;
+       unsigned int index;
+       struct device *dev;
+       u32 reg;
+
+       if (!phy)
+               return;
+
+       lane = phy_get_drvdata(phy);
+       usb2 = to_usb2_lane(lane);
+       pad = to_usb2_pad(lane->pad);
+       padctl = lane->pad->padctl;
+       index = lane->index;
+       dev = padctl->dev;
+
+       if (usb2->powered_on)
+               return;
+
+       mutex_lock(&padctl->lock);
+
+       dev_info(dev, "power on UTMI pads %d\n", index);
+
+       tegra210_utmi_bias_pad_power_on(padctl, pad);
+
+       udelay(2);
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+       reg &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+       reg &= ~XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+
+       usb2->powered_on = true;
+
+       mutex_unlock(&padctl->lock);
+}
+
+void tegra210_utmi_pad_power_down(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane;
+       struct tegra_xusb_usb2_lane *usb2;
+       struct tegra_xusb_usb2_pad *pad;
+       struct tegra_xusb_padctl *padctl;
+       unsigned int index;
+       struct device *dev;
+       u32 reg;
+
+       if (!phy)
+               return;
+
+       lane = phy_get_drvdata(phy);
+       usb2 = to_usb2_lane(lane);
+       pad = to_usb2_pad(lane->pad);
+       padctl = lane->pad->padctl;
+       index = lane->index;
+       dev = padctl->dev;
+
+       if (!usb2->powered_on)
+               return;
+
+       mutex_lock(&padctl->lock);
+
+       dev_info(dev, "power down UTMI pad %d\n", index);
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+       reg |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+       reg |= XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+
+       udelay(2);
+
+       tegra210_utmi_bias_pad_power_off(padctl, pad);
+       usb2->powered_on = false;
+
+       mutex_unlock(&padctl->lock);
+}
+
 static int tegra210_usb2_phy_init(struct phy *phy)
 {
        struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
@@ -948,6 +1118,34 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
        priv = to_tegra210_xusb_padctl(padctl);
 
+       if (port->usb3_port_fake != -1) {
+               value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+               value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
+                                       port->usb3_port_fake);
+               value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(
+                                       port->usb3_port_fake, index);
+               padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+               value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+               value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+               usleep_range(100, 200);
+
+               value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+               value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+               usleep_range(100, 200);
+
+               value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+               value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+       }
+
        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) |
@@ -965,7 +1163,14 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
 
        value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
        value &= ~XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(index);
-       value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index);
+       if (port->port_cap == USB_PORT_DISABLED)
+               value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DISABLED(index);
+       else if (port->port_cap == USB_DEVICE_CAP)
+               value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DEVICE(index);
+       else if (port->port_cap == USB_HOST_CAP)
+               value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index);
+       else if (port->port_cap == USB_OTG_CAP)
+               value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_OTG(index);
        padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
 
        value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
@@ -997,7 +1202,12 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
                             XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
        value &= ~(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK <<
                   XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT);
-       value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18;
+       if (port->port_cap == USB_HOST_CAP)
+               value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18;
+       else
+               value |=
+                     XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL <<
+                     XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT;
        padctl_writel(padctl, value,
                      XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
@@ -1070,6 +1280,34 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
 
        mutex_lock(&padctl->lock);
 
+       if (port->usb3_port_fake != -1) {
+               value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+               value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+               usleep_range(100, 200);
+
+               value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+               value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+               usleep_range(250, 350);
+
+               value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+               value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+               value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+               value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
+                                       port->usb3_port_fake);
+               value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
+                                       port->usb3_port_fake);
+               padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+       }
+
        if (WARN_ON(pad->enable == 0))
                goto out;
 
@@ -1953,6 +2191,55 @@ static const struct tegra_xusb_port_ops 
tegra210_usb3_port_ops = {
        .map = tegra210_usb3_port_map,
 };
 
+static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
+                                             bool set)
+{
+       u32 reg;
+
+       dev_dbg(padctl->dev, "%s vbus override\n", set ? "set" : "clear");
+
+       reg = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+       if (set) {
+               reg |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+               reg &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+                          XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+               reg |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VAL <<
+                        XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+       } else
+               reg &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+       padctl_writel(padctl, reg, XUSB_PADCTL_USB2_VBUS_ID);
+
+       return 0;
+}
+
+static int tegra210_utmi_port_reset_quirk(struct phy *phy)
+{
+       struct tegra_xusb_padctl *padctl;
+       struct tegra_xusb_lane *lane;
+       struct device *dev;
+       u32 reg;
+
+       if (!phy)
+               return -ENODEV;
+
+       lane = phy_get_drvdata(phy);
+       padctl = lane->pad->padctl;
+       dev = padctl->dev;
+
+       reg = padctl_readl(padctl,
+                               XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL0(0));
+       dev_dbg(dev, "BATTERY_CHRG_OTGPADX_CTL0(0): 0x%x\n", reg);
+
+       if ((reg & XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIP) ||
+           (reg & XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIN)) {
+               dev_dbg(dev, "Toggle vbus\n");
+               tegra210_xusb_padctl_vbus_override(padctl, false);
+               tegra210_xusb_padctl_vbus_override(padctl, true);
+               return 1;
+       }
+       return 0;
+}
+
 static int
 tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration 
*fuse)
 {
@@ -2015,6 +2302,10 @@ static const struct tegra_xusb_padctl_ops 
tegra210_xusb_padctl_ops = {
        .remove = tegra210_xusb_padctl_remove,
        .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect,
        .hsic_set_idle = tegra210_hsic_set_idle,
+       .vbus_override = tegra210_xusb_padctl_vbus_override,
+       .utmi_pad_power_on = tegra210_utmi_pad_power_on,
+       .utmi_pad_power_down = tegra210_utmi_pad_power_down,
+       .utmi_port_reset_quirk = tegra210_utmi_port_reset_quirk,
 };
 
 const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = {
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 5b3b886..9a25b5d 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2018, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -546,9 +546,26 @@ static int tegra_xusb_usb2_port_parse_dt(struct 
tegra_xusb_usb2_port *usb2)
 {
        struct tegra_xusb_port *port = &usb2->base;
        struct device_node *np = port->dev.of_node;
+       const char *prop_string;
+       u32 value;
 
        usb2->internal = of_property_read_bool(np, "nvidia,internal");
 
+       usb2->port_cap = USB_PORT_DISABLED; /* default */
+       if (!of_property_read_string(np, "mode", &prop_string)) {
+               if (!strcmp("host", prop_string))
+                       usb2->port_cap = USB_HOST_CAP;
+               else if (!strcmp("device", prop_string))
+                       usb2->port_cap = USB_DEVICE_CAP;
+               else if (!strcmp("otg", prop_string))
+                       usb2->port_cap = USB_OTG_CAP;
+       }
+
+       if (!of_property_read_u32(np, "nvidia,usb3-port-fake", &value))
+               usb2->usb3_port_fake = value;
+       else
+               usb2->usb3_port_fake = -1; /* default */
+
        usb2->supply = devm_regulator_get(&port->dev, "vbus");
        return PTR_ERR_OR_ZERO(usb2->supply);
 }
@@ -976,7 +993,7 @@ int tegra_xusb_padctl_usb3_save_context(struct 
tegra_xusb_padctl *padctl,
        if (padctl->soc->ops->usb3_save_context)
                return padctl->soc->ops->usb3_save_context(padctl, port);
 
-       return -ENOSYS;
+       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context);
 
@@ -986,7 +1003,7 @@ int tegra_xusb_padctl_hsic_set_idle(struct 
tegra_xusb_padctl *padctl,
        if (padctl->soc->ops->hsic_set_idle)
                return padctl->soc->ops->hsic_set_idle(padctl, port, idle);
 
-       return -ENOSYS;
+       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
 
@@ -997,10 +1014,60 @@ int tegra_xusb_padctl_usb3_set_lfps_detect(struct 
tegra_xusb_padctl *padctl,
                return padctl->soc->ops->usb3_set_lfps_detect(padctl, port,
                                                              enable);
 
-       return -ENOSYS;
+       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect);
 
+int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl)
+{
+       if (padctl->soc->ops->vbus_override)
+               return padctl->soc->ops->vbus_override(padctl, true);
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_set_vbus_override);
+
+int tegra_xusb_padctl_clear_vbus_override(struct tegra_xusb_padctl *padctl)
+{
+       if (padctl->soc->ops->vbus_override)
+               return padctl->soc->ops->vbus_override(padctl, false);
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_clear_vbus_override);
+
+void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+
+       if (padctl->soc->ops->utmi_pad_power_on)
+               padctl->soc->ops->utmi_pad_power_on(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on);
+
+void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+
+       if (padctl->soc->ops->utmi_pad_power_down)
+               padctl->soc->ops->utmi_pad_power_down(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_down);
+
+int tegra_phy_xusb_utmi_port_reset_quirk(struct phy *phy)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+
+       if (padctl->soc->ops->utmi_port_reset_quirk)
+               return padctl->soc->ops->utmi_port_reset_quirk(phy);
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset_quirk);
+
 MODULE_AUTHOR("Thierry Reding <tred...@nvidia.com>");
 MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index b49dbc3..74acc08 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2018, NVIDIA CORPORATION.  All rights reserved.
  * Copyright (c) 2015, Google Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@ struct tegra_xusb_usb2_lane {
        struct tegra_xusb_lane base;
 
        u32 hs_curr_level_offset;
+       bool powered_on;
 };
 
 static inline struct tegra_xusb_usb2_lane *
@@ -267,11 +268,20 @@ struct tegra_xusb_port *
 tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type,
                     unsigned int index);
 
+enum tegra_xusb_usb_port_cap {
+       USB_PORT_DISABLED = 0,
+       USB_HOST_CAP,
+       USB_DEVICE_CAP,
+       USB_OTG_CAP,
+};
+
 struct tegra_xusb_usb2_port {
        struct tegra_xusb_port base;
 
        struct regulator *supply;
        bool internal;
+       enum tegra_xusb_usb_port_cap port_cap;
+       int usb3_port_fake;
 };
 
 static inline struct tegra_xusb_usb2_port *
@@ -353,6 +363,10 @@ struct tegra_xusb_padctl_ops {
                             unsigned int index, bool idle);
        int (*usb3_set_lfps_detect)(struct tegra_xusb_padctl *padctl,
                                    unsigned int index, bool enable);
+       int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set);
+       void (*utmi_pad_power_on)(struct phy *phy);
+       void (*utmi_pad_power_down)(struct phy *phy);
+       int (*utmi_port_reset_quirk)(struct phy *phy);
 };
 
 struct tegra_xusb_padctl_soc {
diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h
index 8e1a57a..143af44 100644
--- a/include/linux/phy/tegra/xusb.h
+++ b/include/linux/phy/tegra/xusb.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2016-2018, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -26,5 +26,10 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl 
*padctl,
                                    unsigned int port, bool idle);
 int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
                                           unsigned int port, bool enable);
+int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl);
+int tegra_xusb_padctl_clear_vbus_override(struct tegra_xusb_padctl *padctl);
 
+void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy);
+void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy);
+int tegra_phy_xusb_utmi_port_reset_quirk(struct phy *phy);
 #endif /* PHY_TEGRA_XUSB_H */
-- 
2.7.4

Reply via email to