phy: tegra: xusb: Add usb3 port fake support on Tegra210
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@nvidia.com>. Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com> Acked-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
parent
ac25b6e9f8
commit
a5be28c365
|
@ -50,6 +50,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)
|
||||
|
@ -944,6 +945,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) |
|
||||
|
@ -1078,6 +1107,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;
|
||||
|
||||
|
@ -2049,6 +2104,7 @@ const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = {
|
|||
.ops = &tegra210_xusb_padctl_ops,
|
||||
.supply_names = tegra210_xusb_padctl_supply_names,
|
||||
.num_supplies = ARRAY_SIZE(tegra210_xusb_padctl_supply_names),
|
||||
.need_fake_usb3_port = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(tegra210_xusb_padctl_soc);
|
||||
|
||||
|
|
|
@ -800,9 +800,62 @@ 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_port_is_companion(struct tegra_xusb_usb2_port *usb2)
|
||||
{
|
||||
unsigned int i;
|
||||
struct tegra_xusb_usb3_port *usb3;
|
||||
struct tegra_xusb_padctl *padctl = usb2->base.padctl;
|
||||
|
||||
for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
|
||||
usb3 = tegra_xusb_find_usb3_port(padctl, i);
|
||||
if (usb3 && usb3->port == usb2->base.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_port_is_companion(usb2)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -832,6 +885,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)
|
||||
|
|
|
@ -291,6 +291,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 *
|
||||
|
@ -389,6 +390,7 @@ struct tegra_xusb_padctl_soc {
|
|||
|
||||
const char * const *supply_names;
|
||||
unsigned int num_supplies;
|
||||
bool need_fake_usb3_port;
|
||||
};
|
||||
|
||||
struct tegra_xusb_padctl {
|
||||
|
|
Loading…
Reference in New Issue