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 <nkristam@nvidia.com> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> [treding@nvidia.com: rebase onto Greg's usb-next branch] Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
ca9e742b5c
commit
f67213cee2
3 changed files with 76 additions and 0 deletions
|
@ -2,6 +2,7 @@
|
||||||
config PHY_TEGRA_XUSB
|
config PHY_TEGRA_XUSB
|
||||||
tristate "NVIDIA Tegra XUSB pad controller driver"
|
tristate "NVIDIA Tegra XUSB pad controller driver"
|
||||||
depends on ARCH_TEGRA
|
depends on ARCH_TEGRA
|
||||||
|
select USB_CONN_GPIO
|
||||||
help
|
help
|
||||||
Choose this option if you have an NVIDIA Tegra SoC.
|
Choose this option if you have an NVIDIA Tegra SoC.
|
||||||
|
|
||||||
|
|
|
@ -541,6 +541,11 @@ unregister:
|
||||||
|
|
||||||
static void tegra_xusb_port_unregister(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);
|
device_unregister(&port->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,11 +556,64 @@ static const char *const modes[] = {
|
||||||
[USB_DR_MODE_OTG] = "otg",
|
[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)
|
static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
|
||||||
{
|
{
|
||||||
struct tegra_xusb_port *port = &usb2->base;
|
struct tegra_xusb_port *port = &usb2->base;
|
||||||
struct device_node *np = port->dev.of_node;
|
struct device_node *np = port->dev.of_node;
|
||||||
const char *mode;
|
const char *mode;
|
||||||
|
int err;
|
||||||
|
|
||||||
usb2->internal = of_property_read_bool(np, "nvidia,internal");
|
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;
|
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");
|
usb2->supply = devm_regulator_get(&port->dev, "vbus");
|
||||||
return PTR_ERR_OR_ZERO(usb2->supply);
|
return PTR_ERR_OR_ZERO(usb2->supply);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include <linux/usb/otg.h>
|
#include <linux/usb/otg.h>
|
||||||
|
#include <linux/usb/role.h>
|
||||||
|
|
||||||
/* legacy entry points for backwards-compatibility */
|
/* legacy entry points for backwards-compatibility */
|
||||||
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
|
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
|
||||||
|
@ -266,6 +267,8 @@ struct tegra_xusb_port {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
|
struct usb_role_switch *usb_role_sw;
|
||||||
|
|
||||||
const struct tegra_xusb_port_ops *ops;
|
const struct tegra_xusb_port_ops *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue