]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
phy: tegra: xusb: Add usb-role-switch support
authorNagarjuna Kristam <nkristam@nvidia.com>
Mon, 10 Feb 2020 08:11:29 +0000 (13:41 +0530)
committerThierry Reding <treding@nvidia.com>
Thu, 19 Mar 2020 12:54:35 +0000 (13:54 +0100)
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>
drivers/phy/tegra/Kconfig
drivers/phy/tegra/xusb.c
drivers/phy/tegra/xusb.h

index f9817c3ae85f02773c88899345ac352c24de178d..df07c4dea059b65a2ca55a40f12bf073fa215d72 100644 (file)
@@ -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.
 
index f98ec3922c0289d1984efd183eab38c55de4dbe1..f1cb472dce96964539bddec689a33cb52ed4a54a 100644 (file)
@@ -541,6 +541,11 @@ unregister:
 
 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);
 }
index da94fcce630752acb3d89f15175fea4b959efc59..9f2789984e881f9ee4dcce9b8bdb8e0bcafb5e14 100644 (file)
@@ -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;
 };