diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-19 16:01:00 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-19 16:01:00 +0100 |
commit | f62c1930674913b18daaa608c348000ff124a481 (patch) | |
tree | 9f26748195ab1348083e6384d2ee2e4a32c5aa01 /drivers | |
parent | 23a73711facabb9dbf36b31ce05196cef5020e0b (diff) | |
parent | b4e19931c98a088fbd80b5c3f892261c9a0e6c23 (diff) | |
download | linux-f62c1930674913b18daaa608c348000ff124a481.tar.bz2 |
Merge tag 'tegra-for-5.7-usb-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into usb-next
Thierry writes:
usb: tegra: Changes for v5.7-rc1
These changes add USB OTG support for the XUSB host and XUSB device
controllers found on NVIDIA Tegra SoCs.
* tag 'tegra-for-5.7-usb-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
usb: gadget: tegra-xudc: Support multiple device modes
usb: gadget: tegra-xudc: Use phy_set_mode() to set/unset device mode
usb: gadget: tegra-xudc: Add usb-phy support
usb: gadget: tegra-xudc: Remove usb-role-switch support
usb: xhci-tegra: Add OTG support
phy: tegra: Select USB_PHY
phy: tegra: Don't use device-managed API to allocate ports
phy: tegra: Fix regulator leak
phy: tegra: Print -EPROBE_DEFER error message at debug level
phy: tegra: xusb: Don't warn on probe defer
phy: tegra: xusb: Add Tegra194 support
phy: tegra: xusb: Protect Tegra186 soc with config
phy: tegra: xusb: Add set_mode support for UTMI phy on Tegra186
phy: tegra: xusb: Add set_mode support for USB 2 phy on Tegra210
phy: tegra: xusb: Add support to get companion USB 3 port
phy: tegra: xusb: Add usb-phy support
phy: tegra: xusb: Add usb-role-switch support
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/phy/tegra/Kconfig | 2 | ||||
-rw-r--r-- | drivers/phy/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/tegra/xusb-tegra124.c | 6 | ||||
-rw-r--r-- | drivers/phy/tegra/xusb-tegra186.c | 265 | ||||
-rw-r--r-- | drivers/phy/tegra/xusb-tegra210.c | 136 | ||||
-rw-r--r-- | drivers/phy/tegra/xusb.c | 263 | ||||
-rw-r--r-- | drivers/phy/tegra/xusb.h | 25 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/tegra-xudc.c | 275 | ||||
-rw-r--r-- | drivers/usb/host/xhci-tegra.c | 228 |
10 files changed, 1022 insertions, 180 deletions
diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig index f9817c3ae85f..a208aca4ba7b 100644 --- a/drivers/phy/tegra/Kconfig +++ b/drivers/phy/tegra/Kconfig @@ -2,6 +2,8 @@ config PHY_TEGRA_XUSB tristate "NVIDIA Tegra XUSB pad controller driver" depends on ARCH_TEGRA + select USB_CONN_GPIO + select USB_PHY help Choose this option if you have an NVIDIA Tegra SoC. diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile index 320dd389f34d..89b84067cb4c 100644 --- a/drivers/phy/tegra/Makefile +++ b/drivers/phy/tegra/Makefile @@ -6,4 +6,5 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c index 98d84920c676..db56c7fbe60b 100644 --- a/drivers/phy/tegra/xusb-tegra124.c +++ b/drivers/phy/tegra/xusb-tegra124.c @@ -1422,6 +1422,8 @@ tegra124_usb2_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = { + .release = tegra_xusb_usb2_port_release, + .remove = tegra_xusb_usb2_port_remove, .enable = tegra124_usb2_port_enable, .disable = tegra124_usb2_port_disable, .map = tegra124_usb2_port_map, @@ -1443,6 +1445,7 @@ tegra124_ulpi_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = { + .release = tegra_xusb_ulpi_port_release, .enable = tegra124_ulpi_port_enable, .disable = tegra124_ulpi_port_disable, .map = tegra124_ulpi_port_map, @@ -1464,6 +1467,7 @@ tegra124_hsic_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = { + .release = tegra_xusb_hsic_port_release, .enable = tegra124_hsic_port_enable, .disable = tegra124_hsic_port_disable, .map = tegra124_hsic_port_map, @@ -1647,6 +1651,8 @@ tegra124_usb3_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = { + .release = tegra_xusb_usb3_port_release, + .remove = tegra_xusb_usb3_port_remove, .enable = tegra124_usb3_port_enable, .disable = tegra124_usb3_port_disable, .map = tegra124_usb3_port_map, diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c index 84c27394c181..5d64f69b39a9 100644 --- a/drivers/phy/tegra/xusb-tegra186.c +++ b/drivers/phy/tegra/xusb-tegra186.c @@ -63,6 +63,10 @@ #define SSPX_ELPG_CLAMP_EN(x) BIT(0 + (x) * 3) #define SSPX_ELPG_CLAMP_EN_EARLY(x) BIT(1 + (x) * 3) #define SSPX_ELPG_VCORE_DOWN(x) BIT(2 + (x) * 3) +#define XUSB_PADCTL_SS_PORT_CFG 0x2c +#define PORTX_SPEED_SUPPORT_SHIFT(x) ((x) * 4) +#define PORTX_SPEED_SUPPORT_MASK (0x3) +#define PORT_SPEED_SUPPORT_GEN1 (0x0) #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x88 + (x) * 0x40) #define HS_CURR_LEVEL(x) ((x) & 0x3f) @@ -301,6 +305,97 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy) tegra186_utmi_bias_pad_power_off(padctl); } +static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, USB2_VBUS_ID); + + if (status) { + value |= VBUS_OVERRIDE; + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_FLOATING; + } else { + value &= ~VBUS_OVERRIDE; + } + + padctl_writel(padctl, value, USB2_VBUS_ID); + + return 0; +} + +static int tegra186_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, USB2_VBUS_ID); + + if (status) { + if (value & VBUS_OVERRIDE) { + value &= ~VBUS_OVERRIDE; + padctl_writel(padctl, value, USB2_VBUS_ID); + usleep_range(1000, 2000); + + value = padctl_readl(padctl, USB2_VBUS_ID); + } + + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_GROUNDED; + } else { + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_FLOATING; + } + + padctl_writel(padctl, value, USB2_VBUS_ID); + + return 0; +} + +static int tegra186_utmi_phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl, + lane->index); + int err = 0; + + mutex_lock(&padctl->lock); + + dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode); + + if (mode == PHY_MODE_USB_OTG) { + if (submode == USB_ROLE_HOST) { + tegra186_xusb_padctl_id_override(padctl, true); + + err = regulator_enable(port->supply); + } else if (submode == USB_ROLE_DEVICE) { + tegra186_xusb_padctl_vbus_override(padctl, true); + } else if (submode == USB_ROLE_NONE) { + /* + * When port is peripheral only or role transitions to + * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not + * enabled. + */ + if (regulator_is_enabled(port->supply)) + regulator_disable(port->supply); + + tegra186_xusb_padctl_id_override(padctl, false); + tegra186_xusb_padctl_vbus_override(padctl, false); + } + } + + mutex_unlock(&padctl->lock); + + return err; +} + static int tegra186_utmi_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); @@ -439,6 +534,7 @@ static const struct phy_ops utmi_phy_ops = { .exit = tegra186_utmi_phy_exit, .power_on = tegra186_utmi_phy_power_on, .power_off = tegra186_utmi_phy_power_off, + .set_mode = tegra186_utmi_phy_set_mode, .owner = THIS_MODULE, }; @@ -503,19 +599,6 @@ static const char * const tegra186_usb2_functions[] = { "xusb", }; -static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = { - TEGRA186_LANE("usb2-0", 0, 0, 0, usb2), - TEGRA186_LANE("usb2-1", 0, 0, 0, usb2), - TEGRA186_LANE("usb2-2", 0, 0, 0, usb2), -}; - -static const struct tegra_xusb_pad_soc tegra186_usb2_pad = { - .name = "usb2", - .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes), - .lanes = tegra186_usb2_lanes, - .ops = &tegra186_usb2_pad_ops, -}; - static int tegra186_usb2_port_enable(struct tegra_xusb_port *port) { return 0; @@ -532,6 +615,8 @@ tegra186_usb2_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = { + .release = tegra_xusb_usb2_port_release, + .remove = tegra_xusb_usb2_port_remove, .enable = tegra186_usb2_port_enable, .disable = tegra186_usb2_port_disable, .map = tegra186_usb2_port_map, @@ -591,6 +676,8 @@ tegra186_usb3_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = { + .release = tegra_xusb_usb3_port_release, + .remove = tegra_xusb_usb3_port_remove, .enable = tegra186_usb3_port_enable, .disable = tegra186_usb3_port_disable, .map = tegra186_usb3_port_map, @@ -635,6 +722,15 @@ static int tegra186_usb3_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CAP); + if (padctl->soc->supports_gen2 && port->disable_gen2) { + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CFG); + value &= ~(PORTX_SPEED_SUPPORT_MASK << + PORTX_SPEED_SUPPORT_SHIFT(index)); + value |= (PORT_SPEED_SUPPORT_GEN1 << + PORTX_SPEED_SUPPORT_SHIFT(index)); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CFG); + } + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value &= ~SSPX_ELPG_VCORE_DOWN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); @@ -765,27 +861,6 @@ static const char * const tegra186_usb3_functions[] = { "xusb", }; -static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = { - TEGRA186_LANE("usb3-0", 0, 0, 0, usb3), - TEGRA186_LANE("usb3-1", 0, 0, 0, usb3), - TEGRA186_LANE("usb3-2", 0, 0, 0, usb3), -}; - -static const struct tegra_xusb_pad_soc tegra186_usb3_pad = { - .name = "usb3", - .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes), - .lanes = tegra186_usb3_lanes, - .ops = &tegra186_usb3_pad_ops, -}; - -static const struct tegra_xusb_pad_soc * const tegra186_pads[] = { - &tegra186_usb2_pad, - &tegra186_usb3_pad, -#if 0 /* TODO implement */ - &tegra186_hsic_pad, -#endif -}; - static int tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) { @@ -802,7 +877,9 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value); if (err) { - dev_err(dev, "failed to read calibration fuse: %d\n", err); + if (err != -EPROBE_DEFER) + dev_err(dev, "failed to read calibration fuse: %d\n", + err); return err; } @@ -857,34 +934,13 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } -static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, - bool status) -{ - u32 value; - - dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); - - value = padctl_readl(padctl, USB2_VBUS_ID); - - if (status) { - value |= VBUS_OVERRIDE; - value &= ~ID_OVERRIDE(~0); - value |= ID_OVERRIDE_FLOATING; - } else { - value &= ~VBUS_OVERRIDE; - } - - padctl_writel(padctl, value, USB2_VBUS_ID); - - return 0; -} - static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = { .probe = tegra186_xusb_padctl_probe, .remove = tegra186_xusb_padctl_remove, .vbus_override = tegra186_xusb_padctl_vbus_override, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) static const char * const tegra186_xusb_padctl_supply_names[] = { "avdd-pll-erefeut", "avdd-usb", @@ -892,6 +948,40 @@ static const char * const tegra186_xusb_padctl_supply_names[] = { "vddio-hsic", }; +static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = { + TEGRA186_LANE("usb2-0", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-1", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-2", 0, 0, 0, usb2), +}; + +static const struct tegra_xusb_pad_soc tegra186_usb2_pad = { + .name = "usb2", + .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes), + .lanes = tegra186_usb2_lanes, + .ops = &tegra186_usb2_pad_ops, +}; + +static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = { + TEGRA186_LANE("usb3-0", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-1", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-2", 0, 0, 0, usb3), +}; + +static const struct tegra_xusb_pad_soc tegra186_usb3_pad = { + .name = "usb3", + .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes), + .lanes = tegra186_usb3_lanes, + .ops = &tegra186_usb3_pad_ops, +}; + +static const struct tegra_xusb_pad_soc * const tegra186_pads[] = { + &tegra186_usb2_pad, + &tegra186_usb3_pad, +#if 0 /* TODO implement */ + &tegra186_hsic_pad, +#endif +}; + const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = { .num_pads = ARRAY_SIZE(tegra186_pads), .pads = tegra186_pads, @@ -916,6 +1006,67 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = { .num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names), }; EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc); +#endif + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) +static const char * const tegra194_xusb_padctl_supply_names[] = { + "avdd-usb", + "vclamp-usb", +}; + +static const struct tegra_xusb_lane_soc tegra194_usb2_lanes[] = { + TEGRA186_LANE("usb2-0", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-1", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-2", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-3", 0, 0, 0, usb2), +}; + +static const struct tegra_xusb_pad_soc tegra194_usb2_pad = { + .name = "usb2", + .num_lanes = ARRAY_SIZE(tegra194_usb2_lanes), + .lanes = tegra194_usb2_lanes, + .ops = &tegra186_usb2_pad_ops, +}; + +static const struct tegra_xusb_lane_soc tegra194_usb3_lanes[] = { + TEGRA186_LANE("usb3-0", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-1", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-2", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-3", 0, 0, 0, usb3), +}; + +static const struct tegra_xusb_pad_soc tegra194_usb3_pad = { + .name = "usb3", + .num_lanes = ARRAY_SIZE(tegra194_usb3_lanes), + .lanes = tegra194_usb3_lanes, + .ops = &tegra186_usb3_pad_ops, +}; + +static const struct tegra_xusb_pad_soc * const tegra194_pads[] = { + &tegra194_usb2_pad, + &tegra194_usb3_pad, +}; + +const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = { + .num_pads = ARRAY_SIZE(tegra194_pads), + .pads = tegra194_pads, + .ports = { + .usb2 = { + .ops = &tegra186_usb2_port_ops, + .count = 4, + }, + .usb3 = { + .ops = &tegra186_usb3_port_ops, + .count = 4, + }, + }, + .ops = &tegra186_xusb_padctl_ops, + .supply_names = tegra194_xusb_padctl_supply_names, + .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names), + .supports_gen2 = true, +}; +EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc); +#endif MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>"); MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver"); diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 394913bb2f20..66bd4613835b 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -236,6 +236,7 @@ #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_FLOATING 8 +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0 struct tegra210_xusb_fuse_calibration { u32 hs_curr_level[4]; @@ -935,6 +936,103 @@ static int tegra210_usb2_phy_exit(struct phy *phy) return tegra210_xusb_padctl_disable(lane->pad->padctl); } +static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + + if (status) { + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } else { + value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + } + + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + + return 0; +} + +static int tegra210_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + + if (status) { + if (value & XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON) { + value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + usleep_range(1000, 2000); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + } + + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } else { + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } + + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + + return 0; +} + +static int tegra210_usb2_phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl, + lane->index); + int err = 0; + + mutex_lock(&padctl->lock); + + dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode); + + if (mode == PHY_MODE_USB_OTG) { + if (submode == USB_ROLE_HOST) { + tegra210_xusb_padctl_id_override(padctl, true); + + err = regulator_enable(port->supply); + } else if (submode == USB_ROLE_DEVICE) { + tegra210_xusb_padctl_vbus_override(padctl, true); + } else if (submode == USB_ROLE_NONE) { + /* + * When port is peripheral only or role transitions to + * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not + * be enabled. + */ + if (regulator_is_enabled(port->supply)) + regulator_disable(port->supply); + + tegra210_xusb_padctl_id_override(padctl, false); + tegra210_xusb_padctl_vbus_override(padctl, false); + } + } + + mutex_unlock(&padctl->lock); + + return err; +} + static int tegra210_usb2_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); @@ -1048,9 +1146,11 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); - err = regulator_enable(port->supply); - if (err) - return err; + if (port->supply && port->mode == USB_DR_MODE_HOST) { + err = regulator_enable(port->supply); + if (err) + return err; + } mutex_lock(&padctl->lock); @@ -1164,6 +1264,7 @@ static const struct phy_ops tegra210_usb2_phy_ops = { .exit = tegra210_usb2_phy_exit, .power_on = tegra210_usb2_phy_power_on, .power_off = tegra210_usb2_phy_power_off, + .set_mode = tegra210_usb2_phy_set_mode, .owner = THIS_MODULE, }; @@ -1852,6 +1953,8 @@ tegra210_usb2_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = { + .release = tegra_xusb_usb2_port_release, + .remove = tegra_xusb_usb2_port_remove, .enable = tegra210_usb2_port_enable, .disable = tegra210_usb2_port_disable, .map = tegra210_usb2_port_map, @@ -1873,6 +1976,7 @@ tegra210_hsic_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = { + .release = tegra_xusb_hsic_port_release, .enable = tegra210_hsic_port_enable, .disable = tegra210_hsic_port_disable, .map = tegra210_hsic_port_map, @@ -2018,35 +2122,13 @@ tegra210_usb3_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = { + .release = tegra_xusb_usb3_port_release, + .remove = tegra_xusb_usb3_port_remove, .enable = tegra210_usb3_port_enable, .disable = tegra210_usb3_port_disable, .map = tegra210_usb3_port_map, }; -static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, - bool status) -{ - u32 value; - - dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); - - value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); - - if (status) { - value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; - value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << - XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); - value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << - XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; - } else { - value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; - } - - padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); - - return 0; -} - static int tegra210_utmi_port_reset(struct phy *phy) { struct tegra_xusb_padctl *padctl; diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index f98ec3922c02..de4a46fe1763 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -66,6 +66,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = { .data = &tegra186_xusb_padctl_soc, }, #endif +#if defined(CONFIG_ARCH_TEGRA_194_SOC) + { + .compatible = "nvidia,tegra194-xusb-padctl", + .data = &tegra194_xusb_padctl_soc, + }, +#endif { } }; MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match); @@ -501,6 +507,10 @@ tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index) static void tegra_xusb_port_release(struct device *dev) { + struct tegra_xusb_port *port = to_tegra_xusb_port(dev); + + if (port->ops->release) + port->ops->release(port); } static struct device_type tegra_xusb_port_type = { @@ -541,6 +551,16 @@ unregister: static void tegra_xusb_port_unregister(struct tegra_xusb_port *port) { + if (!IS_ERR_OR_NULL(port->usb_role_sw)) { + of_platform_depopulate(&port->dev); + usb_role_switch_unregister(port->usb_role_sw); + cancel_work_sync(&port->usb_phy_work); + usb_remove_phy(&port->usb_phy); + } + + if (port->ops->remove) + port->ops->remove(port); + device_unregister(&port->dev); } @@ -551,11 +571,146 @@ static const char *const modes[] = { [USB_DR_MODE_OTG] = "otg", }; +static const char * const usb_roles[] = { + [USB_ROLE_NONE] = "none", + [USB_ROLE_HOST] = "host", + [USB_ROLE_DEVICE] = "device", +}; + +static enum usb_phy_events to_usb_phy_event(enum usb_role role) +{ + switch (role) { + case USB_ROLE_DEVICE: + return USB_EVENT_VBUS; + + case USB_ROLE_HOST: + return USB_EVENT_ID; + + default: + return USB_EVENT_NONE; + } +} + +static void tegra_xusb_usb_phy_work(struct work_struct *work) +{ + struct tegra_xusb_port *port = container_of(work, + struct tegra_xusb_port, + usb_phy_work); + enum usb_role role = usb_role_switch_get_role(port->usb_role_sw); + + usb_phy_set_event(&port->usb_phy, to_usb_phy_event(role)); + + dev_dbg(&port->dev, "%s(): calling notifier for role %s\n", __func__, + usb_roles[role]); + + atomic_notifier_call_chain(&port->usb_phy.notifier, 0, &port->usb_phy); +} + +static int tegra_xusb_role_sw_set(struct usb_role_switch *sw, + enum usb_role role) +{ + struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw); + + dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]); + + schedule_work(&port->usb_phy_work); + + return 0; +} + +static int tegra_xusb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct tegra_xusb_port *port = container_of(otg->usb_phy, + struct tegra_xusb_port, + usb_phy); + + if (gadget != NULL) + schedule_work(&port->usb_phy_work); + + return 0; +} + +static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct tegra_xusb_port *port = container_of(otg->usb_phy, + struct tegra_xusb_port, + usb_phy); + + if (host != NULL) + schedule_work(&port->usb_phy_work); + + return 0; +} + + +static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port) +{ + struct tegra_xusb_lane *lane; + struct usb_role_switch_desc role_sx_desc = { + .fwnode = dev_fwnode(&port->dev), + .set = tegra_xusb_role_sw_set, + }; + int err = 0; + + /* + * USB role switch driver needs parent driver owner info. This is a + * suboptimal solution. TODO: Need to revisit this in a follow-up patch + * where an optimal solution is possible with changes to USB role + * switch driver. + */ + port->dev.driver = devm_kzalloc(&port->dev, + sizeof(struct device_driver), + GFP_KERNEL); + port->dev.driver->owner = THIS_MODULE; + + port->usb_role_sw = usb_role_switch_register(&port->dev, + &role_sx_desc); + if (IS_ERR(port->usb_role_sw)) { + err = PTR_ERR(port->usb_role_sw); + dev_err(&port->dev, "failed to register USB role switch: %d", + err); + return err; + } + + INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work); + usb_role_switch_set_drvdata(port->usb_role_sw, port); + + port->usb_phy.otg = devm_kzalloc(&port->dev, sizeof(struct usb_otg), + GFP_KERNEL); + if (!port->usb_phy.otg) + return -ENOMEM; + + lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index); + + /* + * Assign phy dev to usb-phy dev. Host/device drivers can use phy + * reference to retrieve usb-phy details. + */ + port->usb_phy.dev = &lane->pad->lanes[port->index]->dev; + port->usb_phy.dev->driver = port->padctl->dev->driver; + port->usb_phy.otg->usb_phy = &port->usb_phy; + port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral; + port->usb_phy.otg->set_host = tegra_xusb_set_host; + + err = usb_add_phy_dev(&port->usb_phy); + if (err < 0) { + dev_err(&port->dev, "Failed to add USB PHY: %d\n", err); + return err; + } + + /* populate connector entry */ + of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev); + + return err; +} + 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 *mode; + int err; usb2->internal = of_property_read_bool(np, "nvidia,internal"); @@ -572,7 +727,21 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) usb2->mode = USB_DR_MODE_HOST; } - usb2->supply = devm_regulator_get(&port->dev, "vbus"); + /* usb-role-switch property is mandatory for OTG/Peripheral modes */ + if (usb2->mode == USB_DR_MODE_PERIPHERAL || + usb2->mode == USB_DR_MODE_OTG) { + if (of_property_read_bool(np, "usb-role-switch")) { + err = tegra_xusb_setup_usb_role_switch(port); + if (err < 0) + return err; + } else { + dev_err(&port->dev, "usb-role-switch not found for %s mode", + modes[usb2->mode]); + return -EINVAL; + } + } + + usb2->supply = regulator_get(&port->dev, "vbus"); return PTR_ERR_OR_ZERO(usb2->supply); } @@ -591,7 +760,7 @@ static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL); + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); if (!usb2) { err = -ENOMEM; goto out; @@ -622,6 +791,20 @@ out: return err; } +void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); + + kfree(usb2); +} + +void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); + + regulator_put(usb2->supply); +} + static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi) { struct tegra_xusb_port *port = &ulpi->base; @@ -643,7 +826,7 @@ static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL); + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); if (!ulpi) { err = -ENOMEM; goto out; @@ -674,6 +857,13 @@ out: return err; } +void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_ulpi_port *ulpi = to_ulpi_port(port); + + kfree(ulpi); +} + static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic) { /* XXX */ @@ -691,7 +881,7 @@ static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL); + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); if (!hsic) { err = -ENOMEM; goto out; @@ -722,10 +912,18 @@ out: return err; } +void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_hsic_port *hsic = to_hsic_port(port); + + kfree(hsic); +} + static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) { struct tegra_xusb_port *port = &usb3->base; struct device_node *np = port->dev.of_node; + enum usb_device_speed maximum_speed; u32 value; int err; @@ -739,7 +937,17 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) usb3->internal = of_property_read_bool(np, "nvidia,internal"); - usb3->supply = devm_regulator_get(&port->dev, "vbus"); + if (device_property_present(&port->dev, "maximum-speed")) { + maximum_speed = usb_get_maximum_speed(&port->dev); + if (maximum_speed == USB_SPEED_SUPER) + usb3->disable_gen2 = true; + else if (maximum_speed == USB_SPEED_SUPER_PLUS) + usb3->disable_gen2 = false; + else + return -EINVAL; + } + + usb3->supply = regulator_get(&port->dev, "vbus"); return PTR_ERR_OR_ZERO(usb3->supply); } @@ -759,7 +967,7 @@ static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL); + usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL); if (!usb3) { err = -ENOMEM; goto out; @@ -790,6 +998,20 @@ out: return err; } +void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + + kfree(usb3); +} + +void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + + regulator_put(usb3->supply); +} + static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_port *port, *tmp; @@ -1001,7 +1223,13 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) err = tegra_xusb_setup_ports(padctl); if (err) { - dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err); + const char *level = KERN_ERR; + + if (err == -EPROBE_DEFER) + level = KERN_DEBUG; + + dev_printk(level, &pdev->dev, + dev_fmt("failed to setup XUSB ports: %d\n"), err); goto remove_pads; } @@ -1143,6 +1371,27 @@ int tegra_phy_xusb_utmi_port_reset(struct phy *phy) } EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset); +int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, + unsigned int port) +{ + struct tegra_xusb_usb2_port *usb2; + struct tegra_xusb_usb3_port *usb3; + int i; + + usb2 = tegra_xusb_find_usb2_port(padctl, port); + if (!usb2) + return -EINVAL; + + 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 usb3->base.index; + } + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion); + MODULE_AUTHOR("Thierry Reding <treding@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 da94fcce6307..ea35af747066 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -12,6 +12,7 @@ #include <linux/workqueue.h> #include <linux/usb/otg.h> +#include <linux/usb/role.h> /* legacy entry points for backwards-compatibility */ int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev); @@ -266,9 +267,18 @@ struct tegra_xusb_port { struct list_head list; struct device dev; + struct usb_role_switch *usb_role_sw; + struct work_struct usb_phy_work; + struct usb_phy usb_phy; + const struct tegra_xusb_port_ops *ops; }; +static inline struct tegra_xusb_port *to_tegra_xusb_port(struct device *dev) +{ + return container_of(dev, struct tegra_xusb_port, dev); +} + struct tegra_xusb_lane_map { unsigned int port; const char *type; @@ -303,6 +313,8 @@ to_usb2_port(struct tegra_xusb_port *port) struct tegra_xusb_usb2_port * tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index); +void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port); +void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port); struct tegra_xusb_ulpi_port { struct tegra_xusb_port base; @@ -317,6 +329,8 @@ to_ulpi_port(struct tegra_xusb_port *port) return container_of(port, struct tegra_xusb_ulpi_port, base); } +void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port); + struct tegra_xusb_hsic_port { struct tegra_xusb_port base; }; @@ -327,12 +341,15 @@ to_hsic_port(struct tegra_xusb_port *port) return container_of(port, struct tegra_xusb_hsic_port, base); } +void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port); + struct tegra_xusb_usb3_port { struct tegra_xusb_port base; struct regulator *supply; bool context_saved; unsigned int port; bool internal; + bool disable_gen2; u32 tap1; u32 amp; @@ -349,8 +366,12 @@ to_usb3_port(struct tegra_xusb_port *port) struct tegra_xusb_usb3_port * tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index); +void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port); +void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port); struct tegra_xusb_port_ops { + void (*release)(struct tegra_xusb_port *port); + void (*remove)(struct tegra_xusb_port *port); int (*enable)(struct tegra_xusb_port *port); void (*disable)(struct tegra_xusb_port *port); struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port); @@ -392,6 +413,7 @@ struct tegra_xusb_padctl_soc { const char * const *supply_names; unsigned int num_supplies; + bool supports_gen2; bool need_fake_usb3_port; }; @@ -448,5 +470,8 @@ extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc; #if defined(CONFIG_ARCH_TEGRA_186_SOC) extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc; #endif +#if defined(CONFIG_ARCH_TEGRA_194_SOC) +extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc; +#endif #endif /* __PHY_TEGRA_XUSB_H */ diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 1ff82285ac7a..3a7179e90f4e 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -455,7 +455,6 @@ config USB_TEGRA_XUDC tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller" depends on ARCH_TEGRA || COMPILE_TEST depends on PHY_TEGRA_XUSB - select USB_ROLE_SWITCH help Enables NVIDIA Tegra USB 3.0 device mode controller driver. diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 09eb64e9889a..52a6add961f4 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -26,7 +26,9 @@ #include <linux/reset.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> #include <linux/usb/role.h> +#include <linux/usb/phy.h> #include <linux/workqueue.h> /* XUSB_DEV registers */ @@ -477,17 +479,21 @@ struct tegra_xudc { struct clk_bulk_data *clks; - enum usb_role device_mode; - struct usb_role_switch *usb_role_sw; + bool device_mode; struct work_struct usb_role_sw_work; - struct phy *usb3_phy; - struct phy *utmi_phy; + struct phy **usb3_phy; + struct phy *curr_usb3_phy; + struct phy **utmi_phy; + struct phy *curr_utmi_phy; struct tegra_xudc_save_regs saved_regs; bool suspended; bool powergated; + struct usb_phy **usbphy; + struct notifier_block vbus_nb; + struct completion disconnect_complete; bool selfpowered; @@ -517,6 +523,7 @@ struct tegra_xudc_soc { unsigned int num_supplies; const char * const *clock_names; unsigned int num_clks; + unsigned int num_phys; bool u1_enable; bool u2_enable; bool lpm_enable; @@ -598,19 +605,18 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc) pm_runtime_get_sync(xudc->dev); - err = phy_power_on(xudc->utmi_phy); + err = phy_power_on(xudc->curr_utmi_phy); if (err < 0) dev_err(xudc->dev, "utmi power on failed %d\n", err); - err = phy_power_on(xudc->usb3_phy); + err = phy_power_on(xudc->curr_usb3_phy); if (err < 0) dev_err(xudc->dev, "usb3 phy power on failed %d\n", err); dev_dbg(xudc->dev, "device mode on\n"); - tegra_xusb_padctl_set_vbus_override(xudc->padctl, true); - - xudc->device_mode = USB_ROLE_DEVICE; + phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, + USB_ROLE_DEVICE); } static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) @@ -625,7 +631,7 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) reinit_completion(&xudc->disconnect_complete); - tegra_xusb_padctl_set_vbus_override(xudc->padctl, false); + phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT; @@ -643,8 +649,6 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) xudc_writel(xudc, val, PORTSC); } - xudc->device_mode = USB_ROLE_NONE; - /* Wait for disconnect event. */ if (connected) wait_for_completion(&xudc->disconnect_complete); @@ -652,11 +656,11 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) /* Make sure interrupt handler has completed before powergating. */ synchronize_irq(xudc->irq); - err = phy_power_off(xudc->utmi_phy); + err = phy_power_off(xudc->curr_utmi_phy); if (err < 0) dev_err(xudc->dev, "utmi_phy power off failed %d\n", err); - err = phy_power_off(xudc->usb3_phy); + err = phy_power_off(xudc->curr_usb3_phy); if (err < 0) dev_err(xudc->dev, "usb3_phy power off failed %d\n", err); @@ -668,30 +672,57 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work) struct tegra_xudc *xudc = container_of(work, struct tegra_xudc, usb_role_sw_work); - if (!xudc->usb_role_sw || - usb_role_switch_get_role(xudc->usb_role_sw) == USB_ROLE_DEVICE) + if (xudc->device_mode) tegra_xudc_device_mode_on(xudc); else tegra_xudc_device_mode_off(xudc); +} + +static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc, + struct usb_phy *usbphy) +{ + unsigned int i; + for (i = 0; i < xudc->soc->num_phys; i++) { + if (xudc->usbphy[i] && usbphy == xudc->usbphy[i]) + return i; + } + + dev_info(xudc->dev, "phy index could not be found for shared USB PHY"); + return -1; } -static int tegra_xudc_usb_role_sw_set(struct usb_role_switch *sw, - enum usb_role role) +static int tegra_xudc_vbus_notify(struct notifier_block *nb, + unsigned long action, void *data) { - struct tegra_xudc *xudc = usb_role_switch_get_drvdata(sw); - unsigned long flags; + struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc, + vbus_nb); + struct usb_phy *usbphy = (struct usb_phy *)data; + int phy_index; - dev_dbg(xudc->dev, "%s role is %d\n", __func__, role); + dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event); - spin_lock_irqsave(&xudc->lock, flags); + if ((xudc->device_mode && usbphy->last_event == USB_EVENT_VBUS) || + (!xudc->device_mode && usbphy->last_event != USB_EVENT_VBUS)) { + dev_dbg(xudc->dev, "Same role(%d) received. Ignore", + xudc->device_mode); + return NOTIFY_OK; + } - if (!xudc->suspended) - schedule_work(&xudc->usb_role_sw_work); + xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true : + false; - spin_unlock_irqrestore(&xudc->lock, flags); + phy_index = tegra_xudc_get_phy_index(xudc, usbphy); + dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__, + phy_index); - return 0; + if (!xudc->suspended && phy_index != -1) { + xudc->curr_utmi_phy = xudc->utmi_phy[phy_index]; + xudc->curr_usb3_phy = xudc->usb3_phy[phy_index]; + schedule_work(&xudc->usb_role_sw_work); + } + + return NOTIFY_OK; } static void tegra_xudc_plc_reset_work(struct work_struct *work) @@ -709,9 +740,11 @@ static void tegra_xudc_plc_reset_work(struct work_struct *work) if (pls == PORTSC_PLS_INACTIVE) { dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n"); - tegra_xusb_padctl_set_vbus_override(xudc->padctl, - false); - tegra_xusb_padctl_set_vbus_override(xudc->padctl, true); + phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, + USB_ROLE_NONE); + phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, + USB_ROLE_DEVICE); + xudc->wait_csc = false; } } @@ -730,8 +763,7 @@ static void tegra_xudc_port_reset_war_work(struct work_struct *work) spin_lock_irqsave(&xudc->lock, flags); - if ((xudc->device_mode == USB_ROLE_DEVICE) - && xudc->wait_for_sec_prc) { + if (xudc->device_mode && xudc->wait_for_sec_prc) { pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT; dev_dbg(xudc->dev, "pls = %x\n", pls); @@ -739,7 +771,8 @@ static void tegra_xudc_port_reset_war_work(struct work_struct *work) if (pls == PORTSC_PLS_DISABLED) { dev_dbg(xudc->dev, "toggle vbus\n"); /* PRC doesn't complete in 100ms, toggle the vbus */ - ret = tegra_phy_xusb_utmi_port_reset(xudc->utmi_phy); + ret = tegra_phy_xusb_utmi_port_reset( + xudc->curr_utmi_phy); if (ret == 1) xudc->wait_for_sec_prc = 0; } @@ -1928,6 +1961,7 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget, unsigned long flags; u32 val; int ret; + unsigned int i; if (!driver) return -EINVAL; @@ -1963,6 +1997,10 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget, xudc_writel(xudc, val, CTRL); } + for (i = 0; i < xudc->soc->num_phys; i++) + if (xudc->usbphy[i]) + otg_set_peripheral(xudc->usbphy[i]->otg, gadget); + xudc->driver = driver; unlock: dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); @@ -1978,11 +2016,16 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget) struct tegra_xudc *xudc = to_xudc(gadget); unsigned long flags; u32 val; + unsigned int i; pm_runtime_get_sync(xudc->dev); spin_lock_irqsave(&xudc->lock, flags); + for (i = 0; i < xudc->soc->num_phys; i++) + if (xudc->usbphy[i]) + otg_set_peripheral(xudc->usbphy[i]->otg, NULL); + val = xudc_readl(xudc, CTRL); val &= ~(CTRL_IE | CTRL_ENABLE); xudc_writel(xudc, val, CTRL); @@ -3315,33 +3358,120 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc) xudc_writel(xudc, val, CFG_DEV_SSPI_XFER); } -static int tegra_xudc_phy_init(struct tegra_xudc *xudc) +static int tegra_xudc_phy_get(struct tegra_xudc *xudc) { - int err; + int err = 0, usb3; + unsigned int i; - err = phy_init(xudc->utmi_phy); - if (err < 0) { - dev_err(xudc->dev, "utmi phy init failed: %d\n", err); - return err; - } + xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, + sizeof(*xudc->utmi_phy), GFP_KERNEL); + if (!xudc->utmi_phy) + return -ENOMEM; - err = phy_init(xudc->usb3_phy); - if (err < 0) { - dev_err(xudc->dev, "usb3 phy init failed: %d\n", err); - goto exit_utmi_phy; + xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, + sizeof(*xudc->usb3_phy), GFP_KERNEL); + if (!xudc->usb3_phy) + return -ENOMEM; + + xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, + sizeof(*xudc->usbphy), GFP_KERNEL); + if (!xudc->usbphy) + return -ENOMEM; + + xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify; + + for (i = 0; i < xudc->soc->num_phys; i++) { + char phy_name[] = "usb.-."; + + /* Get USB2 phy */ + snprintf(phy_name, sizeof(phy_name), "usb2-%d", i); + xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name); + if (IS_ERR(xudc->utmi_phy[i])) { + err = PTR_ERR(xudc->utmi_phy[i]); + if (err != -EPROBE_DEFER) + dev_err(xudc->dev, "failed to get usb2-%d phy: %d\n", + i, err); + + goto clean_up; + } else if (xudc->utmi_phy[i]) { + /* Get usb-phy, if utmi phy is available */ + xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev, + xudc->utmi_phy[i]->dev.of_node, + &xudc->vbus_nb); + if (IS_ERR(xudc->usbphy[i])) { + err = PTR_ERR(xudc->usbphy[i]); + dev_err(xudc->dev, "failed to get usbphy-%d: %d\n", + i, err); + goto clean_up; + } + } else if (!xudc->utmi_phy[i]) { + /* if utmi phy is not available, ignore USB3 phy get */ + continue; + } + + /* Get USB3 phy */ + usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i); + if (usb3 < 0) + continue; + + snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3); + xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name); + if (IS_ERR(xudc->usb3_phy[i])) { + err = PTR_ERR(xudc->usb3_phy[i]); + if (err != -EPROBE_DEFER) + dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n", + usb3, err); + + goto clean_up; + } else if (xudc->usb3_phy[i]) + dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3); } - return 0; + return err; + +clean_up: + for (i = 0; i < xudc->soc->num_phys; i++) { + xudc->usb3_phy[i] = NULL; + xudc->utmi_phy[i] = NULL; + xudc->usbphy[i] = NULL; + } -exit_utmi_phy: - phy_exit(xudc->utmi_phy); return err; } static void tegra_xudc_phy_exit(struct tegra_xudc *xudc) { - phy_exit(xudc->usb3_phy); - phy_exit(xudc->utmi_phy); + unsigned int i; + + for (i = 0; i < xudc->soc->num_phys; i++) { + phy_exit(xudc->usb3_phy[i]); + phy_exit(xudc->utmi_phy[i]); + } +} + +static int tegra_xudc_phy_init(struct tegra_xudc *xudc) +{ + int err; + unsigned int i; + + for (i = 0; i < xudc->soc->num_phys; i++) { + err = phy_init(xudc->utmi_phy[i]); + if (err < 0) { + dev_err(xudc->dev, "utmi phy init failed: %d\n", err); + goto exit_phy; + } + + err = phy_init(xudc->usb3_phy[i]); + if (err < 0) { + dev_err(xudc->dev, "usb3 phy init failed: %d\n", err); + goto exit_phy; + } + } + return 0; + +exit_phy: + tegra_xudc_phy_exit(xudc); + return err; } static const char * const tegra210_xudc_supply_names[] = { @@ -3369,6 +3499,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = { .num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names), .clock_names = tegra210_xudc_clock_names, .num_clks = ARRAY_SIZE(tegra210_xudc_clock_names), + .num_phys = 4, .u1_enable = false, .u2_enable = true, .lpm_enable = false, @@ -3381,6 +3512,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = { static struct tegra_xudc_soc tegra186_xudc_soc_data = { .clock_names = tegra186_xudc_clock_names, .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names), + .num_phys = 4, .u1_enable = true, .u2_enable = true, .lpm_enable = false, @@ -3458,7 +3590,6 @@ static int tegra_xudc_probe(struct platform_device *pdev) { struct tegra_xudc *xudc; struct resource *res; - struct usb_role_switch_desc role_sx_desc = { 0 }; unsigned int i; int err; @@ -3544,19 +3675,9 @@ static int tegra_xudc_probe(struct platform_device *pdev) goto put_padctl; } - xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3"); - if (IS_ERR(xudc->usb3_phy)) { - err = PTR_ERR(xudc->usb3_phy); - dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err); - goto disable_regulator; - } - - xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2"); - if (IS_ERR(xudc->utmi_phy)) { - err = PTR_ERR(xudc->utmi_phy); - dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err); + err = tegra_xudc_phy_get(xudc); + if (err) goto disable_regulator; - } err = tegra_xudc_powerdomain_init(xudc); if (err) @@ -3585,25 +3706,6 @@ static int tegra_xudc_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&xudc->port_reset_war_work, tegra_xudc_port_reset_war_work); - if (of_property_read_bool(xudc->dev->of_node, "usb-role-switch")) { - role_sx_desc.set = tegra_xudc_usb_role_sw_set; - role_sx_desc.fwnode = dev_fwnode(xudc->dev); - role_sx_desc.driver_data = xudc; - - xudc->usb_role_sw = usb_role_switch_register(xudc->dev, - &role_sx_desc); - if (IS_ERR(xudc->usb_role_sw)) { - err = PTR_ERR(xudc->usb_role_sw); - dev_err(xudc->dev, "Failed to register USB role SW: %d", - err); - goto free_eps; - } - } else { - /* Set the mode as device mode and this keeps phy always ON */ - dev_info(xudc->dev, "Set usb role to device mode always"); - schedule_work(&xudc->usb_role_sw_work); - } - pm_runtime_enable(&pdev->dev); xudc->gadget.ops = &tegra_xudc_gadget_ops; @@ -3638,15 +3740,12 @@ put_padctl: static int tegra_xudc_remove(struct platform_device *pdev) { struct tegra_xudc *xudc = platform_get_drvdata(pdev); + unsigned int i; pm_runtime_get_sync(xudc->dev); cancel_delayed_work(&xudc->plc_reset_work); - - if (xudc->usb_role_sw) { - usb_role_switch_unregister(xudc->usb_role_sw); - cancel_work_sync(&xudc->usb_role_sw_work); - } + cancel_work_sync(&xudc->usb_role_sw_work); usb_del_gadget_udc(&xudc->gadget); @@ -3657,8 +3756,10 @@ static int tegra_xudc_remove(struct platform_device *pdev) regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); - phy_power_off(xudc->utmi_phy); - phy_power_off(xudc->usb3_phy); + for (i = 0; i < xudc->soc->num_phys; i++) { + phy_power_off(xudc->utmi_phy[i]); + phy_power_off(xudc->usb3_phy[i]); + } tegra_xudc_phy_exit(xudc); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index a6e36b3c968f..2eaf5c0af80c 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -24,6 +24,9 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/usb/otg.h> +#include <linux/usb/phy.h> +#include <linux/usb/role.h> #include <soc/tegra/pmc.h> #include "xhci.h" @@ -204,6 +207,7 @@ struct tegra_xusb_soc { bool scale_ss_clock; bool has_ipfs; bool lpm_support; + bool otg_reset_sspi; }; struct tegra_xusb_context { @@ -251,6 +255,14 @@ struct tegra_xusb { struct phy **phys; unsigned int num_phys; + struct usb_phy **usbphy; + unsigned int num_usb_phys; + int otg_usb2_port; + int otg_usb3_port; + bool host_mode; + struct notifier_block id_nb; + struct work_struct id_work; + /* Firmware loading related */ struct { size_t size; @@ -1082,6 +1094,205 @@ static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) return err; } +static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main, + bool set) +{ + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd; + unsigned int wait = (!main && !set) ? 1000 : 10; + u16 typeReq = set ? SetPortFeature : ClearPortFeature; + u16 wIndex = main ? tegra->otg_usb2_port + 1 : tegra->otg_usb3_port + 1; + u32 status; + u32 stat_power = main ? USB_PORT_STAT_POWER : USB_SS_PORT_STAT_POWER; + u32 status_val = set ? stat_power : 0; + + dev_dbg(tegra->dev, "%s():%s %s port power\n", __func__, + set ? "set" : "clear", main ? "HS" : "SS"); + + hcd->driver->hub_control(hcd, typeReq, USB_PORT_FEAT_POWER, wIndex, + NULL, 0); + + do { + tegra_xhci_hc_driver.hub_control(hcd, GetPortStatus, 0, wIndex, + (char *) &status, sizeof(status)); + if (status_val == (status & stat_power)) + break; + + if (!main && !set) + usleep_range(600, 700); + else + usleep_range(10, 20); + } while (--wait > 0); + + if (status_val != (status & stat_power)) + dev_info(tegra->dev, "failed to %s %s PP %d\n", + set ? "set" : "clear", + main ? "HS" : "SS", status); +} + +static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name, + int port) +{ + unsigned int i, phy_count = 0; + + for (i = 0; i < tegra->soc->num_types; i++) { + if (!strncmp(tegra->soc->phy_types[i].name, "usb2", + strlen(name))) + return tegra->phys[phy_count+port]; + + phy_count += tegra->soc->phy_types[i].num; + } + + return NULL; +} + +static void tegra_xhci_id_work(struct work_struct *work) +{ + struct tegra_xusb *tegra = container_of(work, struct tegra_xusb, + id_work); + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct tegra_xusb_mbox_msg msg; + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", + tegra->otg_usb2_port); + u32 status; + int ret; + + dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off"); + + mutex_lock(&tegra->lock); + + if (tegra->host_mode) + phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); + else + phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); + + mutex_unlock(&tegra->lock); + + if (tegra->host_mode) { + /* switch to host mode */ + if (tegra->otg_usb3_port >= 0) { + if (tegra->soc->otg_reset_sspi) { + /* set PP=0 */ + tegra_xhci_hc_driver.hub_control( + xhci->shared_hcd, GetPortStatus, + 0, tegra->otg_usb3_port+1, + (char *) &status, sizeof(status)); + if (status & USB_SS_PORT_STAT_POWER) + tegra_xhci_set_port_power(tegra, false, + false); + + /* reset OTG port SSPI */ + msg.cmd = MBOX_CMD_RESET_SSPI; + msg.data = tegra->otg_usb3_port+1; + + ret = tegra_xusb_mbox_send(tegra, &msg); + if (ret < 0) { + dev_info(tegra->dev, + "failed to RESET_SSPI %d\n", + ret); + } + } + + tegra_xhci_set_port_power(tegra, false, true); + } + + tegra_xhci_set_port_power(tegra, true, true); + + } else { + if (tegra->otg_usb3_port >= 0) + tegra_xhci_set_port_power(tegra, false, false); + + tegra_xhci_set_port_power(tegra, true, false); + } +} + +static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra, + struct usb_phy *usbphy) +{ + unsigned int i; + + for (i = 0; i < tegra->num_usb_phys; i++) { + if (tegra->usbphy[i] && usbphy == tegra->usbphy[i]) + return i; + } + + return -1; +} + +static int tegra_xhci_id_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct tegra_xusb *tegra = container_of(nb, struct tegra_xusb, + id_nb); + struct usb_phy *usbphy = (struct usb_phy *)data; + + dev_dbg(tegra->dev, "%s(): action is %d", __func__, usbphy->last_event); + + if ((tegra->host_mode && usbphy->last_event == USB_EVENT_ID) || + (!tegra->host_mode && usbphy->last_event != USB_EVENT_ID)) { + dev_dbg(tegra->dev, "Same role(%d) received. Ignore", + tegra->host_mode); + return NOTIFY_OK; + } + + tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy); + tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion( + tegra->padctl, + tegra->otg_usb2_port); + + tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false; + + schedule_work(&tegra->id_work); + + return NOTIFY_OK; +} + +static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra) +{ + unsigned int i; + + tegra->usbphy = devm_kcalloc(tegra->dev, tegra->num_usb_phys, + sizeof(*tegra->usbphy), GFP_KERNEL); + if (!tegra->usbphy) + return -ENOMEM; + + INIT_WORK(&tegra->id_work, tegra_xhci_id_work); + tegra->id_nb.notifier_call = tegra_xhci_id_notify; + + for (i = 0; i < tegra->num_usb_phys; i++) { + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i); + + if (!phy) + continue; + + tegra->usbphy[i] = devm_usb_get_phy_by_node(tegra->dev, + phy->dev.of_node, + &tegra->id_nb); + if (!IS_ERR(tegra->usbphy[i])) { + dev_dbg(tegra->dev, "usbphy-%d registered", i); + otg_set_host(tegra->usbphy[i]->otg, &tegra->hcd->self); + } else { + /* + * usb-phy is optional, continue if its not available. + */ + tegra->usbphy[i] = NULL; + } + } + + return 0; +} + +static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra) +{ + unsigned int i; + + cancel_work_sync(&tegra->id_work); + + for (i = 0; i < tegra->num_usb_phys; i++) + if (tegra->usbphy[i]) + otg_set_host(tegra->usbphy[i]->otg, NULL); +} + static int tegra_xusb_probe(struct platform_device *pdev) { struct tegra_xusb *tegra; @@ -1255,8 +1466,11 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_powerdomains; } - for (i = 0; i < tegra->soc->num_types; i++) + for (i = 0; i < tegra->soc->num_types; i++) { + if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4)) + tegra->num_usb_phys = tegra->soc->phy_types[i].num; tegra->num_phys += tegra->soc->phy_types[i].num; + } tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys, sizeof(*tegra->phys), GFP_KERNEL); @@ -1385,6 +1599,12 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto remove_usb3; } + err = tegra_xusb_init_usb_phy(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err); + goto remove_usb3; + } + return 0; remove_usb3: @@ -1421,6 +1641,8 @@ static int tegra_xusb_remove(struct platform_device *pdev) struct tegra_xusb *tegra = platform_get_drvdata(pdev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + tegra_xusb_deinit_usb_phy(tegra); + usb_remove_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd); xhci->shared_hcd = NULL; @@ -1695,6 +1917,7 @@ static const struct tegra_xusb_soc tegra124_soc = { }, .scale_ss_clock = true, .has_ipfs = true, + .otg_reset_sspi = false, .mbox = { .cmd = 0xe4, .data_in = 0xe8, @@ -1734,6 +1957,7 @@ static const struct tegra_xusb_soc tegra210_soc = { }, .scale_ss_clock = false, .has_ipfs = true, + .otg_reset_sspi = true, .mbox = { .cmd = 0xe4, .data_in = 0xe8, @@ -1774,6 +1998,7 @@ static const struct tegra_xusb_soc tegra186_soc = { }, .scale_ss_clock = false, .has_ipfs = false, + .otg_reset_sspi = false, .mbox = { .cmd = 0xe4, .data_in = 0xe8, @@ -1804,6 +2029,7 @@ static const struct tegra_xusb_soc tegra194_soc = { }, .scale_ss_clock = false, .has_ipfs = false, + .otg_reset_sspi = false, .mbox = { .cmd = 0x68, .data_in = 0x6c, |