2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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-AB port
- host mode support by connecting pen-drive to micro-AB USB port on DUT
using standard-A to micro-A converter.
- toggling between these 2 modes by hot plugging corresponding cables.

DUT: Jetson-tx1, Jetson tx2.

V3:
- Port and cable names updated in "Tests done" section of cover letter as
per JC inputs.
- Fixed arguments allignments in USB padctl driver.
- Padctl driver aborts if usb-role-switch is not present in dt for
peripheral/otg roles.
- Added Reviewed and ACKed details for corresponding patches.
V2:
- Updated usb-role-switch documentation for Padctl driver.
- Update XUDC bindings doc as suggested by Rob.
- Used standard error codes for error return.
- Added of_platform_depopulate during error and driver removal.
- Updated error variable during phy initialization in XUDC driver.
- Updated Tegra210 soc dtb file as per changes to binding doc.

Nagarjuna Kristam (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 | 6 +
.../devicetree/bindings/usb/nvidia,tegra-xudc.yaml | 190 ++++++++++++++
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 | 152 ++++++++++++
drivers/phy/tegra/xusb.h | 5 +
drivers/usb/gadget/udc/tegra-xudc.c | 276 ++++++++++++++-------
drivers/usb/host/xhci-tegra.c | 225 ++++++++++++++++-
include/linux/phy/tegra/xusb.h | 2 +
14 files changed, 1049 insertions(+), 138 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml

--
2.7.4


2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
drivers/usb/gadget/udc/tegra-xudc.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 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-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

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

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

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

2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

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

usb2-1 {
--
2.7.4

2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V3:
- No changes in this version
---
V2:
- Removed extra line before tegra_xusb_probe API.
---
drivers/usb/host/xhci-tegra.c | 225 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 224 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 0b58ef3..22f1d36 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -24,6 +24,9 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/role.h>
#include <soc/tegra/pmc.h>

#include "xhci.h"
@@ -203,6 +206,7 @@ struct tegra_xusb_soc {

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

struct tegra_xusb_context {
@@ -250,6 +254,14 @@ struct tegra_xusb {
struct phy **phys;
unsigned int num_phys;

+ struct usb_phy **usbphy;
+ unsigned int num_usb_phys;
+ int otg_usb2_port;
+ int otg_usb3_port;
+ bool host_mode;
+ struct notifier_block id_nb;
+ struct work_struct id_work;
+
/* Firmware loading related */
struct {
size_t size;
@@ -1081,6 +1093,205 @@ static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
return err;
}

+static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main,
+ bool set)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd;
+ 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 *tegra;
@@ -1254,8 +1465,11 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto put_powerdomains;
}

- for (i = 0; i < tegra->soc->num_types; i++)
+ for (i = 0; i < tegra->soc->num_types; i++) {
+ if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4))
+ tegra->num_usb_phys = tegra->soc->phy_types[i].num;
tegra->num_phys += tegra->soc->phy_types[i].num;
+ }

tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
sizeof(*tegra->phys), GFP_KERNEL);
@@ -1384,6 +1598,12 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto remove_usb3;
}

+ err = tegra_xusb_init_usb_phy(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to Init usb-phy: %d\n", err);
+ goto remove_usb3;
+ }
+
return 0;

remove_usb3:
@@ -1420,6 +1640,8 @@ static int tegra_xusb_remove(struct platform_device *pdev)
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);

+ tegra_xusb_deinit_usb_phy(tegra);
+
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
xhci->shared_hcd = NULL;
@@ -1733,6 +1955,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-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V3:
- Updated arguments and variable allignments as per Thierry inputs.
---
V2:
- Added dev_set_drvdata for port->dev.
---
drivers/phy/tegra/xusb.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 2 ++
2 files changed, 76 insertions(+)

diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 11ea9b5..536b2fc 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -533,6 +533,8 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
if (err < 0)
goto unregister;

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

unregister:
@@ -545,6 +547,8 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
if (!IS_ERR_OR_NULL(port->usb_role_sw)) {
of_platform_depopulate(&port->dev);
usb_role_switch_unregister(port->usb_role_sw);
+ cancel_work_sync(&port->usb_phy_work);
+ usb_remove_phy(&port->usb_phy);
}

device_unregister(&port->dev);
@@ -563,15 +567,62 @@ static const char * const usb_roles[] = {
[USB_ROLE_DEVICE] = "device",
};

+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 %s\n", __func__,
+ usb_roles[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: role %s\n", __func__, usb_roles[role]);

+ schedule_work(&port->usb_phy_work);
+
return 0;
}

+static int tegra_xusb_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct tegra_xusb_port *port = container_of(otg->usb_phy,
+ struct tegra_xusb_port,
+ usb_phy);
+
+ if (gadget != NULL)
+ schedule_work(&port->usb_phy_work);
+
+ return 0;
+}
+
+static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct tegra_xusb_port *port = container_of(otg->usb_phy,
+ struct tegra_xusb_port,
+ usb_phy);
+
+ if (host != NULL)
+ schedule_work(&port->usb_phy_work);
+
+ return 0;
+}
+
+
static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
{
+ struct tegra_xusb_lane *lane = tegra_xusb_find_lane(port->padctl,
+ "usb2",
+ port->index);
struct usb_role_switch_desc role_sx_desc = {
.fwnode = dev_fwnode(&port->dev),
.set = tegra_xusb_role_sw_set,
@@ -587,6 +638,29 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
return err;
}

+ INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work);
+
+ port->usb_phy.otg = devm_kzalloc(&port->dev,
+ sizeof(struct usb_otg), GFP_KERNEL);
+ if (!port->usb_phy.otg)
+ return -ENOMEM;
+
+ /*
+ * 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;
+ }
+
/* populate connector entry */
of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev);

diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index 9f27899..2345657 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -268,6 +268,8 @@ struct tegra_xusb_port {
struct device dev;

struct usb_role_switch *usb_role_sw;
+ struct work_struct usb_phy_work;
+ struct usb_phy usb_phy;

const struct tegra_xusb_port_ops *ops;
};
--
2.7.4

2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
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-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V3:
- Driver aborts if usb-role-switch is not added in dt forotg/peripheral
roles.
- Added role name strings instead of enum values in debug prints.
- Updated arguments and variable allignments as per Thierry inputs.
---
V2:
- Removed dev_set_drvdata for port->dev.
- Added of_platform_depopulate during error handling and driver removal.
---
drivers/phy/tegra/Kconfig | 1 +
drivers/phy/tegra/xusb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 3 +++
3 files changed, 61 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..11ea9b5 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)
@@ -541,6 +542,11 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,

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

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

+static const char * const usb_roles[] = {
+ [USB_ROLE_NONE] = "none",
+ [USB_ROLE_HOST] = "host",
+ [USB_ROLE_DEVICE] = "device",
+};
+
+static int tegra_xusb_role_sw_set(struct device *dev, enum usb_role role)
+{
+ dev_dbg(dev, "%s: role %s\n", __func__, usb_roles[role]);
+
+ return 0;
+}
+
+static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
+{
+ struct usb_role_switch_desc role_sx_desc = {
+ .fwnode = dev_fwnode(&port->dev),
+ .set = tegra_xusb_role_sw_set,
+ };
+ int err = 0;
+
+ port->usb_role_sw = usb_role_switch_register(&port->dev,
+ &role_sx_desc);
+ if (IS_ERR(port->usb_role_sw)) {
+ err = PTR_ERR(port->usb_role_sw);
+ dev_err(&port->dev, "failed to register USB role switch: %d",
+ err);
+ return err;
+ }
+
+ /* populate connector entry */
+ of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev);
+
+ return err;
+}
+
static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
{
struct tegra_xusb_port *port = &usb2->base;
struct device_node *np = port->dev.of_node;
const char *mode;
+ int err;

usb2->internal = of_property_read_bool(np, "nvidia,internal");

@@ -572,6 +615,20 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
usb2->mode = USB_DR_MODE_HOST;
}

+ /* usb-role-switch property is mandatory for OTG/Peripheral modes */
+ if (usb2->mode == USB_DR_MODE_PERIPHERAL ||
+ usb2->mode == USB_DR_MODE_OTG) {
+ if (of_property_read_bool(np, "usb-role-switch")) {
+ err = tegra_xusb_setup_usb_role_switch(port);
+ if (err < 0)
+ return err;
+ } else {
+ dev_err(&port->dev, "usb-role-switch not found for %s mode",
+ modes[usb2->mode]);
+ return -EINVAL;
+ }
+ }
+
usb2->supply = devm_regulator_get(&port->dev, "vbus");
return PTR_ERR_OR_ZERO(usb2->supply);
}
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index da94fcc..9f27899 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -12,6 +12,7 @@
#include <linux/workqueue.h>

#include <linux/usb/otg.h>
+#include <linux/usb/role.h>

/* legacy entry points for backwards-compatibility */
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
@@ -266,6 +267,8 @@ struct tegra_xusb_port {
struct list_head list;
struct device dev;

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

--
2.7.4

2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
Acked-by: Thierry Reding <[email protected]>
---
V3:
- Added Acked-by updates to commit message.
---
V2:
- Moved usb-role-switch to seperate Required section as suggested by Thierry.
- Added reference to usb/usb-conn-gpio.txt for connector subnode.
---
.../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 6 ++++++
1 file changed, 6 insertions(+)

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

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

2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
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-30 11:15:59

by Nagarjuna Kristam

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

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

2019-12-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
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-30 11:15:59

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
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-30 11:16:19

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index 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-30 11:16:36

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

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

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

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

2019-12-30 11:17:15

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
Reviewed-by: JC Kuo <[email protected]>
---
V3:
- Added Reviewed-by updates to commit message.
---
V2:
- Added -ENODEV as return instead of -1, to sync other errors.
---
drivers/phy/tegra/xusb.c | 21 +++++++++++++++++++++
include/linux/phy/tegra/xusb.h | 2 ++
2 files changed, 23 insertions(+)

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

2019-12-30 11:17:16

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V2-V3:
- No changes in this version
---
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)

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

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

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

2019-12-30 11:17:24

by Nagarjuna Kristam

[permalink] [raw]
Subject: [Patch V3 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]>
---
V3:
- No changes in this version
---
V2:
- Updated err variable on failure to get usbphy.
- Corrected identation after tegra_xudc_phy_get API call in tegra_xudc_probe.
---
drivers/usb/gadget/udc/tegra-xudc.c | 228 ++++++++++++++++++++++++++----------
1 file changed, 167 insertions(+), 61 deletions(-)

diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 283c320..bf80fae 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,118 @@ 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])) {
+ err = PTR_ERR(xudc->usbphy[i]);
+ dev_err(xudc->dev, "failed to get usbphy-%d: %d\n",
+ i, err);
+ goto clean_up;
+ }
+ } else if (!xudc->utmi_phy[i]) {
+ /* if utmi phy is not available, ignore USB3 phy get */
+ continue;
+ }
+
+ /* Get USB3 phy */
+ usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i);
+ if (usb3 < 0)
+ continue;
+
+ snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3);
+ xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
+ if (IS_ERR(xudc->usb3_phy[i])) {
+ err = PTR_ERR(xudc->usb3_phy[i]);
+ if (err != -EPROBE_DEFER)
+ dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n",
+ usb3, err);
+
+ goto clean_up;
+ } else if (xudc->usb3_phy[i])
+ dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3);
}

- return 0;
+ return err;
+
+clean_up:
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ xudc->usb3_phy[i] = NULL;
+ xudc->utmi_phy[i] = NULL;
+ xudc->usbphy[i] = NULL;
+ }

-exit_utmi_phy:
- phy_exit(xudc->utmi_phy);
return err;
}

static void tegra_xudc_phy_exit(struct tegra_xudc *xudc)
{
- phy_exit(xudc->usb3_phy);
- phy_exit(xudc->utmi_phy);
+ 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 +3504,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 +3517,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 +3683,9 @@ static int tegra_xudc_probe(struct platform_device *pdev)
goto put_padctl;
}

- xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3");
- if (IS_ERR(xudc->usb3_phy)) {
- err = PTR_ERR(xudc->usb3_phy);
- dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err);
- goto disable_regulator;
- }
-
- xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2");
- if (IS_ERR(xudc->utmi_phy)) {
- err = PTR_ERR(xudc->utmi_phy);
- dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err);
+ err = tegra_xudc_phy_get(xudc);
+ if (err)
goto disable_regulator;
- }

err = tegra_xudc_powerdomain_init(xudc);
if (err)
@@ -3601,16 +3714,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 +3748,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 +3764,10 @@ static int tegra_xudc_remove(struct platform_device *pdev)

regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);

- phy_power_off(xudc->utmi_phy);
- phy_power_off(xudc->usb3_phy);
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ phy_power_off(xudc->utmi_phy[i]);
+ phy_power_off(xudc->usb3_phy[i]);
+ }

tegra_xudc_phy_exit(xudc);

--
2.7.4

2020-01-04 00:38:34

by Rob Herring (Arm)

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

On Mon, 30 Dec 2019 16:39:38 +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]>
> Acked-by: Thierry Reding <[email protected]>
> ---
> V3:
> - Added Acked-by updates to commit message.
> ---
> V2:
> - Moved usb-role-switch to seperate Required section as suggested by Thierry.
> - Added reference to usb/usb-conn-gpio.txt for connector subnode.
> ---
> .../devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt | 6 ++++++
> 1 file changed, 6 insertions(+)
>

Acked-by: Rob Herring <[email protected]>

2020-01-28 17:33:59

by Thierry Reding

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

On Mon, Dec 30, 2019 at 04:39:40PM +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]>
> ---
> V3:
> - Driver aborts if usb-role-switch is not added in dt forotg/peripheral
> roles.
> - Added role name strings instead of enum values in debug prints.
> - Updated arguments and variable allignments as per Thierry inputs.
> ---
> V2:
> - Removed dev_set_drvdata for port->dev.
> - Added of_platform_depopulate during error handling and driver removal.
> ---
> drivers/phy/tegra/Kconfig | 1 +
> drivers/phy/tegra/xusb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
> drivers/phy/tegra/xusb.h | 3 +++
> 3 files changed, 61 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..11ea9b5 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;

This looks wrong. I don't think driver's are supposed to set this
because it basically means that the device is being attached to the
driver, but in this case it doesn't get probed by the driver and in
fact the ports don't match the pad controller, so they can't really
be driven by the same driver.

Is there any particular reason why you need this?

Thierry


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

2020-01-28 17:39:50

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 04/18] phy: tegra: xusb: Add usb-phy support

On Mon, Dec 30, 2019 at 04:39:41PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> V3:
> - Updated arguments and variable allignments as per Thierry inputs.
> ---
> V2:
> - Added dev_set_drvdata for port->dev.
> ---
> drivers/phy/tegra/xusb.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/phy/tegra/xusb.h | 2 ++
> 2 files changed, 76 insertions(+)
>
> diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
> index 11ea9b5..536b2fc 100644
> --- a/drivers/phy/tegra/xusb.c
> +++ b/drivers/phy/tegra/xusb.c
> @@ -533,6 +533,8 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,
> if (err < 0)
> goto unregister;
>
> + dev_set_drvdata(&port->dev, port);
> +
> return 0;
>
> unregister:
> @@ -545,6 +547,8 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
> if (!IS_ERR_OR_NULL(port->usb_role_sw)) {
> of_platform_depopulate(&port->dev);
> usb_role_switch_unregister(port->usb_role_sw);
> + cancel_work_sync(&port->usb_phy_work);
> + usb_remove_phy(&port->usb_phy);
> }
>
> device_unregister(&port->dev);
> @@ -563,15 +567,62 @@ static const char * const usb_roles[] = {
> [USB_ROLE_DEVICE] = "device",
> };
>
> +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 %s\n", __func__,
> + usb_roles[role]);

Nit: I'd add parentheses into the above message, after %s, to highlight
that this is the name of a function.

> +
> + 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: role %s\n", __func__, usb_roles[role]);
>
> + schedule_work(&port->usb_phy_work);
> +
> return 0;
> }
>
> +static int tegra_xusb_set_peripheral(struct usb_otg *otg,
> + struct usb_gadget *gadget)
> +{
> + struct tegra_xusb_port *port = container_of(otg->usb_phy,
> + struct tegra_xusb_port,
> + usb_phy);
> +
> + if (gadget != NULL)
> + schedule_work(&port->usb_phy_work);
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host)
> +{
> + struct tegra_xusb_port *port = container_of(otg->usb_phy,
> + struct tegra_xusb_port,
> + usb_phy);
> +
> + if (host != NULL)
> + schedule_work(&port->usb_phy_work);
> +
> + return 0;
> +}
> +
> +
> static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
> {
> + struct tegra_xusb_lane *lane = tegra_xusb_find_lane(port->padctl,
> + "usb2",
> + port->index);

Nit: This looks slightly awkward. It might be worth just declaring the
variable here and move the initialization to a separate line following
the declarations because then it fits on a single line.

> struct usb_role_switch_desc role_sx_desc = {
> .fwnode = dev_fwnode(&port->dev),
> .set = tegra_xusb_role_sw_set,
> @@ -587,6 +638,29 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
> return err;
> }
>
> + INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work);
> +
> + port->usb_phy.otg = devm_kzalloc(&port->dev,
> + sizeof(struct usb_otg), GFP_KERNEL);

Nit: typically you try to fill up a line as much as possible before you
wrap, so the sizeof(...) can go onto the first line.

> + 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);

Nit: "failed to add USB PHY: %d\n", to make this more consistent with
the existing error messages.

Other than that, looks great:

Acked-by: Thierry Reding <[email protected]>


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

2020-01-28 17:42:01

by Thierry Reding

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

On Mon, Dec 30, 2019 at 04:39:42PM +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]>
> Reviewed-by: JC Kuo <[email protected]>
> ---
> V3:
> - Added Reviewed-by updates to commit message.
> ---
> V2:
> - Added -ENODEV as return instead of -1, to sync other errors.
> ---
> drivers/phy/tegra/xusb.c | 21 +++++++++++++++++++++
> include/linux/phy/tegra/xusb.h | 2 ++
> 2 files changed, 23 insertions(+)
>
> diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
> index 536b2fc..80dbc57 100644
> --- a/drivers/phy/tegra/xusb.c
> +++ b/drivers/phy/tegra/xusb.c
> @@ -1274,6 +1274,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);

Nit: may be better to move the initialization of the variable to below,
right before the line that checks the validity of the pointer.

Other than that, looks good:

Acked-by: Thierry Reding <[email protected]>


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

2020-01-28 17:42:58

by Thierry Reding

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

On Mon, Dec 30, 2019 at 04:39:43PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> V2-V3:
> - No changes in this version
> ---
> drivers/phy/tegra/xusb-tegra210.c | 126 ++++++++++++++++++++++++++++++--------
> 1 file changed, 99 insertions(+), 27 deletions(-)

Acked-by: Thierry Reding <[email protected]>


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

2020-01-28 17:46:17

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 07/18] phy: tegra: xusb: Add set_mode support for utmi phy on Tegra186

On Mon, Dec 30, 2019 at 04:39:44PM +0530, Nagarjuna Kristam wrote:
> Add support for set_mode on utmi phy. This allow XUSB host/device mode
> drivers to configure the hardware to corresponding modes.

"utmi" -> "UTMI" in the subject and the commit message.

>
> Signed-off-by: Nagarjuna Kristam <[email protected]>
> ---
> V2-V3:
> - No changes in this version
> ---
> 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))

I vaguely recall that we discussed this before, but I don't recall. Why
do we need to check that the regulator is enabled? Regulators are
reference-counted, so as long as the reference count is balanced, there
should be no need to check for this.

If there's really no way to avoid this check, perhaps add a comment that
points out exactly why this is needed?

With that fixed:

Acked-by: Thierry Reding <[email protected]>


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

2020-01-28 17:55:18

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 08/18] usb: xhci-tegra: Add OTG support

On Mon, Dec 30, 2019 at 04:39:45PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> V3:
> - No changes in this version
> ---
> V2:
> - Removed extra line before tegra_xusb_probe API.
> ---
> drivers/usb/host/xhci-tegra.c | 225 +++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 224 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
> index 0b58ef3..22f1d36 100644
> --- a/drivers/usb/host/xhci-tegra.c
> +++ b/drivers/usb/host/xhci-tegra.c
> @@ -24,6 +24,9 @@
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> #include <linux/slab.h>
> +#include <linux/usb/otg.h>
> +#include <linux/usb/phy.h>
> +#include <linux/usb/role.h>
> #include <soc/tegra/pmc.h>
>
> #include "xhci.h"
> @@ -203,6 +206,7 @@ struct tegra_xusb_soc {
>
> bool scale_ss_clock;
> bool has_ipfs;
> + bool otg_reset_sspi;
> };
>
> struct tegra_xusb_context {
> @@ -250,6 +254,14 @@ struct tegra_xusb {
> struct phy **phys;
> unsigned int num_phys;
>
> + struct usb_phy **usbphy;
> + unsigned int num_usb_phys;
> + int otg_usb2_port;
> + int otg_usb3_port;
> + bool host_mode;
> + struct notifier_block id_nb;
> + struct work_struct id_work;
> +
> /* Firmware loading related */
> struct {
> size_t size;
> @@ -1081,6 +1093,205 @@ static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
> return err;
> }
>
> +static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main,
> + bool set)
> +{
> + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
> + struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd;
> + 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");

Perhaps add parentheses after the first %s to make it clear that it's a
function name. Also, maybe spell out PP to make it a bit clearer what's
meant by it?

> +
> + tegra_xhci_hc_driver.hub_control(hcd, typeReq, USB_PORT_FEAT_POWER,

Can we replace tegra_xhci_hc_driver. by hcd->driver-> here and below?
That's slightly shorter and decouples the implementation from this hard-
coded name.

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

These can be unsigned int. Same elsewhere.

> +
> + 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 *tegra;
> @@ -1254,8 +1465,11 @@ static int tegra_xusb_probe(struct platform_device *pdev)
> goto put_powerdomains;
> }
>
> - for (i = 0; i < tegra->soc->num_types; i++)
> + for (i = 0; i < tegra->soc->num_types; i++) {
> + if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4))
> + tegra->num_usb_phys = tegra->soc->phy_types[i].num;
> tegra->num_phys += tegra->soc->phy_types[i].num;
> + }
>
> tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
> sizeof(*tegra->phys), GFP_KERNEL);
> @@ -1384,6 +1598,12 @@ static int tegra_xusb_probe(struct platform_device *pdev)
> goto remove_usb3;
> }
>
> + err = tegra_xusb_init_usb_phy(tegra);
> + if (err < 0) {
> + dev_err(&pdev->dev, "failed to Init usb-phy: %d\n", err);

s/Init/init/, s/usb-phy/USB PHY/

> + goto remove_usb3;
> + }
> +
> return 0;
>
> remove_usb3:
> @@ -1420,6 +1640,8 @@ static int tegra_xusb_remove(struct platform_device *pdev)
> struct tegra_xusb *tegra = platform_get_drvdata(pdev);
> struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
>
> + tegra_xusb_deinit_usb_phy(tegra);
> +
> usb_remove_hcd(xhci->shared_hcd);
> usb_put_hcd(xhci->shared_hcd);
> xhci->shared_hcd = NULL;
> @@ -1733,6 +1955,7 @@ static const struct tegra_xusb_soc tegra210_soc = {
> },
> .scale_ss_clock = false,
> .has_ipfs = true,
> + .otg_reset_sspi = true,

I'd prefer if these were initialized to false for the other per-SoC
structures. This has the advantage of making it clear what exactly is
supported and helps clarify whether this was done on purpose or whether
it was just omitted by accident.

With the nitpicks addressed, feel free to add:

Acked-by: Thierry Reding <[email protected]>


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

2020-01-28 17:59:23

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 09/18] usb: gadget: tegra-xudc: Remove usb-role-switch support

On Mon, Dec 30, 2019 at 04:39:46PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> V2-V3:
> - No changes in this version
> ---
> 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;

Could use an extra blank line after the block.

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

But this blank line is gratuituous.

> }
>
> 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");

This doesn't seem useful. This is going to be always printed, but it's
not a special case or anything. If it's normal behaviour, no need to
explicitly mention it.

Thierry

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


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

2020-01-28 18:03:34

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 10/18] usb: gadget: tegra-xudc: Add usb-phy support

On Mon, Dec 30, 2019 at 04:39:47PM +0530, Nagarjuna Kristam wrote:
> usb-phy is used to get notified on the USB role changes. Get usb-phy from
> the utmi phy.

s/utmi phy/UTMI PHY/

>
> Signed-off-by: Nagarjuna Kristam <[email protected]>
> ---
> V2-V3:
> - No changes in this version
> ---
> 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);

I'd add the parentheses here, too. Maybe also a colon:

"%s(): action is %ld\n"

> +
> + 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");

This obsoletes my comment in the previous patch, but maybe consider not
adding it in the first place.

> - 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);

I'd make this: "failed to get USB PHY: %d\n", which is easier to read
than the above.

Thierry

> + goto free_eps;
> + }
>
> pm_runtime_enable(&pdev->dev);
>
> --
> 2.7.4
>


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

2020-01-28 18:04:38

by Thierry Reding

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

On Mon, Dec 30, 2019 at 04:39:48PM +0530, Nagarjuna Kristam wrote:
> When device mode is set/uset, vbus override activity is done via

s/vbus/VBUS/

> exported functions from padctl driver. Use phy_set_mode instead.

Nit: phy_set_mode() to make it clearer that this is a function getting
called.

Thierry


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

2020-01-28 18:12:21

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 12/18] usb: gadget: tegra-xudc: support multiple device modes

On Mon, Dec 30, 2019 at 04:39:49PM +0530, Nagarjuna Kristam wrote:
> 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]>
> ---
> V3:
> - No changes in this version
> ---
> V2:
> - Updated err variable on failure to get usbphy.
> - Corrected identation after tegra_xudc_phy_get API call in tegra_xudc_probe.
> ---
> drivers/usb/gadget/udc/tegra-xudc.c | 228 ++++++++++++++++++++++++++----------
> 1 file changed, 167 insertions(+), 61 deletions(-)
>
> diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
> index 283c320..bf80fae 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;

Can be unsigned int. It's also very long. It might be better to choose a
shorter name so that when you use it, the lines don't get excessively
long. Alternatively you could keep this field name and instead declare
local variables to reference the current PHY to make lines shorter.

Actually, looking at this a bit more, I don't see current_phy_index ever
used by itself (other than the assignment and one check to see if a PHY
has been selected). So why not just store a pointer to the current PHY
and avoid all the dereferencing?

Thierry


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

2020-01-29 06:49:14

by Nagarjuna Kristam

[permalink] [raw]
Subject: Re: [Patch V3 12/18] usb: gadget: tegra-xudc: support multiple device modes



On 28-01-2020 23:40, Thierry Reding wrote:
>> struct tegra_xudc_save_regs saved_regs;
>> bool suspended;
>> bool powergated;
>>
>> - struct usb_phy *usbphy;
>> + struct usb_phy **usbphy;
>> + int current_phy_index;
> Can be unsigned int. It's also very long. It might be better to choose a
> shorter name so that when you use it, the lines don't get excessively
> long. Alternatively you could keep this field name and instead declare
> local variables to reference the current PHY to make lines shorter.
>
> Actually, looking at this a bit more, I don't see current_phy_index ever
> used by itself (other than the assignment and one check to see if a PHY
> has been selected). So why not just store a pointer to the current PHY
> and avoid all the dereferencing?
>
> Thierry

current_phy_index main purpose is to quickly get which index for USB 2
and 3 phy's to be used. This is used at mulitple functions. Based on
your comment above, I believe its good to use 2 pointers for UTMI and
USB 3 phy's, which are points to current phy index. This ensures to keep
line length as less as possible.

Thanks,
Nagarjuna

2020-01-29 09:14:36

by Nagarjuna Kristam

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



On 28-01-2020 23:02, Thierry Reding wrote:
> On Mon, Dec 30, 2019 at 04:39:40PM +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]>
>> ---
>> V3:
>> - Driver aborts if usb-role-switch is not added in dt forotg/peripheral
>> roles.
>> - Added role name strings instead of enum values in debug prints.
>> - Updated arguments and variable allignments as per Thierry inputs.
>> ---
>> V2:
>> - Removed dev_set_drvdata for port->dev.
>> - Added of_platform_depopulate during error handling and driver removal.
>> ---
>> drivers/phy/tegra/Kconfig | 1 +
>> drivers/phy/tegra/xusb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/phy/tegra/xusb.h | 3 +++
>> 3 files changed, 61 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..11ea9b5 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;
> This looks wrong. I don't think driver's are supposed to set this
> because it basically means that the device is being attached to the
> driver, but in this case it doesn't get probed by the driver and in
> fact the ports don't match the pad controller, so they can't really
> be driven by the same driver.
>
> Is there any particular reason why you need this?
>
> Thierry

Yes, port->dev.driver->owner is accessed in USB role switch driver in
API usb_role_switch_get. If driver param is not updated, it causes NULL
pointer exception. Based on your inputs, since this assignment is not
supposed to used, I believe option available is to create a new
device_driver structure and assign the same here.
Please share your thoughts.

- Nagarjuna

2020-01-29 09:27:19

by Thierry Reding

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

On Wed, Jan 29, 2020 at 02:45:38PM +0530, Nagarjuna Kristam wrote:
>
>
> On 28-01-2020 23:02, Thierry Reding wrote:
> > On Mon, Dec 30, 2019 at 04:39:40PM +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]>
> > > ---
> > > V3:
> > > - Driver aborts if usb-role-switch is not added in dt forotg/peripheral
> > > roles.
> > > - Added role name strings instead of enum values in debug prints.
> > > - Updated arguments and variable allignments as per Thierry inputs.
> > > ---
> > > V2:
> > > - Removed dev_set_drvdata for port->dev.
> > > - Added of_platform_depopulate during error handling and driver removal.
> > > ---
> > > drivers/phy/tegra/Kconfig | 1 +
> > > drivers/phy/tegra/xusb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
> > > drivers/phy/tegra/xusb.h | 3 +++
> > > 3 files changed, 61 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..11ea9b5 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;
> > This looks wrong. I don't think driver's are supposed to set this
> > because it basically means that the device is being attached to the
> > driver, but in this case it doesn't get probed by the driver and in
> > fact the ports don't match the pad controller, so they can't really
> > be driven by the same driver.
> >
> > Is there any particular reason why you need this?
> >
> > Thierry
>
> Yes, port->dev.driver->owner is accessed in USB role switch driver in API
> usb_role_switch_get. If driver param is not updated, it causes NULL pointer
> exception. Based on your inputs, since this assignment is not supposed to
> used, I believe option available is to create a new device_driver structure
> and assign the same here.
> Please share your thoughts.

Sounds to me more like what we want is to make the module_get() and
module_put() calls conditional to avoid the NULL pointer dereference.
Another common variant that I've seen in other subsystems is to make
drivers pass in an owner explicitly via some operations structure to
allow more fine-grained control over the reference count.

In this particular case one option to do this would be to add a struct
module *owner field to usb_role_switch_desc and that's copied to struct
usb_role_switch in usb_role_switch_register() so that that can be used
for reference counting (perhaps with the current code as fallback).

That would allow using simple devices (i.e. not bound to a driver) to
work with this framework as well.

Thierry


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

2020-01-29 09:28:39

by Thierry Reding

[permalink] [raw]
Subject: Re: [Patch V3 12/18] usb: gadget: tegra-xudc: support multiple device modes

On Wed, Jan 29, 2020 at 12:20:09PM +0530, Nagarjuna Kristam wrote:
>
>
> On 28-01-2020 23:40, Thierry Reding wrote:
> > > struct tegra_xudc_save_regs saved_regs;
> > > bool suspended;
> > > bool powergated;
> > > - struct usb_phy *usbphy;
> > > + struct usb_phy **usbphy;
> > > + int current_phy_index;
> > Can be unsigned int. It's also very long. It might be better to choose a
> > shorter name so that when you use it, the lines don't get excessively
> > long. Alternatively you could keep this field name and instead declare
> > local variables to reference the current PHY to make lines shorter.
> >
> > Actually, looking at this a bit more, I don't see current_phy_index ever
> > used by itself (other than the assignment and one check to see if a PHY
> > has been selected). So why not just store a pointer to the current PHY
> > and avoid all the dereferencing?
> >
> > Thierry
>
> current_phy_index main purpose is to quickly get which index for USB 2 and 3
> phy's to be used. This is used at mulitple functions. Based on your comment
> above, I believe its good to use 2 pointers for UTMI and USB 3 phy's, which
> are points to current phy index. This ensures to keep line length as less as
> possible.

Yes, I think in general it's preferable to use the objects directly
rather than an index into an array to find the objects.

Thierry


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