Merge 5.18-rc5 into usb-next
[linux-2.6-microblaze.git] / drivers / usb / dwc3 / core.c
index d28cd1a..2345a54 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
 #include <linux/acpi.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/reset.h>
@@ -85,7 +86,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
                 * mode. If the controller supports DRD but the dr_mode is not
                 * specified or set to OTG, then set the mode to peripheral.
                 */
-               if (mode == USB_DR_MODE_OTG &&
+               if (mode == USB_DR_MODE_OTG && !dwc->edev &&
                    (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
                     !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
                    !DWC3_VER_IS_PRIOR(DWC3, 330A))
@@ -342,7 +343,6 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
  *             from the default, this will set clock period in DWC3_GUCTL
  *             register.
  * @dwc: Pointer to our controller context structure
- * @ref_clk_per: reference clock period in ns
  */
 static void dwc3_ref_clk_period(struct dwc3 *dwc)
 {
@@ -1633,6 +1633,51 @@ static void dwc3_check_params(struct dwc3 *dwc)
        }
 }
 
+static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
+{
+       struct device *dev = dwc->dev;
+       struct device_node *np_phy;
+       struct extcon_dev *edev = NULL;
+       const char *name;
+
+       if (device_property_read_bool(dev, "extcon"))
+               return extcon_get_edev_by_phandle(dev, 0);
+
+       /*
+        * Device tree platforms should get extcon via phandle.
+        * On ACPI platforms, we get the name from a device property.
+        * This device property is for kernel internal use only and
+        * is expected to be set by the glue code.
+        */
+       if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) {
+               edev = extcon_get_extcon_dev(name);
+               if (!edev)
+                       return ERR_PTR(-EPROBE_DEFER);
+
+               return edev;
+       }
+
+       /*
+        * Try to get an extcon device from the USB PHY controller's "port"
+        * node. Check if it has the "port" node first, to avoid printing the
+        * error message from underlying code, as it's a valid case: extcon
+        * device (and "port" node) may be missing in case of "usb-role-switch"
+        * or OTG mode.
+        */
+       np_phy = of_parse_phandle(dev->of_node, "phys", 0);
+       if (of_graph_is_present(np_phy)) {
+               struct device_node *np_conn;
+
+               np_conn = of_graph_get_remote_node(np_phy, -1, -1);
+               if (np_conn)
+                       edev = extcon_find_edev_by_node(np_conn);
+               of_node_put(np_conn);
+       }
+       of_node_put(np_phy);
+
+       return edev;
+}
+
 static int dwc3_probe(struct platform_device *pdev)
 {
        struct device           *dev = &pdev->dev;
@@ -1768,6 +1813,13 @@ static int dwc3_probe(struct platform_device *pdev)
                goto err2;
        }
 
+       dwc->edev = dwc3_get_extcon(dwc);
+       if (IS_ERR(dwc->edev)) {
+               ret = PTR_ERR(dwc->edev);
+               dev_err_probe(dwc->dev, ret, "failed to get extcon\n");
+               goto err3;
+       }
+
        ret = dwc3_get_dr_mode(dwc);
        if (ret)
                goto err3;