Commit f67213ce authored by Nagarjuna Kristam's avatar Nagarjuna Kristam Committed by Thierry Reding
Browse files

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: default avatarNagarjuna Kristam <nkristam@nvidia.com>
Acked-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
[treding@nvidia.com: rebase onto Greg's usb-next branch]
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent ca9e742b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.

+72 −0
Original line number Diff line number Diff line
@@ -541,6 +541,11 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port,

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

	device_unregister(&port->dev);
}

@@ -551,11 +556,64 @@ 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 usb_role_switch *sw,
				  enum usb_role role)
{
	struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw);

	dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]);

	return 0;
}

static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
{
	struct usb_role_switch_desc role_sx_desc = {
		.fwnode = dev_fwnode(&port->dev),
		.set = tegra_xusb_role_sw_set,
	};
	int err = 0;

	/*
	 * USB role switch driver needs parent driver owner info. This is a
	 * suboptimal solution. TODO: Need to revisit this in a follow-up patch
	 * where an optimal solution is possible with changes to USB role
	 * switch driver.
	 */
	port->dev.driver = devm_kzalloc(&port->dev,
					sizeof(struct device_driver),
					GFP_KERNEL);
	port->dev.driver->owner	 = THIS_MODULE;

	port->usb_role_sw = usb_role_switch_register(&port->dev,
						     &role_sx_desc);
	if (IS_ERR(port->usb_role_sw)) {
		err = PTR_ERR(port->usb_role_sw);
		dev_err(&port->dev, "failed to register USB role switch: %d",
			err);
		return err;
	}

	usb_role_switch_set_drvdata(port->usb_role_sw, port);

	/* 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 +630,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);
}
+3 −0
Original line number Diff line number Diff line
@@ -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;
};