On 21-05-2019 08:30, jckuo wrote: > 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. Yes, will remove the same -Nagarjuna >> + } >> + >> + 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 {