2019-12-06 10:52:05

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 00/18] 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

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-B port
- host mode support by connecting pen-drive to micro USB port on DUT
using micro-B OTG cable.
- toggling between these 2 modes by hot plugging corresponding cables.

DUT: Jetson-tx1, Jetson tx2.

Nagarjuna Kristam (18):
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

.../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 4 +
.../devicetree/bindings/usb/nvidia,tegra-xudc.yaml | 204 +++++++++++++++
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 | 109 ++++++--
drivers/phy/tegra/xusb-tegra210.c | 126 ++++++++--
drivers/phy/tegra/xusb.c | 132 ++++++++++
drivers/phy/tegra/xusb.h | 5 +
drivers/usb/gadget/udc/tegra-xudc.c | 277 ++++++++++++++-------
drivers/usb/host/xhci-tegra.c | 226 ++++++++++++++++-
include/linux/phy/tegra/xusb.h | 2 +
14 files changed, 1042 insertions(+), 139 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml

--
2.7.4


2019-12-06 10:52:27

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 02/18] 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]>
---
.../devicetree/bindings/usb/nvidia,tegra-xudc.yaml | 204 +++++++++++++++++++++
1 file changed, 204 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..b23c451
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: GPL-2.0
+%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:
+ oneOf:
+ - items:
+ - const: nvidia,tegra210-xudc # For Tegra210
+ - items:
+ - const: nvidia,tegra186-xudc # For Tegra186
+
+ interrupts:
+ maxItems: 1
+ description: Must contain the XUSB device interrupt.
+
+ power-domains:
+ maxItems: 2
+ description:
+ A list of PM domain specifiers that reference each power-domain
+ used by the XUSB device mode controller. This list must comprise of a
+ specifier for the XUSBA and XUSBB power-domains.
+ See ../power/power_domain.txt and ../arm/tegra/nvidia,tegra20-pmc.txt
+ for details.
+
+ power-domains-names:
+ maxItems: 2
+ description:
+ A list of names that represent each of the specifiers in
+ the 'power-domains' property.
+ items:
+ - const: ss
+ - const: dev
+
+ 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
+ - power-domains
+ - power-domain-names
+ - nvidia,xusb-padctl
+ - phys
+ - phy-names
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ items:
+ const: nvidia,tegra210-xudc
+
+ then:
+ properties:
+ reg:
+ maxItems: 3
+ items:
+ - description: XUSB device controller registers
+ - description: XUSB device PCI Config registers
+ - description: XUSB device registers.
+ reg-names:
+ maxItems: 3
+ items:
+ - const: base
+ - const: fpci
+ - const: ipfs
+ clocks:
+ description:
+ Must contain an entry for all clocks used. See ../clock/clock-bindings.txt
+ for details.
+ 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:
+ items:
+ - const: dev
+ - const: ss
+ - const: ss_src
+ - const: hs_src
+ - const: fs_src
+ required:
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - avddio-usb-supply
+ - hvdd-usb-supply
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nvidia,tegra186-xudc
+
+ then:
+ properties:
+ reg:
+ maxItems: 2
+ items:
+ - description: XUSB device controller registers
+ - description: XUSB device PCI Config registers
+ reg-names:
+ maxItems: 2
+ items:
+ - const: base
+ - const: fpci
+ clocks:
+ description:
+ Must contain an entry for all clocks used. See ../clock/clock-bindings.txt
+ for details.
+ maxItems: 4
+ 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 full speed dev clock.
+ clock-names:
+ items:
+ - const: dev
+ - const: ss
+ - const: ss_src
+ - const: fs_src
+ required:
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+
+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_HS_SRC>,
+ <&tegra_car TEGRA210_CLK_XUSB_FS_SRC>;
+ clock-names = "dev", "ss", "ss_src", "hs_src", "fs_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

2019-12-06 10:52:33

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 04/18] 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]>
---
drivers/phy/tegra/xusb.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++-
drivers/phy/tegra/xusb.h | 2 ++
2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index da60a63..4c86c99 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -543,7 +543,11 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,

static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
{
- usb_role_switch_unregister(port->usb_role_sw);
+ if (port->usb_role_sw) {
+ cancel_work_sync(&port->usb_phy_work);
+ usb_role_switch_unregister(port->usb_role_sw);
+ usb_remove_phy(&port->usb_phy);
+ }
device_unregister(&port->dev);
}

@@ -554,16 +558,59 @@ static const char *const modes[] = {
[USB_DR_MODE_OTG] = "otg",
};

+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);
+
+ dev_dbg(&port->dev, "%s calling notifier for role %d\n", __func__,
+ role);
+
+ atomic_notifier_call_chain(&port->usb_phy.notifier, role,
+ &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 calling notifier for role is %d\n", __func__, 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)
{
int err = 0;
+ struct tegra_xusb_lane *lane = tegra_xusb_find_lane(port->padctl,
+ "usb2", port->index);
struct usb_role_switch_desc role_sx_desc = {
.set = tegra_xusb_role_sw_set,
.fwnode = dev_fwnode(&port->dev),
@@ -576,6 +623,30 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
if (err != EPROBE_DEFER)
dev_err(&port->dev, "Failed to register USB role SW: %d",
err);
+ 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;
+
+ /*
+ * 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 usbphy: %d\n", err);
+ return err;
}

return err;
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

2019-12-06 10:52:37

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 05/18] 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]>
---
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 4c86c99..2e73cf8 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1254,6 +1254,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 = tegra_xusb_find_usb2_port(padctl,
+ port);
+ struct tegra_xusb_usb3_port *usb3;
+ int i;
+
+ 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 -1;
+}
+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

2019-12-06 10:52:42

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 06/18] 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]>
---
drivers/phy/tegra/xusb-tegra210.c | 126 ++++++++++++++++++++++++++++++--------
1 file changed, 99 insertions(+), 27 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 394913b..6610b1d7 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,98 @@ 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) {
+ 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 +1141,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 +1259,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 +2119,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

2019-12-06 10:52:49

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 07/18] 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]>
---
drivers/phy/tegra/xusb-tegra186.c | 109 ++++++++++++++++++++++++++++++--------
1 file changed, 87 insertions(+), 22 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index 84c2739..9a45160 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -301,6 +301,92 @@ 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) {
+ 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 +525,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 +944,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

2019-12-06 10:53:07

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 10/18] 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]>
---
drivers/usb/gadget/udc/tegra-xudc.c | 39 +++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 6ddb974..0f27d57 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 */
@@ -488,6 +490,9 @@ struct tegra_xudc {
bool suspended;
bool powergated;

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

bool selfpowered;
@@ -678,7 +683,22 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
tegra_xudc_device_mode_on(xudc);
else
tegra_xudc_device_mode_off(xudc);
+}
+
+static int tegra_xudc_vbus_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
+ vbus_nb);
+
+ dev_dbg(xudc->dev, "%s action is %ld\n", __func__, action);
+
+ xudc->role = (enum usb_role)action;

+ if (!xudc->suspended)
+ schedule_work(&xudc->usb_role_sw_work);
+
+ return NOTIFY_OK;
}

static void tegra_xudc_plc_reset_work(struct work_struct *work)
@@ -1949,6 +1969,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);
@@ -1969,6 +1992,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);
@@ -3573,10 +3599,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 */
- dev_info(xudc->dev, "Set usb role to device mode always");
- xudc->role = USB_ROLE_DEVICE;
- schedule_work(&xudc->usb_role_sw_work);
+ xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notifier;
+ 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 usbphy phy: %d\n", err);
+ goto free_eps;
+ }

pm_runtime_enable(&pdev->dev);

--
2.7.4

2019-12-06 10:53:10

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 11/18] 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]>
---
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 0f27d57..283c320 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -615,7 +615,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);

xudc->device_mode = true;

@@ -636,7 +636,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;
@@ -716,9 +716,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

2019-12-06 10:53:10

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 08/18] 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]>
---
drivers/usb/host/xhci-tegra.c | 226 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 225 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index bf90654..4b6db83 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -23,6 +23,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"
@@ -170,6 +173,7 @@ struct tegra_xusb_soc {

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

struct tegra_xusb {
@@ -212,6 +216,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;
@@ -966,6 +978,206 @@ static int tegra_xusb_powerdomain_init(struct device *dev,
return 0;
}

+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;
+ 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 PP\n", __func__, set ? "set" : "clear",
+ main ? "HS" : "SS");
+
+ tegra_xhci_hc_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)
+{
+ 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)
+{
+ 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 %ld", __func__, action);
+
+ if ((tegra->host_mode && action == USB_ROLE_HOST) ||
+ (!tegra->host_mode && action != USB_ROLE_HOST)) {
+ 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 = (action == USB_ROLE_HOST) ? true : false;
+
+ schedule_work(&tegra->id_work);
+
+ return NOTIFY_OK;
+}
+
+static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra)
+{
+ 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)
+{
+ 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_mbox_msg msg;
@@ -1136,8 +1348,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);
@@ -1268,6 +1483,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:
@@ -1301,6 +1522,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;
@@ -1421,6 +1644,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,
--
2.7.4

2019-12-06 10:53:11

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 01/18] 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]>
---
Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 4 ++++
1 file changed, 4 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..0f19ed6 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -179,6 +179,10 @@ Optional properties:
is internal. In the absence of this property the port is considered to be
external.
- vbus-supply: phandle to a regulator supplying the VBUS voltage.
+- usb-role-switch: boolean property to indicate use of USB Role Switch.
+ This property is MUST for OTG,Peripheral capable USB 2 ports. Connector
+ should be added as subnode, see connector.txt. vbus-gpio in connector is
+ Mandatory.

ULPI ports:
-----------
--
2.7.4

2019-12-06 10:53:15

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 13/18] 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]>
---
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

2019-12-06 10:53:20

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 15/18] 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]>
---
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..023f4c3 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_HS_SRC>,
+ <&tegra_car TEGRA210_CLK_XUSB_FS_SRC>;
+ clock-names = "dev", "ss", "ss_src", "hs_src", "fs_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

2019-12-06 10:53:25

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 17/18] 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]>
---
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 7893d78..6da9d09 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -554,6 +554,25 @@
#size-cells = <0>;
};

+ 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

2019-12-06 10:53:33

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 18/18] 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]>
---
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

2019-12-06 10:53:45

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 12/18] 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]>
---
drivers/usb/gadget/udc/tegra-xudc.c | 229 ++++++++++++++++++++++++++----------
1 file changed, 167 insertions(+), 62 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 283c320..a17d896 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -483,14 +483,15 @@ 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 **utmi_phy;

struct tegra_xudc_save_regs saved_regs;
bool suspended;
bool powergated;

- struct usb_phy *usbphy;
+ struct usb_phy **usbphy;
+ int current_phy_index;
struct notifier_block vbus_nb;

struct completion disconnect_complete;
@@ -522,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;
@@ -605,17 +607,18 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
return;
pm_runtime_get_sync(xudc->dev);

- err = phy_power_on(xudc->utmi_phy);
+ err = phy_power_on(xudc->utmi_phy[xudc->current_phy_index]);
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->usb3_phy[xudc->current_phy_index]);
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->utmi_phy[xudc->current_phy_index],
+ PHY_MODE_USB_OTG, USB_ROLE_DEVICE);

xudc->device_mode = true;

@@ -636,7 +639,8 @@ 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->utmi_phy[xudc->current_phy_index],
+ PHY_MODE_USB_OTG, USB_ROLE_NONE);

pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
PORTSC_PLS_SHIFT;
@@ -663,11 +667,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->utmi_phy[xudc->current_phy_index]);
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->usb3_phy[xudc->current_phy_index]);
if (err < 0)
dev_err(xudc->dev, "usb3_phy power off failed %d\n", err);

@@ -685,17 +689,43 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
tegra_xudc_device_mode_off(xudc);
}

-static int tegra_xudc_vbus_notifier(struct notifier_block *nb,
+static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc,
+ struct usb_phy *usbphy)
+{
+ 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;

dev_dbg(xudc->dev, "%s action is %ld\n", __func__, action);

+ if ((xudc->device_mode && action == USB_ROLE_DEVICE) ||
+ (!xudc->device_mode && action != USB_ROLE_DEVICE)) {
+ dev_info(xudc->dev, "Same role(%d) received. Ignore",
+ xudc->device_mode);
+ return NOTIFY_OK;
+ }
+
xudc->role = (enum usb_role)action;

- if (!xudc->suspended)
+ xudc->current_phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
+ dev_dbg(xudc->dev, "%s current phy index is %d\n", __func__,
+ xudc->current_phy_index);
+
+ if (!xudc->suspended && xudc->current_phy_index != -1)
schedule_work(&xudc->usb_role_sw_work);

return NOTIFY_OK;
@@ -716,10 +746,12 @@ 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,
- USB_ROLE_NONE);
- phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
- USB_ROLE_DEVICE);
+ phy_set_mode_ext(
+ xudc->utmi_phy[xudc->current_phy_index],
+ PHY_MODE_USB_OTG, USB_ROLE_NONE);
+ phy_set_mode_ext(
+ xudc->utmi_phy[xudc->current_phy_index],
+ PHY_MODE_USB_OTG, USB_ROLE_DEVICE);

xudc->wait_csc = false;
}
@@ -747,7 +779,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->utmi_phy[xudc->current_phy_index]);
if (ret == 1)
xudc->wait_for_sec_prc = 0;
}
@@ -1935,7 +1968,7 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
struct tegra_xudc *xudc = to_xudc(gadget);
unsigned long flags;
u32 val;
- int ret;
+ int ret, i;

if (!driver)
return -EINVAL;
@@ -1971,8 +2004,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:
@@ -1989,13 +2023,15 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
struct tegra_xudc *xudc = to_xudc(gadget);
unsigned long flags;
u32 val;
+ 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);
@@ -3329,33 +3365,117 @@ 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, i, usb3;

- 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])) {
+ 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);
+ 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, 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[] = {
@@ -3383,6 +3503,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,
@@ -3395,6 +3516,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,
@@ -3560,19 +3682,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);
- goto disable_regulator;
- }
+ err = tegra_xudc_phy_get(xudc);
+ if (err)
+ goto disable_regulator;

err = tegra_xudc_powerdomain_init(xudc);
if (err)
@@ -3601,16 +3713,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_notifier;
- 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 usbphy phy: %d\n", err);
- goto free_eps;
- }
-
pm_runtime_enable(&pdev->dev);

xudc->gadget.ops = &tegra_xudc_gadget_ops;
@@ -3645,6 +3747,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);
+ int i;

pm_runtime_get_sync(xudc->dev);

@@ -3660,8 +3763,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

2019-12-06 10:53:56

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 09/18] 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]>
---
drivers/usb/gadget/udc/tegra-xudc.c | 65 ++++++++++---------------------------
1 file changed, 17 insertions(+), 48 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 634c2c1..6ddb974 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -477,8 +477,8 @@ struct tegra_xudc {

struct clk_bulk_data *clks;

- enum usb_role device_mode;
- struct usb_role_switch *usb_role_sw;
+ enum usb_role role;
+ bool device_mode;
struct work_struct usb_role_sw_work;

struct phy *usb3_phy;
@@ -596,6 +596,8 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
{
int err;

+ if (xudc->device_mode)
+ return;
pm_runtime_get_sync(xudc->dev);

err = phy_power_on(xudc->utmi_phy);
@@ -610,7 +612,8 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)

tegra_xusb_padctl_set_vbus_override(xudc->padctl, true);

- xudc->device_mode = USB_ROLE_DEVICE;
+ xudc->device_mode = true;
+
}

static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
@@ -619,6 +622,9 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
u32 pls, val;
int err;

+ if (!xudc->device_mode)
+ return;
+
dev_dbg(xudc->dev, "device mode off\n");

connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS);
@@ -643,7 +649,7 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
xudc_writel(xudc, val, PORTSC);
}

- xudc->device_mode = USB_ROLE_NONE;
+ xudc->device_mode = false;

/* Wait for disconnect event. */
if (connected)
@@ -668,31 +674,13 @@ 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->role == USB_ROLE_DEVICE)
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)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -729,8 +717,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 +3444,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 +3573,10 @@ 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 */
+ dev_info(xudc->dev, "Set usb role to device mode always");
+ xudc->role = USB_ROLE_DEVICE;
+ schedule_work(&xudc->usb_role_sw_work);

pm_runtime_enable(&pdev->dev);

@@ -3643,11 +3616,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

2019-12-06 10:54:13

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 14/18] 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]>
---
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

2019-12-06 10:54:20

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 16/18] 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]>
---
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

2019-12-06 10:55:15

by Nagarjuna Kristam

[permalink] [raw]
Subject: [PATCH 03/18] 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]>
---
drivers/phy/tegra/Kconfig | 1 +
drivers/phy/tegra/xusb.c | 40 ++++++++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 3 +++
3 files changed, 44 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..da60a63 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -523,6 +523,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
port->dev.type = &tegra_xusb_port_type;
port->dev.of_node = of_node_get(np);
port->dev.parent = padctl->dev;
+ port->dev.driver = padctl->dev->driver;

err = dev_set_name(&port->dev, "%s-%u", name, index);
if (err < 0)
@@ -532,6 +533,7 @@ 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:
@@ -541,6 +543,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,

static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
{
+ usb_role_switch_unregister(port->usb_role_sw);
device_unregister(&port->dev);
}

@@ -551,11 +554,39 @@ static const char *const modes[] = {
[USB_DR_MODE_OTG] = "otg",
};

+static int tegra_xusb_role_sw_set(struct device *dev, enum usb_role role)
+{
+ dev_dbg(dev, "%s calling notifier for role is %d\n", __func__, role);
+
+ return 0;
+}
+
+static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
+{
+ int err = 0;
+ struct usb_role_switch_desc role_sx_desc = {
+ .set = tegra_xusb_role_sw_set,
+ .fwnode = dev_fwnode(&port->dev),
+ };
+
+ 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);
+ if (err != EPROBE_DEFER)
+ dev_err(&port->dev, "Failed to register USB role SW: %d",
+ err);
+ }
+
+ 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 +603,15 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
usb2->mode = USB_DR_MODE_HOST;
}

+ if (of_property_read_bool(np, "usb-role-switch")) {
+ /* populate connector entry */
+ of_platform_populate(np, NULL, NULL, &port->dev);
+
+ err = tegra_xusb_setup_usb_role_switch(port);
+ if (err < 0)
+ return err;
+ }
+
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

2019-12-06 14:49:49

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH 01/18] dt-bindings: phy: tegra-xusb: Add usb-role-switch

On Fri, Dec 06, 2019 at 04:20:04PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 4 ++++
> 1 file changed, 4 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..0f19ed6 100644
> --- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
> +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
> @@ -179,6 +179,10 @@ Optional properties:
> is internal. In the absence of this property the port is considered to be
> external.
> - vbus-supply: phandle to a regulator supplying the VBUS voltage.
> +- usb-role-switch: boolean property to indicate use of USB Role Switch.

That first sentence here seems a bit useless and vague. It doesn't
really convey anything other than the name already does. Perhaps
something like:

Boolean property to indicate that the port support OTG. If
present, the port supports switching between USB host and
peripheral roles.

> + This property is MUST for OTG,Peripheral capable USB 2 ports. Connector

If this is mandatory, why not add it to the list of required properties?
I guess since it's only mandatory for ports that support OTG, perhaps we
could add a section "Required properties for OTG capable ports:" or
something like that? Then you can also omit the second sentence in the
description.

> + should be added as subnode, see connector.txt. vbus-gpio in connector is

There's no file called "connector.txt". Are you referring to

Documentation/devicetree/bindings/connector/usb-connector.txt

? Also, that file calls the property "vbus-gpios" and lists it as
optional. What would happen if we don't specify it? Doesn't that just
mean that we can't support role detection?

> + Mandatory.

"mandatory"

Thierry

>
> ULPI ports:
> -----------
> --
> 2.7.4
>


Attachments:
(No filename) (2.22 kB)
signature.asc (849.00 B)
Download all attachments

2019-12-06 14:55:33

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH 03/18] phy: tegra: xusb: Add usb-role-switch support

On Fri, Dec 06, 2019 at 04:20:06PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> drivers/phy/tegra/Kconfig | 1 +
> drivers/phy/tegra/xusb.c | 40 ++++++++++++++++++++++++++++++++++++++++
> drivers/phy/tegra/xusb.h | 3 +++
> 3 files changed, 44 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..da60a63 100644
> --- a/drivers/phy/tegra/xusb.c
> +++ b/drivers/phy/tegra/xusb.c
> @@ -523,6 +523,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
> port->dev.type = &tegra_xusb_port_type;
> port->dev.of_node = of_node_get(np);
> port->dev.parent = padctl->dev;
> + port->dev.driver = padctl->dev->driver;
>
> err = dev_set_name(&port->dev, "%s-%u", name, index);
> if (err < 0)
> @@ -532,6 +533,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
> if (err < 0)
> goto unregister;
>
> + dev_set_drvdata(&port->dev, port);

You never seem to use dev_get_drvdata() to get at this. Also, you can
get at it via container_of(), so this is only marginally useful to begin
with.

> return 0;
>
> unregister:
> @@ -541,6 +543,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
>
> static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
> {
> + usb_role_switch_unregister(port->usb_role_sw);
> device_unregister(&port->dev);
> }
>
> @@ -551,11 +554,39 @@ static const char *const modes[] = {
> [USB_DR_MODE_OTG] = "otg",
> };
>
> +static int tegra_xusb_role_sw_set(struct device *dev, enum usb_role role)
> +{
> + dev_dbg(dev, "%s calling notifier for role is %d\n", __func__, role);
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
> +{
> + int err = 0;
> + struct usb_role_switch_desc role_sx_desc = {
> + .set = tegra_xusb_role_sw_set,
> + .fwnode = dev_fwnode(&port->dev),
> + };
> +
> + 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);
> + if (err != EPROBE_DEFER)
> + dev_err(&port->dev, "Failed to register USB role SW: %d",
> + err);
> + }
> +
> + 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 +603,15 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
> usb2->mode = USB_DR_MODE_HOST;
> }
>
> + if (of_property_read_bool(np, "usb-role-switch")) {
> + /* populate connector entry */
> + of_platform_populate(np, NULL, NULL, &port->dev);

I think we want to clean this up on failure, don't we? Otherwise we
might end up trying to register the same platform device multiple times.
Also, do we want to depopulate when the port is removed again?

Have you tried unloading and loading the driver to see if that works?

Thierry

> +
> + err = tegra_xusb_setup_usb_role_switch(port);
> + if (err < 0)
> + return err;
> + }
> +
> 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
>


Attachments:
(No filename) (4.50 kB)
signature.asc (849.00 B)
Download all attachments

2019-12-06 14:58:27

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH 05/18] phy: tegra: xusb: Add support to get companion USB 3 port

On Fri, Dec 06, 2019 at 04:20:08PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> 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 4c86c99..2e73cf8 100644
> --- a/drivers/phy/tegra/xusb.c
> +++ b/drivers/phy/tegra/xusb.c
> @@ -1254,6 +1254,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 = tegra_xusb_find_usb2_port(padctl,
> + port);
> + struct tegra_xusb_usb3_port *usb3;
> + int i;
> +
> + 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 -1;

Since you return -EINVAL above, callers will have to interpret negative
return values as standard errors, which would make this EPERM. That does
not really make sense. Perhaps something like -ENODEV would be more
appropriate in this case?

Thierry

> +}
> +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
>


Attachments:
(No filename) (2.31 kB)
signature.asc (849.00 B)
Download all attachments

2019-12-09 04:44:25

by Nagarjuna Kristam

[permalink] [raw]
Subject: Re: [PATCH 01/18] dt-bindings: phy: tegra-xusb: Add usb-role-switch



> On 06-12-2019 20:19, Thierry Reding wrote:
>
> On Fri, Dec 06, 2019 at 04:20:04PM +0530, Nagarjuna Kristam wrote:
>> 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]>
>> ---
>> Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 4 ++++
>> 1 file changed, 4 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..0f19ed6 100644
>> --- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>> +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>> @@ -179,6 +179,10 @@ Optional properties:
>> is internal. In the absence of this property the port is considered to be
>> external.
>> - vbus-supply: phandle to a regulator supplying the VBUS voltage.
>> +- usb-role-switch: boolean property to indicate use of USB Role Switch.
> That first sentence here seems a bit useless and vague. It doesn't
> really convey anything other than the name already does. Perhaps
> something like:
>
> Boolean property to indicate that the port support OTG. If
> present, the port supports switching between USB host and
> peripheral roles.
>
Will update this accordingly.

>> + This property is MUST for OTG,Peripheral capable USB 2 ports. Connector
> If this is mandatory, why not add it to the list of required properties?
> I guess since it's only mandatory for ports that support OTG, perhaps we
> could add a section "Required properties for OTG capable ports:" or
> something like that? Then you can also omit the second sentence in the
> description.
>
Will add section "Required properties for OTG/Peripheral capable ports:" under ports
and add usb-role-switch there.

>> + should be added as subnode, see connector.txt. vbus-gpio in connector is
> There's no file called "connector.txt". Are you referring to
>
> Documentation/devicetree/bindings/connector/usb-connector.txt
>
> ? Also, that file calls the property "vbus-gpios" and lists it as
> optional. What would happen if we don't specify it? Doesn't that just
> mean that we can't support role detection?
>
After going through other bindings found usb/usb-conn-gpio.txt is the one that should
be referred. Will update accordingly. All details on gpios are documented clearly
in usb/usb-conn-gpio.txt.

>> + Mandatory.
> "mandatory"
>
> Thierry
>
>>
Will update.

Thanks,
Nagarjuna
>> ULPI ports:
>> -----------
>> --
>> 2.7.4
>>

2019-12-09 06:27:19

by Nagarjuna Kristam

[permalink] [raw]
Subject: Re: [PATCH 05/18] phy: tegra: xusb: Add support to get companion USB 3 port



On 06-12-2019 20:27, Thierry Reding wrote:
> On Fri, Dec 06, 2019 at 04:20:08PM +0530, Nagarjuna Kristam wrote:
>> 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]>
>> ---
>> 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 4c86c99..2e73cf8 100644
>> --- a/drivers/phy/tegra/xusb.c
>> +++ b/drivers/phy/tegra/xusb.c
>> @@ -1254,6 +1254,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 = tegra_xusb_find_usb2_port(padctl,
>> + port);
>> + struct tegra_xusb_usb3_port *usb3;
>> + int i;
>> +
>> + 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 -1;
> Since you return -EINVAL above, callers will have to interpret negative
> return values as standard errors, which would make this EPERM. That does
> not really make sense. Perhaps something like -ENODEV would be more
> appropriate in this case?
>
> Thierry
>
Yes, making -ENODEV instead of -1 makes it inline with generic error codes.
Will update accordingly.

-Nagarjuna
>> +}
>> +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
>>

2019-12-10 03:51:56

by Nagarjuna Kristam

[permalink] [raw]
Subject: Re: [PATCH 03/18] phy: tegra: xusb: Add usb-role-switch support



On 06-12-2019 20:24, Thierry Reding wrote:
> On Fri, Dec 06, 2019 at 04:20:06PM +0530, Nagarjuna Kristam wrote:
>> 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]>
>> ---
>> drivers/phy/tegra/Kconfig | 1 +
>> drivers/phy/tegra/xusb.c | 40 ++++++++++++++++++++++++++++++++++++++++
>> drivers/phy/tegra/xusb.h | 3 +++
>> 3 files changed, 44 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..da60a63 100644
>> --- a/drivers/phy/tegra/xusb.c
>> +++ b/drivers/phy/tegra/xusb.c
>> @@ -523,6 +523,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
>> port->dev.type = &tegra_xusb_port_type;
>> port->dev.of_node = of_node_get(np);
>> port->dev.parent = padctl->dev;
>> + port->dev.driver = padctl->dev->driver;
>>
>> err = dev_set_name(&port->dev, "%s-%u", name, index);
>> if (err < 0)
>> @@ -532,6 +533,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
>> if (err < 0)
>> goto unregister;
>>
>> + dev_set_drvdata(&port->dev, port);
> You never seem to use dev_get_drvdata() to get at this. Also, you can
> get at it via container_of(), so this is only marginally useful to begin
> with.
>
Its actually used in API tegra_xusb_role_sw_set, but thats in patch 0004.
Will move this line to 0004 patch to align with the usage.

>> return 0;
>>
>> unregister:
>> @@ -541,6 +543,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
>>
>> static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
>> {
>> + usb_role_switch_unregister(port->usb_role_sw);
>> device_unregister(&port->dev);
>> }
>>
>> @@ -551,11 +554,39 @@ static const char *const modes[] = {
>> [USB_DR_MODE_OTG] = "otg",
>> };
>>
>> +static int tegra_xusb_role_sw_set(struct device *dev, enum usb_role role)
>> +{
>> + dev_dbg(dev, "%s calling notifier for role is %d\n", __func__, role);
>> +
>> + return 0;
>> +}
>> +
>> +static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
>> +{
>> + int err = 0;
>> + struct usb_role_switch_desc role_sx_desc = {
>> + .set = tegra_xusb_role_sw_set,
>> + .fwnode = dev_fwnode(&port->dev),
>> + };
>> +
>> + 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);
>> + if (err != EPROBE_DEFER)
>> + dev_err(&port->dev, "Failed to register USB role SW: %d",
>> + err);
>> + }
>> +
>> + 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 +603,15 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
>> usb2->mode = USB_DR_MODE_HOST;
>> }
>>
>> + if (of_property_read_bool(np, "usb-role-switch")) {
>> + /* populate connector entry */
>> + of_platform_populate(np, NULL, NULL, &port->dev);
> I think we want to clean this up on failure, don't we? Otherwise we
> might end up trying to register the same platform device multiple times.
> Also, do we want to depopulate when the port is removed again?
>
> Have you tried unloading and loading the driver to see if that works?
>
> Thierry
>
platform needs to be depopulate on error/remove and will add corresponding code.
padctl driver can be unloaded after unloading all dependent drivers. re-loading
caused failure of usb role switch due to missing depopulate. Will update changes
to consider the same.

Thanks,
Nagarjuna
>> +
>> + err = tegra_xusb_setup_usb_role_switch(port);
>> + if (err < 0)
>> + return err;
>> + }
>> +
>> 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
>>

2019-12-13 18:46:47

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH 02/18] dt-bindings: usb: Add NVIDIA Tegra XUSB device mode controller binding

On Fri, Dec 06, 2019 at 04:20:05PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---

Next time version your patches correctly and include version history
here so I don't have to go look up why my R-by is missing.

> .../devicetree/bindings/usb/nvidia,tegra-xudc.yaml | 204 +++++++++++++++++++++
> 1 file changed, 204 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..b23c451
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml
> @@ -0,0 +1,204 @@
> +# SPDX-License-Identifier: GPL-2.0

Dual license new bindings please:

(GPL-2.0-only 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:
> + oneOf:
> + - items:
> + - const: nvidia,tegra210-xudc # For Tegra210
> + - items:
> + - const: nvidia,tegra186-xudc # For Tegra186

Use 'enum' instead of oneOf+const.

> +
> + interrupts:
> + maxItems: 1
> + description: Must contain the XUSB device interrupt.

Don't need a description for a single interrupt line.

> +
> + power-domains:
> + maxItems: 2
> + description:
> + A list of PM domain specifiers that reference each power-domain
> + used by the XUSB device mode controller. This list must comprise of a
> + specifier for the XUSBA and XUSBB power-domains.
> + See ../power/power_domain.txt and ../arm/tegra/nvidia,tegra20-pmc.txt
> + for details.

Just need:

items:
- description: XUSBA power-domain
- description: XUSBB power-domain

> +
> + power-domains-names:
> + maxItems: 2
> + description:
> + A list of names that represent each of the specifiers in
> + the 'power-domains' property.

That's every 'power-domains-names'.

> + items:
> + - const: ss
> + - const: dev

Okay, but those names don't match up with XUSBA and XUSBB. Names should
be meaningful or local to the module, not the provider if that helps.

> +
> + 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
> + - power-domains
> + - power-domain-names
> + - nvidia,xusb-padctl
> + - phys
> + - phy-names
> +
> +allOf:
> + - if:
> + properties:
> + compatible:
> + items:
> + const: nvidia,tegra210-xudc
> +
> + then:
> + properties:
> + reg:
> + maxItems: 3
> + items:
> + - description: XUSB device controller registers
> + - description: XUSB device PCI Config registers
> + - description: XUSB device registers.
> + reg-names:
> + maxItems: 3
> + items:
> + - const: base
> + - const: fpci
> + - const: ipfs

As these are a superset of tegra186, you can move this up and tegra186
just needs to define 'maxItems: 2'.

> + clocks:
> + description:
> + Must contain an entry for all clocks used. See ../clock/clock-bindings.txt
> + for details.
> + 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:
> + items:
> + - const: dev
> + - const: ss
> + - const: ss_src
> + - const: hs_src
> + - const: fs_src

I would re-order the last 2 entries so that you can do the same thing as
I said for 'reg'.

> + required:
> + - reg
> + - reg-names
> + - clocks
> + - clock-names

No need for these to be under the if. They are always required and
don't have to be where defined by 'properties'.

> + - avddio-usb-supply
> + - hvdd-usb-supply
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: nvidia,tegra186-xudc
> +
> + then:
> + properties:
> + reg:
> + maxItems: 2
> + items:
> + - description: XUSB device controller registers
> + - description: XUSB device PCI Config registers
> + reg-names:
> + maxItems: 2
> + items:
> + - const: base
> + - const: fpci
> + clocks:
> + description:
> + Must contain an entry for all clocks used. See ../clock/clock-bindings.txt
> + for details.
> + maxItems: 4
> + 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 full speed dev clock.
> + clock-names:
> + items:
> + - const: dev
> + - const: ss
> + - const: ss_src
> + - const: fs_src
> + required:
> + - reg
> + - reg-names
> + - clocks
> + - clock-names
> +
> +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_HS_SRC>,
> + <&tegra_car TEGRA210_CLK_XUSB_FS_SRC>;
> + clock-names = "dev", "ss", "ss_src", "hs_src", "fs_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
>