2020-02-04 11:19:58

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 00/19] Tegra XUSB OTG support

This patch series adds OTG support on XUSB hardware used in Tegra210 and
Tegra186 SoCs.

This patchset is composed with :
- dt bindings of XUSB Pad Controller
- dt bindings for XUSB device Driver
- Tegra PHY driver for usb-role-switch and usb-phy
- Tegra XUSB host mode driver to support OTG mode
- Tegra XUSB device mode driver to use usb-phy and multi device mode
- dts for XUSB pad controller
- dts for xudc for Jetson TX1 and TX2
- dts for Jetson-TK1

Tegra Pad controller driver register for role switch updates for
OTG/peripheral capable USB ports and adds usb-phy for that corresponding
USB ports.

Host and Device mode drivers gets usb-phy from USB2's phy and registers
notifier for role changes to perform corresponding role tasks.

Tests done:
- device mode support using micro-B USB cable connection between ubuntu
host and DUT on micro-AB port
- host mode support by connecting pen-drive to micro-AB USB port on DUT
using standard-A to micro-A converter.
- toggling between these 2 modes by hot plugging corresponding cables.

DUT: Jetson-tx1, Jetson tx2.

V4:
- Owner info is updated to port->dev for which USB role switch is used.
- Updated function name in logs, unsigned int usage as per comments.
- usb_phy_event is used instead of usb_role to detect role changes as
notifier arguments of usb-phy is used for charging current updates.
- Added ACKed-by info for corresponding patches.
V3:
- Port and cable names updated in "Tests done" section of cover letter as
per JC inputs.
- Fixed arguments allignments in USB padctl driver.
- Padctl driver aborts if usb-role-switch is not present in dt for
peripheral/otg roles.
- Added Reviewed and ACKed details for corresponding patches.
V2:
- Updated usb-role-switch documentation for Padctl driver.
- Update XUDC bindings doc as suggested by Rob.
- Used standard error codes for error return.
- Added of_platform_depopulate during error and driver removal.
- Updated error variable during phy initialization in XUDC driver.
- Updated Tegra210 soc dtb file as per changes to binding doc.

Nagarjuna Kristam (19):
dt-bindings: phy: tegra-xusb: Add usb-role-switch
dt-bindings: usb: Add NVIDIA Tegra XUSB device mode controller binding
phy: tegra: xusb: Add usb-role-switch support
phy: tegra: xusb: Add usb-phy support
phy: tegra: xusb: Add support to get companion USB 3 port
phy: tegra: xusb: Add set_mode support for USB 2 phy on Tegra210
phy: tegra: xusb: Add set_mode support for UTMI phy on Tegra186
usb: xhci-tegra: Add OTG support
usb: gadget: tegra-xudc: Remove usb-role-switch support
usb: gadget: tegra-xudc: Add usb-phy support
usb: gadget: tegra-xudc: use phy_set_mode to set/unset device mode
usb: gadget: tegra-xudc: support multiple device modes
arm64: tegra: update OTG port entries for jetson-tx1
arm64: tegra: update OTG port entries for jetson-tx2
arm64: tegra: Add xudc node for Tegra210
arm64: tegra: Enable xudc on Jetson TX1
arm64: tegra: Add xudc node for Tegra186
arm64: tegra: Enable xudc node on Jetson TX2
ARM: tegra: Remove USB 2-0 port from Jetson TK1 padctl

.../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 6 +
.../devicetree/bindings/usb/nvidia,tegra-xudc.yaml | 190 ++++++++++++++
arch/arm/boot/dts/tegra124-jetson-tk1.dts | 6 -
arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 23 +-
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 19 ++
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 34 ++-
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 19 ++
drivers/phy/tegra/Kconfig | 1 +
drivers/phy/tegra/xusb-tegra186.c | 114 +++++++--
drivers/phy/tegra/xusb-tegra210.c | 131 ++++++++--
drivers/phy/tegra/xusb.c | 172 +++++++++++++
drivers/phy/tegra/xusb.h | 5 +
drivers/usb/gadget/udc/tegra-xudc.c | 273 ++++++++++++++-------
drivers/usb/host/xhci-tegra.c | 228 ++++++++++++++++-
include/linux/phy/tegra/xusb.h | 2 +
15 files changed, 1078 insertions(+), 145 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml

--
2.7.4


2020-02-04 11:20:13

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 01/19] dt-bindings: phy: tegra-xusb: Add usb-role-switch

Add usb-role-switch property for Tegra210 and Tegra186 platforms. This
entry is used by XUSB pad controller driver to register for role changes
for OTG/Peripheral capable USB 2 ports.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Acked-by: Thierry Reding <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
V3-V4:
- Added Acked-by updates to commit message.
---
V2:
- Moved usb-role-switch to seperate Required section as suggested by Thierry.
- Added reference to usb/usb-conn-gpio.txt for connector subnode.
---
.../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
index 9fb682e..23bf354 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -174,6 +174,12 @@ Required properties:
- "device": for USB device mode
- "otg": for USB OTG mode

+Required properties for OTG/Peripheral capable USB2 ports:
+- usb-role-switch: Boolean property to indicate that the port support OTG or
+ peripheral mode. If present, the port supports switching between USB host
+ and peripheral roles. Connector should be added as subnode.
+ See usb/usb-conn-gpio.txt.
+
Optional properties:
- nvidia,internal: A boolean property whose presence determines that a port
is internal. In the absence of this property the port is considered to be
--
2.7.4

2020-02-04 11:20:26

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 04/19] phy: tegra: xusb: Add usb-phy support

For USB 2 ports that has usb-role-switch enabled, add usb-phy for
corresponding USB 2 phy. USB role changes from role switch are then
updated to corresponding host and device mode drivers via usb-phy notifier
block.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Acked-by: Thierry Reding <[email protected]>
---
V4:
- Addressed nit comments from Thierry.
- usb_phy_event is set accoring to received usb_role.
- Added Acked-by info.
V3:
- Updated arguments and variable allignments as per Thierry inputs.
---
V2:
- Added dev_set_drvdata for port->dev.
---
drivers/phy/tegra/xusb.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 2 ++
2 files changed, 86 insertions(+)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 0fdbaa2..2c0e29c 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -532,6 +532,8 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
if (err < 0)
goto unregister;

+ dev_set_drvdata(&port->dev, port);
+
return 0;

unregister:
@@ -544,6 +546,8 @@ 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);
}

device_unregister(&port->dev);
@@ -562,15 +566,70 @@ static const char * const usb_roles[] = {
[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 device *dev, enum usb_role role)
{
+ struct tegra_xusb_port *port = dev_get_drvdata(dev);
+
dev_dbg(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,
@@ -597,6 +656,31 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
return err;
}

+ INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work);
+
+ 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);

diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index 9f27899..2345657 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -268,6 +268,8 @@ struct tegra_xusb_port {
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;
};
--
2.7.4

2020-02-04 11:20:34

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 05/19] phy: tegra: xusb: Add support to get companion USB 3 port

Tegra XUSB host, device mode driver requires the USB 3 companion port
number for corresponding USB 2 port. Add API to retrieve the same.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Reviewed-by: JC Kuo <[email protected]>
Acked-by: Thierry Reding <[email protected]>
---
V4:
- Addressed comment from Thierry.
- Added Acked-by info.
---
V3:
- Added Reviewed-by updates to commit message.
---
V2:
- Added -ENODEV as return instead of -1, to sync other errors.
---
drivers/phy/tegra/xusb.c | 21 +++++++++++++++++++++
include/linux/phy/tegra/xusb.h | 2 ++
2 files changed, 23 insertions(+)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 2c0e29c..a8f0a0b 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1294,6 +1294,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 <[email protected]>");
MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
MODULE_LICENSE("GPL v2");
diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h
index 1235865..71d9569 100644
--- a/include/linux/phy/tegra/xusb.h
+++ b/include/linux/phy/tegra/xusb.h
@@ -21,4 +21,6 @@ int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl,
bool val);
int tegra_phy_xusb_utmi_port_reset(struct phy *phy);
+int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
+ unsigned int port);
#endif /* PHY_TEGRA_XUSB_H */
--
2.7.4

2020-02-04 11:20:50

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 07/19] phy: tegra: xusb: Add set_mode support for UTMI phy on Tegra186

Add support for set_mode on UTMI phy. This allow XUSB host/device mode
drivers to configure the hardware to corresponding modes.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Acked-by: Thierry Reding <[email protected]>
---
V4:
- Added comment for conditional regulator disable.
- Added Acked-by info.
---
V2-V3:
- No changes in this version
---
drivers/phy/tegra/xusb-tegra186.c | 114 ++++++++++++++++++++++++++++++--------
1 file changed, 92 insertions(+), 22 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index 84c2739..ea62251 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -301,6 +301,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 +530,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,
};

@@ -857,28 +949,6 @@ 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,
--
2.7.4

2020-02-04 11:20:51

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 03/19] phy: tegra: xusb: Add usb-role-switch support

If usb-role-switch property is present in USB 2 port, register
usb-role-switch to receive usb role changes.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V4:
- Updated function name in debug messages as suggested by Thierry.
- Added owner info to port->dev during USB role switch registration.
---
V3:
- Driver aborts if usb-role-switch is not added in dt forotg/peripheral
roles.
- Added role name strings instead of enum values in debug prints.
- Updated arguments and variable allignments as per Thierry inputs.
---
V2:
- Removed dev_set_drvdata for port->dev.
- Added of_platform_depopulate during error handling and driver removal.
---
drivers/phy/tegra/Kconfig | 1 +
drivers/phy/tegra/xusb.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 3 +++
3 files changed, 71 insertions(+)

diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
index f9817c3..df07c4d 100644
--- a/drivers/phy/tegra/Kconfig
+++ b/drivers/phy/tegra/Kconfig
@@ -2,6 +2,7 @@
config PHY_TEGRA_XUSB
tristate "NVIDIA Tegra XUSB pad controller driver"
depends on ARCH_TEGRA
+ select USB_CONN_GPIO
help
Choose this option if you have an NVIDIA Tegra SoC.

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index f98ec39..0fdbaa2 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -541,6 +541,11 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,

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);
+ }
+
device_unregister(&port->dev);
}

@@ -551,11 +556,59 @@ 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 int tegra_xusb_role_sw_set(struct device *dev, enum usb_role role)
+{
+ dev_dbg(dev, "%s(): role %s\n", __func__, usb_roles[role]);
+
+ return 0;
+}
+
+static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
+{
+ 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;
+ }
+
+ /* 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,6 +625,20 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
usb2->mode = USB_DR_MODE_HOST;
}

+ /* 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 = devm_regulator_get(&port->dev, "vbus");
return PTR_ERR_OR_ZERO(usb2->supply);
}
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index da94fcc..9f27899 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,6 +267,8 @@ struct tegra_xusb_port {
struct list_head list;
struct device dev;

+ struct usb_role_switch *usb_role_sw;
+
const struct tegra_xusb_port_ops *ops;
};

--
2.7.4

2020-02-04 11:20:56

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 08/19] usb: xhci-tegra: Add OTG support

Get usb-phy's for availbale USB 2 phys. Register id notifiers for available
usb-phy's to receive role change notifications. Perform PP for the received
role change usb ports.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Acked-by: Thierry Reding <[email protected]>
---
V4:
- Addressed nit comments from Thierry.
- usb_phy_event is used to detect host mode instead of usb_role.
- Added Acked-by info.
---
V3:
- No changes in this version
---
V2:
- Removed extra line before tegra_xusb_probe API.
---
drivers/usb/host/xhci-tegra.c | 228 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 227 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 8163aef..32f630b 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"
@@ -203,6 +206,7 @@ struct tegra_xusb_soc {

bool scale_ss_clock;
bool has_ipfs;
+ bool otg_reset_sspi;
};

struct tegra_xusb_context {
@@ -250,6 +254,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;
@@ -1081,6 +1093,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;
@@ -1254,8 +1465,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);
@@ -1384,6 +1598,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:
@@ -1420,6 +1640,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;
@@ -1694,6 +1916,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,
@@ -1733,6 +1956,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,
@@ -1773,6 +1997,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,
@@ -1802,6 +2027,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,
--
2.7.4

2020-02-04 11:21:06

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 10/19] usb: gadget: tegra-xudc: Add usb-phy support

usb-phy is used to get notified on the USB role changes. Get usb-phy from
the UTMI PHY.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V4:
- Addressed nit comments from Thierry.
- usb_phy_event is used to detect device mode instead of usb_role.
---
V2-V3:
- No changes in this version
---
drivers/usb/gadget/udc/tegra-xudc.c | 48 ++++++++++++++++++++++++++++++++++---
1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index acecdcf..7411dd15 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 */
@@ -487,6 +489,9 @@ struct tegra_xudc {
bool suspended;
bool powergated;

+ struct usb_phy *usbphy;
+ struct notifier_block vbus_nb;
+
struct completion disconnect_complete;

bool selfpowered;
@@ -669,6 +674,31 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
tegra_xudc_device_mode_off(xudc);
}

+static int tegra_xudc_vbus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
+ vbus_nb);
+ struct usb_phy *usbphy = (struct usb_phy *)data;
+
+ dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event);
+
+ 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;
+ }
+
+ xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
+ false;
+
+ if (!xudc->suspended)
+ schedule_work(&xudc->usb_role_sw_work);
+
+ return NOTIFY_OK;
+}
+
static void tegra_xudc_plc_reset_work(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -1937,6 +1967,9 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
xudc_writel(xudc, val, CTRL);
}

+ if (xudc->usbphy)
+ otg_set_peripheral(xudc->usbphy->otg, gadget);
+
xudc->driver = driver;
unlock:
dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret);
@@ -1957,6 +1990,9 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)

spin_lock_irqsave(&xudc->lock, flags);

+ if (xudc->usbphy)
+ otg_set_peripheral(xudc->usbphy->otg, NULL);
+
val = xudc_readl(xudc, CTRL);
val &= ~(CTRL_IE | CTRL_ENABLE);
xudc_writel(xudc, val, CTRL);
@@ -3561,9 +3597,15 @@ static int tegra_xudc_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&xudc->port_reset_war_work,
tegra_xudc_port_reset_war_work);

- /* Set the mode as device mode and this keeps phy always ON */
- xudc->device_mode = true;
- schedule_work(&xudc->usb_role_sw_work);
+ xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
+ xudc->usbphy = devm_usb_get_phy_by_node(xudc->dev,
+ xudc->utmi_phy->dev.of_node,
+ &xudc->vbus_nb);
+ if (IS_ERR(xudc->usbphy)) {
+ err = PTR_ERR(xudc->usbphy);
+ dev_err(xudc->dev, "failed to get USB PHY: %d\n", err);
+ goto free_eps;
+ }

pm_runtime_enable(&pdev->dev);

--
2.7.4

2020-02-04 11:21:14

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 02/19] dt-bindings: usb: Add NVIDIA Tegra XUSB device mode controller binding

Add device-tree binding documentation for the XUSB device mode controller
present on Tegra210 and Tegra186 SoC. This controller supports the USB 3.0
specification.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
Acked-by: Thierry Reding <[email protected]>
---
V4:
- No changes.
V3:
- Added Reviewed-by and Acked-by updates to commit message.
---
V2:
- used enum instead of oneOf and const.
- Moved reg, reg-names, clocks, clock-names to property section from allOf.
- Limited allOf to min and max items based on soc.
- Updated description for power-domains.
- Added V1 reference
---
V1
- This document is yaml version of [1], with difference of usb-role-switch
removal.
[1] https://patchwork.kernel.org/patch/11156253/
---
.../devicetree/bindings/usb/nvidia,tegra-xudc.yaml | 190 +++++++++++++++++++++
1 file changed, 190 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml

diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml b/Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml
new file mode 100644
index 0000000..b84ed8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml
@@ -0,0 +1,190 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/usb/nvidia,tegra-xudc.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Device tree binding for NVIDIA Tegra XUSB device mode controller (XUDC)
+
+description:
+ The Tegra XUDC controller supports both USB 2.0 HighSpeed/FullSpeed and
+ USB 3.0 SuperSpeed protocols.
+
+maintainers:
+ - Nagarjuna Kristam <[email protected]>
+ - JC Kuo <[email protected]>
+ - Thierry Reding <[email protected]>
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - nvidia,tegra210-xudc # For Tegra210
+ - nvidia,tegra186-xudc # For Tegra186
+
+ reg:
+ minItems: 2
+ maxItems: 3
+ items:
+ - description: XUSB device controller registers
+ - description: XUSB device PCI Config registers
+ - description: XUSB device registers.
+
+ reg-names:
+ minItems: 2
+ maxItems: 3
+ items:
+ - const: base
+ - const: fpci
+ - const: ipfs
+
+ interrupts:
+ maxItems: 1
+ description: Must contain the XUSB device interrupt.
+
+ clocks:
+ minItems: 4
+ maxItems: 5
+ items:
+ - description: Clock to enable core XUSB dev clock.
+ - description: Clock to enable XUSB super speed clock.
+ - description: Clock to enable XUSB super speed dev clock.
+ - description: Clock to enable XUSB high speed dev clock.
+ - description: Clock to enable XUSB full speed dev clock.
+
+ clock-names:
+ minItems: 4
+ maxItems: 5
+ items:
+ - const: dev
+ - const: ss
+ - const: ss_src
+ - const: fs_src
+ - const: hs_src
+
+ power-domains:
+ maxItems: 2
+ items:
+ - description: XUSBB(device) power-domain
+ - description: XUSBA(superspeed) power-domain
+
+ power-domain-names:
+ maxItems: 2
+ items:
+ - const: dev
+ - const: ss
+
+ nvidia,xusb-padctl:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ phandle to the XUSB pad controller that is used to configure the USB pads
+ used by the XUDC controller.
+
+ phys:
+ minItems: 1
+ description:
+ Must contain an entry for each entry in phy-names.
+ See ../phy/phy-bindings.txt for details.
+
+ phy-names:
+ minItems: 1
+ items:
+ - const: usb2-0
+ - const: usb2-1
+ - const: usb2-2
+ - const: usb2-3
+ - const: usb3-0
+ - const: usb3-1
+ - const: usb3-2
+ - const: usb3-3
+
+ avddio-usb-supply:
+ description: PCIe/USB3 analog logic power supply. Must supply 1.05 V.
+
+ hvdd-usb-supply:
+ description: USB controller power supply. Must supply 3.3 V.
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - interrupts
+ - clocks
+ - clock-names
+ - power-domains
+ - power-domain-names
+ - nvidia,xusb-padctl
+ - phys
+ - phy-names
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nvidia,tegra210-xudc
+ then:
+ properties:
+ reg:
+ minItems: 3
+ reg-names:
+ minItems: 3
+ clocks:
+ minItems: 5
+ clock-names:
+ minItems: 5
+ required:
+ - avddio-usb-supply
+ - hvdd-usb-supply
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nvidia,tegra186-xudc
+ then:
+ properties:
+ reg:
+ maxItems: 2
+ reg-names:
+ maxItems: 2
+ clocks:
+ maxItems: 4
+ clock-names:
+ maxItems: 4
+
+examples:
+ - |
+ #include <dt-bindings/clock/tegra210-car.h>
+ #include <dt-bindings/gpio/tegra-gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ usb@700d0000 {
+ compatible = "nvidia,tegra210-xudc";
+ reg = <0x0 0x700d0000 0x0 0x8000>,
+ <0x0 0x700d8000 0x0 0x1000>,
+ <0x0 0x700d9000 0x0 0x1000>;
+ reg-names = "base", "fpci", "ipfs";
+
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&tegra_car TEGRA210_CLK_XUSB_DEV>,
+ <&tegra_car TEGRA210_CLK_XUSB_SS>,
+ <&tegra_car TEGRA210_CLK_XUSB_SSP_SRC>,
+ <&tegra_car TEGRA210_CLK_XUSB_FS_SRC>,
+ <&tegra_car TEGRA210_CLK_XUSB_HS_SRC>;
+ clock-names = "dev", "ss", "ss_src", "fs_src", "hs_src";
+
+ power-domains = <&pd_xusbdev>, <&pd_xusbss>;
+ power-domain-names = "dev", "ss";
+
+ nvidia,xusb-padctl = <&padctl>;
+
+ phys = <&micro_b>;
+ phy-names = "usb2-0";
+
+ avddio-usb-supply = <&vdd_pex_1v05>;
+ hvdd-usb-supply = <&vdd_3v3_sys>;
+ };
--
2.7.4

2020-02-04 11:21:19

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 12/19] usb: gadget: tegra-xudc: support multiple device modes

This change supports limited multiple device modes by:
- At most 4 ports contains OTG/Device capability.
- One port run as device mode at a time.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V4:
- Replaced current_phy_index usage with curr_*phy pointers.
- Used unsigned int instead of int wherever needed.
---
V3:
- No changes in this version
---
V2:
- Updated err variable on failure to get usbphy.
- Corrected identation after tegra_xudc_phy_get API call in tegra_xudc_probe.
---
drivers/usb/gadget/udc/tegra-xudc.c | 217 ++++++++++++++++++++++++++----------
1 file changed, 160 insertions(+), 57 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index a2299ee..3ffb929 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -482,14 +482,16 @@ struct tegra_xudc {
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 usb_phy **usbphy;
struct notifier_block vbus_nb;

struct completion disconnect_complete;
@@ -521,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;
@@ -602,17 +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");

- phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, 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)
@@ -627,7 +631,7 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)

reinit_completion(&xudc->disconnect_complete);

- phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
+ 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;
@@ -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);

@@ -674,12 +678,27 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
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_vbus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
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(): event is %d\n", __func__, usbphy->last_event);

@@ -693,8 +712,15 @@ static int tegra_xudc_vbus_notify(struct notifier_block *nb,
xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
false;

- if (!xudc->suspended)
+ phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
+ dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
+ phy_index);
+
+ 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;
}
@@ -714,9 +740,9 @@ 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");
- phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
+ phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_NONE);
- phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
+ phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_DEVICE);

xudc->wait_csc = false;
@@ -745,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;
}
@@ -1934,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;
@@ -1969,8 +1997,9 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
xudc_writel(xudc, val, CTRL);
}

- if (xudc->usbphy)
- otg_set_peripheral(xudc->usbphy->otg, gadget);
+ 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:
@@ -1987,13 +2016,15 @@ 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);

- if (xudc->usbphy)
- otg_set_peripheral(xudc->usbphy->otg, NULL);
+ 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);
@@ -3327,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[] = {
@@ -3381,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,
@@ -3393,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,
@@ -3558,19 +3678,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)
@@ -3599,16 +3709,6 @@ static int tegra_xudc_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&xudc->port_reset_war_work,
tegra_xudc_port_reset_war_work);

- xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
- xudc->usbphy = devm_usb_get_phy_by_node(xudc->dev,
- xudc->utmi_phy->dev.of_node,
- &xudc->vbus_nb);
- if (IS_ERR(xudc->usbphy)) {
- err = PTR_ERR(xudc->usbphy);
- dev_err(xudc->dev, "failed to get USB PHY: %d\n", err);
- goto free_eps;
- }
-
pm_runtime_enable(&pdev->dev);

xudc->gadget.ops = &tegra_xudc_gadget_ops;
@@ -3643,6 +3743,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
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);

@@ -3658,8 +3759,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);

--
2.7.4

2020-02-04 11:21:20

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 15/19] arm64: tegra: Add xudc node for Tegra210

Tegra210 has one XUSB device mode controller, which can be operated
HS and SS modes. Add DT entry for XUSB device mode controller.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V2-V4:
- Updated clocks and clock-names sequence as per change in bindings doc.
---
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 48c6325..dfce47b 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1207,6 +1207,25 @@
status = "disabled";
};

+ usb@700d0000 {
+ compatible = "nvidia,tegra210-xudc";
+ reg = <0x0 0x700d0000 0x0 0x8000>,
+ <0x0 0x700d8000 0x0 0x1000>,
+ <0x0 0x700d9000 0x0 0x1000>;
+ reg-names = "base", "fpci", "ipfs";
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA210_CLK_XUSB_DEV>,
+ <&tegra_car TEGRA210_CLK_XUSB_SS>,
+ <&tegra_car TEGRA210_CLK_XUSB_SSP_SRC>,
+ <&tegra_car TEGRA210_CLK_XUSB_FS_SRC>,
+ <&tegra_car TEGRA210_CLK_XUSB_HS_SRC>;
+ clock-names = "dev", "ss", "ss_src", "fs_src", "hs_src";
+ power-domains = <&pd_xusbdev>, <&pd_xusbss>;
+ power-domain-names = "dev", "ss";
+ nvidia,xusb-padctl = <&padctl>;
+ status = "disabled";
+ };
+
mipi: mipi@700e3000 {
compatible = "nvidia,tegra210-mipi";
reg = <0x0 0x700e3000 0x0 0x100>;
--
2.7.4

2020-02-04 11:21:23

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 16/19] arm64: tegra: Enable xudc on Jetson TX1

Enable XUSB device mode driver for USB 2-0 slot on Jetson TX1.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V2-V4:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index 18c0610..49a2a82 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -1361,7 +1361,7 @@
status = "okay";

lanes {
- usb2-0 {
+ micro_b: usb2-0 {
nvidia,function = "xusb";
status = "okay";
};
@@ -1494,6 +1494,14 @@
vmmc-supply = <&vdd_3v3_sd>;
};

+ usb@700d0000 {
+ status = "okay";
+ phys = <&micro_b>;
+ phy-names = "usb2-0";
+ avddio-usb-supply = <&vdd_3v3_sys>;
+ hvdd-usb-supply = <&vdd_1v8>;
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
--
2.7.4

2020-02-04 11:21:29

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 18/19] arm64: tegra: Enable xudc node on Jetson TX2

Enable XUSB device mode driver for USB 2-0 slot on Jetson TX2.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V2-V4:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
index a1dcdb9..d7628f5 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
@@ -131,7 +131,7 @@
status = "okay";

lanes {
- usb2-0 {
+ micro_b: usb2-0 {
nvidia,function = "xusb";
status = "okay";
};
@@ -213,6 +213,13 @@
phy-names = "usb2-0", "usb2-1", "usb3-0";
};

+ usb@3550000 {
+ status = "okay";
+
+ phys = <&micro_b>;
+ phy-names = "usb2-0";
+ };
+
i2c@c250000 {
/* carrier board ID EEPROM */
eeprom@57 {
--
2.7.4

2020-02-04 11:21:41

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 06/19] phy: tegra: xusb: Add set_mode support for USB 2 phy on Tegra210

Add support for set_mode on USB 2 phy. This allow XUSB host/device mode
drivers to configure the hardware to corresponding modes.

Signed-off-by: Nagarjuna Kristam <[email protected]>
Acked-by: Thierry Reding <[email protected]>
---
V4:
- Added comment for conditional regulator disable.
- Added Acked-by info.
---
V2-V3:
- No changes in this version
---
drivers/phy/tegra/xusb-tegra210.c | 131 ++++++++++++++++++++++++++++++--------
1 file changed, 104 insertions(+), 27 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 394913b..54d6826 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,
};

@@ -2023,30 +2124,6 @@ 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 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;
--
2.7.4

2020-02-04 11:21:48

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 13/19] arm64: tegra: update OTG port entries for jetson-tx1

Populate OTG vbus regulator. Add usb-role-switch entry to USB 2-0 port
and corresponding connector details.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V2-V4:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index b009507..18c0610 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -1336,7 +1336,6 @@
<&{/padctl@7009f000/pads/pcie/lanes/pcie-5}>;
phy-names = "usb2-0", "usb2-1", "usb2-2", "usb2-3", "usb3-0",
"usb3-1";
-
dvddio-pex-supply = <&vdd_pex_1v05>;
hvddio-pex-supply = <&vdd_1v8>;
avdd-usb-supply = <&vdd_3v3_sys>;
@@ -1440,7 +1439,19 @@
ports {
usb2-0 {
status = "okay";
+ vbus-supply = <&vdd_usb_vbus_otg>;
mode = "otg";
+
+ usb-role-switch;
+ connector {
+ compatible = "usb-b-connector",
+ "gpio-usb-b-connector";
+ label = "micro-USB";
+ type = "micro";
+ vbus-gpio = <&gpio TEGRA_GPIO(Z, 0)
+ GPIO_ACTIVE_LOW>;
+ id-gpio = <&pmic 0 0>;
+ };
};

usb2-1 {
@@ -1606,6 +1617,17 @@
vin-supply = <&vdd_5v0_sys>;
};

+ vdd_usb_vbus_otg: regulator@11 {
+ compatible = "regulator-fixed";
+ reg = <9>;
+ regulator-name = "USB_VBUS_EN0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio TEGRA_GPIO(CC, 4) GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ vin-supply = <&vdd_5v0_sys>;
+ };
+
vdd_hdmi: regulator@10 {
compatible = "regulator-fixed";
reg = <10>;
--
2.7.4

2020-02-04 11:21:56

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 09/19] usb: gadget: tegra-xudc: Remove usb-role-switch support

Padctl driver will act as a central driver to receive USB role changes via
usb-role-switch. This is updated to corresponding host, device drivers.
Hence remove usb-role-switch from XUDC driver.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V4
- Updated device_mode variable with usb_role and remove role variable.
- Removed debug log related to always on.
---
V2-V3:
- No changes in this version
---
drivers/usb/gadget/udc/tegra-xudc.c | 58 +++++--------------------------------
1 file changed, 7 insertions(+), 51 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 634c2c1..acecdcf 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -477,8 +477,7 @@ 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;
@@ -609,8 +608,6 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
dev_dbg(xudc->dev, "device mode on\n");

tegra_xusb_padctl_set_vbus_override(xudc->padctl, true);
-
- xudc->device_mode = USB_ROLE_DEVICE;
}

static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
@@ -643,8 +640,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);
@@ -668,29 +663,10 @@ 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_usb_role_sw_set(struct device *dev, enum usb_role role)
-{
- struct tegra_xudc *xudc = dev_get_drvdata(dev);
- unsigned long flags;
-
- dev_dbg(dev, "%s role is %d\n", __func__, role);
-
- spin_lock_irqsave(&xudc->lock, flags);
-
- if (!xudc->suspended)
- schedule_work(&xudc->usb_role_sw_work);
-
- spin_unlock_irqrestore(&xudc->lock, flags);
-
- return 0;
}

static void tegra_xudc_plc_reset_work(struct work_struct *work)
@@ -729,8 +705,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);
@@ -3457,7 +3432,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;

@@ -3587,23 +3561,9 @@ 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);
-
- 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);
- }
+ /* Set the mode as device mode and this keeps phy always ON */
+ xudc->device_mode = true;
+ schedule_work(&xudc->usb_role_sw_work);

pm_runtime_enable(&pdev->dev);

@@ -3643,11 +3603,7 @@ static int tegra_xudc_remove(struct platform_device *pdev)
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);

--
2.7.4

2020-02-04 11:22:04

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 11/19] usb: gadget: tegra-xudc: use phy_set_mode to set/unset device mode

When device mode is set/uset, VBUS override activity is done via
exported functions from padctl driver. Use phy_set_mode() instead.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V4:
- Updated commit message as per comments from Thierry.
---
V2-V3:
- No changes in this version
---
drivers/usb/gadget/udc/tegra-xudc.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 7411dd15..a2299ee 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -612,7 +612,7 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)

dev_dbg(xudc->dev, "device mode on\n");

- tegra_xusb_padctl_set_vbus_override(xudc->padctl, true);
+ phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_DEVICE);
}

static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
@@ -627,7 +627,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->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);

pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
PORTSC_PLS_SHIFT;
@@ -714,9 +714,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->utmi_phy, PHY_MODE_USB_OTG,
+ USB_ROLE_NONE);
+ phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
+ USB_ROLE_DEVICE);
+
xudc->wait_csc = false;
}
}
--
2.7.4

2020-02-04 11:22:12

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 14/19] arm64: tegra: update OTG port entries for jetson-tx2

Add usb-role-switch entry to OTG usb port and add corresponding connector
details.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V2-V4:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
index f1de4ff..a1dcdb9 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
@@ -174,8 +174,20 @@
usb2-0 {
status = "okay";
mode = "otg";
-
vbus-supply = <&vdd_usb0>;
+
+ usb-role-switch;
+ connector {
+ compatible = "usb-b-connector",
+ "gpio-usb-b-connector";
+ label = "micro-USB";
+ type = "micro";
+ vbus-gpio = <&gpio
+ TEGRA186_MAIN_GPIO(X, 7)
+ GPIO_ACTIVE_LOW>;
+ id-gpio = <&pmic 0 GPIO_ACTIVE_HIGH>;
+ };
+
};

usb2-1 {
--
2.7.4

2020-02-04 11:22:21

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 17/19] arm64: tegra: Add xudc node for Tegra186

Tegra186 has one XUSB device mode controller, which can be operated
HS and SS modes. Add DT entry for XUSB device mode controller.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V2-V4:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index c905527..58100fb 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -572,6 +572,25 @@
nvidia,xusb-padctl = <&padctl>;
};

+ usb@3550000 {
+ compatible = "nvidia,tegra186-xudc";
+ reg = <0x0 0x03550000 0x0 0x8000>,
+ <0x0 0x03558000 0x0 0x1000>;
+ reg-names = "base", "fpci";
+ interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&bpmp TEGRA186_CLK_XUSB_CORE_DEV>,
+ <&bpmp TEGRA186_CLK_XUSB_SS>,
+ <&bpmp TEGRA186_CLK_XUSB_CORE_SS>,
+ <&bpmp TEGRA186_CLK_XUSB_FS>;
+ clock-names = "dev", "ss", "ss_src", "fs_src";
+ iommus = <&smmu TEGRA186_SID_XUSB_DEV>;
+ power-domains = <&bpmp TEGRA186_POWER_DOMAIN_XUSBB>,
+ <&bpmp TEGRA186_POWER_DOMAIN_XUSBA>;
+ power-domain-names = "dev", "ss";
+ nvidia,xusb-padctl = <&padctl>;
+ status = "disabled";
+ };
+
fuse@3820000 {
compatible = "nvidia,tegra186-efuse";
reg = <0x0 0x03820000 0x0 0x10000>;
--
2.7.4

2020-02-04 11:22:30

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V4 19/19] ARM: tegra: Remove USB 2-0 port from Jetson TK1 padctl

On Jetson TK1 USB 2-0 port is controlled by phy-tegra-usb driver
rather than padctl driver. Remove the entry for the same.

Signed-off-by: Nagarjuna Kristam <[email protected]>
---
V4:
- New patch in the series.
---
arch/arm/boot/dts/tegra124-jetson-tk1.dts | 6 ------
1 file changed, 6 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
index d5fd642..54600ff 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
@@ -1782,12 +1782,6 @@
};

ports {
- /* Micro A/B */
- usb2-0 {
- status = "okay";
- mode = "otg";
- };
-
/* Mini PCIe */
usb2-1 {
status = "okay";
--
2.7.4