Hi Nagarjuna,
Please check tegra_xusb_update_usb3_fake_port().
Thanks,
JC
On 5/16/19 2:39 PM, Nagarjuna Kristam wrote:
On Tegra210, usb2 only otg/peripheral ports dont work in device mode.
They need an assosciated usb3 port to work in device mode. Identify
an unused usb3 port and assign it as a fake USB3 port to USB2 only
port whose mode is otg/peripheral.
Based on work by BH Hsieh <bhsieh@xxxxxxxxxx>.
Signed-off-by: Nagarjuna Kristam <nkristam@xxxxxxxxxx>
---
drivers/phy/tegra/xusb-tegra210.c | 56 +++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.c | 69 +++++++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 2 ++
3 files changed, 127 insertions(+)
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 4beebcc..829aca5 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -58,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)
@@ -952,6 +953,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) |
@@ -1086,6 +1115,32 @@ 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(port->usb3_port_fake,
+ XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+ }
+
if (WARN_ON(pad->enable == 0))
goto out;
@@ -2051,6 +2106,7 @@ const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = {
},
},
.ops = &tegra210_xusb_padctl_ops,
+ .need_fake_usb3_port = true,
};
EXPORT_SYMBOL_GPL(tegra210_xusb_padctl_soc);
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 0417213..6618db7 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -808,9 +808,66 @@ static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
}
}
+static int tegra_xusb_find_unused_usb3_port(struct tegra_xusb_padctl *padctl)
+{
+ struct device_node *np;
+ unsigned int i;
+
+ for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
+ np = tegra_xusb_find_port_node(padctl, "usb3", i);
+ if (!np || !of_device_is_available(np))
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static bool tegra_xusb_usb3_port_has_companion(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ unsigned int i;
+ struct tegra_xusb_usb3_port *usb3;
+
+ for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
+ usb3 = tegra_xusb_find_usb3_port(padctl, i);
+ if (usb3 && usb3->port == index)
+ return true;
+ }
+
+ return false;
+}
+
+static int tegra_xusb_update_usb3_fake_port(struct tegra_xusb_usb2_port *usb2)
+{
+ int fake;
+
+ /* Disable usb3_port_fake usage by default and assign if needed */
+ usb2->usb3_port_fake = -1;
+
+ if ((usb2->mode == USB_DR_MODE_OTG ||
+ usb2->mode == USB_DR_MODE_PERIPHERAL) &&
+ !tegra_xusb_usb3_port_has_companion(usb2->base.padctl,
+ usb2->base.index)) {
+ fake = tegra_xusb_find_unused_usb3_port(usb2->base.padctl);
+
+ if (fake < 0) {
+ dev_err(&usb2->base.dev, "no unused USB3 ports available\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(&usb2->base.dev, "Found unused usb3 port: %d\n",
+ fake);
+ usb2->usb3_port_fake = fake;
+ tegra_xusb_find_unused_usb3_port(usb2->base.padctl);
This looks redundant to me.
+ }
+
+ return 0;
+}
+
static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_port *port;
+ struct tegra_xusb_usb2_port *usb2;
unsigned int i;
int err = 0;
@@ -840,6 +897,18 @@ static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl)
goto remove_ports;
}
+ if (padctl->soc->need_fake_usb3_port) {
+ for (i = 0; i < padctl->soc->ports.usb2.count; i++) {
+ usb2 = tegra_xusb_find_usb2_port(padctl, i);
+ if (!usb2)
+ continue;
+
+ err = tegra_xusb_update_usb3_fake_port(usb2);
+ if (err < 0)
+ goto remove_ports;
+ }
+ }
+
list_for_each_entry(port, &padctl->ports, list) {
err = port->ops->enable(port);
if (err < 0)
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index e0028b9f..26dd6d2 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -299,6 +299,7 @@ struct tegra_xusb_usb2_port {
struct regulator *supply;
enum usb_dr_mode mode;
bool internal;
+ int usb3_port_fake;
};
static inline struct tegra_xusb_usb2_port *
@@ -397,6 +398,7 @@ struct tegra_xusb_padctl_soc {
const char * const *supply_names;
unsigned int num_supplies;
+ bool need_fake_usb3_port;
};
struct tegra_xusb_padctl {