diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 923a6c623850..53a8d92ad663 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -137,6 +137,8 @@ static void __dwc3_set_mode(struct work_struct *work)
 	if (!desired_dr_role)
 		goto out;
 
+	dwc3_notify_set_mode(dwc, desired_dr_role);
+
 	if (desired_dr_role == dwc->current_dr_role)
 		goto out;
 
@@ -1751,7 +1753,8 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
 	return 0;
 }
 
-struct dwc3 *dwc3_probe(struct platform_device *pdev)
+struct dwc3 *dwc3_probe(struct platform_device *pdev,
+			struct dwc3_glue_data *glue_data)
 {
 	struct device		*dev = &pdev->dev;
 	struct resource		*res, dwc_res;
@@ -1765,6 +1768,11 @@ struct dwc3 *dwc3_probe(struct platform_device *pdev)
 
 	dwc->dev = dev;
 
+	if (glue_data) {
+		dwc->glue_data = glue_data->glue_data;
+		dwc->glue_ops = glue_data->ops;
+	}
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
 		dev_err(dev, "missing memory resource\n");
@@ -1914,7 +1922,7 @@ static int dwc3_plat_probe(struct platform_device *pdev)
 {
 	struct dwc3 *dwc;
 
-	dwc = dwc3_probe(pdev);
+	dwc = dwc3_probe(pdev, NULL);
 	if (IS_ERR(dwc))
 		return PTR_ERR(dwc);
 
@@ -1925,6 +1933,9 @@ static int dwc3_plat_probe(struct platform_device *pdev)
 
 void dwc3_remove(struct dwc3 *dwc)
 {
+	dwc->glue_data = NULL;
+	dwc->glue_ops = NULL;
+
 	pm_runtime_get_sync(dwc->dev);
 
 	dwc3_core_exit_mode(dwc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 3ec4a961befe..aefcb0d388b7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -968,6 +968,23 @@ struct dwc3_scratchpad_array {
 	__le64	dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
 };
 
+/*
+ * struct dwc3_glue_ops - The ops indicate the notifications that
+ *				need to be passed on to glue layer.
+ * @notify_cable_disconnect: Notify glue of cable removal
+ *				irrespective of host or device mode.
+ * @set_mode: Notify glue before mode change is about to happen.
+ */
+struct dwc3_glue_ops {
+	void	(*notify_cable_disconnect)(void *glue_data);
+	void	(*set_mode)(void *glue_data, u32 desired_dr_role);
+};
+
+struct dwc3_glue_data {
+	void			*glue_data;
+	struct dwc3_glue_ops	*ops;
+};
+
 /**
  * struct dwc3 - representation of our controller
  * @drd_work: workqueue used for role swapping
@@ -1124,6 +1141,9 @@ struct dwc3_scratchpad_array {
  * @num_ep_resized: carries the current number endpoints which have had its tx
  *		    fifo resized.
  * @debug_root: root debugfs directory for this device to put its files in.
+ * @glue_data: Private data stored by core to be passed on to glue during
+ *		role switch notifications.
+ * @glue_ops: Store pointers to glue notifcation callbacks.
  */
 struct dwc3 {
 	struct work_struct	drd_work;
@@ -1340,6 +1360,9 @@ struct dwc3 {
 	int			last_fifo_depth;
 	int			num_ep_resized;
 	struct dentry		*debug_root;
+
+	void			*glue_data;
+	const struct dwc3_glue_ops *glue_ops;
 };
 
 #define INCRX_BURST_MODE 0
@@ -1553,7 +1576,8 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
 
 int dwc3_core_soft_reset(struct dwc3 *dwc);
 
-struct dwc3 *dwc3_probe(struct platform_device *pdev);
+struct dwc3 *dwc3_probe(struct platform_device *pdev,
+			struct dwc3_glue_data *glue_data);
 void dwc3_remove(struct dwc3 *dwc);
 
 int dwc3_runtime_suspend(struct dwc3 *dwc);
@@ -1563,6 +1587,19 @@ int dwc3_suspend(struct dwc3 *dwc);
 int dwc3_resume(struct dwc3 *dwc);
 void dwc3_complete(struct dwc3 *dwc);
 
+static inline void dwc3_notify_cable_disconnect(struct dwc3 *dwc)
+{
+	if (dwc->glue_ops && dwc->glue_ops->notify_cable_disconnect)
+		dwc->glue_ops->notify_cable_disconnect(dwc->glue_data);
+}
+
+static inline void dwc3_notify_set_mode(struct dwc3 *dwc,
+					u32 desired_dr_role)
+{
+	if (dwc->glue_ops && dwc->glue_ops->set_mode)
+		dwc->glue_ops->set_mode(dwc->glue_data, desired_dr_role);
+}
+
 #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
 int dwc3_host_init(struct dwc3 *dwc);
 void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 039bf241769a..947faeef0e4d 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -461,6 +461,15 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
 		break;
 	}
 
+	/*
+	 * When cable is removed, the role changes to default mode.
+	 * In the case we are in device mode and removed the cable, the
+	 * glue needs to know that we are disconnected. It must not notify
+	 * the change of mode to default mode.
+	 */
+	if (role == USB_ROLE_NONE)
+		dwc3_notify_cable_disconnect(dwc);
+
 	dwc3_set_mode(dwc, mode);
 	return 0;
 }
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 3c9a2b5cd559..4013a5e6c6c0 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -88,6 +88,9 @@ struct dwc3_qcom {
 	bool			pm_suspended;
 	struct icc_path		*icc_path_ddr;
 	struct icc_path		*icc_path_apps;
+
+	bool			enable_rt;
+	enum usb_role		current_role;
 };
 
 static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
@@ -673,16 +676,71 @@ static const struct software_node dwc3_qcom_swnode = {
 	.properties = dwc3_qcom_acpi_properties,
 };
 
+static void dwc3_qcom_handle_cable_disconnect(void *data)
+{
+	struct dwc3_qcom *qcom = (struct dwc3_qcom *)data;
+	/*
+	 * If we are in device mode and get a cable disconnect,
+	 * handle it by clearing OTG_VBUS_VALID bit in wrapper.
+	 * The next set_mode to default role can be ignored.
+	 */
+	if (qcom->current_role == USB_ROLE_DEVICE) {
+		pm_runtime_get_sync(qcom->dev);
+		dwc3_qcom_vbus_override_enable(qcom, false);
+		pm_runtime_put_autosuspend(qcom->dev);
+	}
+
+	pm_runtime_mark_last_busy(qcom->dev);
+	qcom->current_role = USB_ROLE_NONE;
+}
+
+static void dwc3_qcom_handle_set_mode(void *data, u32 desired_dr_role)
+{
+	struct dwc3_qcom *qcom = (struct dwc3_qcom *)data;
+
+	/*
+	 * If we are in device mode and get a cable disconnect,
+	 * handle it by clearing OTG_VBUS_VALID bit in wrapper.
+	 * The next set_mode to default role can be ignored and
+	 * so the OTG_VBUS_VALID should be set iff the current role
+	 * is NONE and we need to enter DEVICE mode.
+	 */
+	if ((qcom->current_role == USB_ROLE_NONE) &&
+	    (desired_dr_role == DWC3_GCTL_PRTCAP_DEVICE)) {
+		dwc3_qcom_vbus_override_enable(qcom, true);
+		qcom->current_role = USB_ROLE_DEVICE;
+	} else if ((desired_dr_role == DWC3_GCTL_PRTCAP_HOST) &&
+		   (qcom->current_role != USB_ROLE_HOST)) {
+		qcom->current_role = USB_ROLE_HOST;
+	}
+
+	pm_runtime_mark_last_busy(qcom->dev);
+}
+
+struct dwc3_glue_ops dwc3_qcom_glue_hooks = {
+	.notify_cable_disconnect = dwc3_qcom_handle_cable_disconnect,
+	.set_mode = dwc3_qcom_handle_set_mode,
+};
+
 static int dwc3_qcom_probe_core(struct platform_device *pdev, struct dwc3_qcom *qcom)
 {
 	struct dwc3 *dwc;
 
-	dwc = dwc3_probe(pdev);
+	struct dwc3_glue_data qcom_glue_data = {
+		.glue_data	= qcom,
+		.ops		= &dwc3_qcom_glue_hooks,
+	};
+
+	dwc = dwc3_probe(pdev,
+			 qcom->enable_rt ? &qcom_glue_data : NULL);
 	if (IS_ERR(dwc))
 		return PTR_ERR(dwc);
 
 	qcom->dwc = dwc;
 
+	if (qcom->enable_rt)
+		pm_runtime_allow(qcom->dev);
+
 	return 0;
 }
 
@@ -897,6 +955,23 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
 	if (ignore_pipe_clk)
 		dwc3_qcom_select_utmi_clk(qcom);
 
+	qcom->enable_rt = device_property_read_bool(dev,
+				"qcom,enable-rt");
+	if (!legacy_binding) {
+		/*
+		 * If we are enabling runtime, then we are using flattened
+		 * device implementation.
+		 */
+		qcom->mode = usb_get_dr_mode(dev);
+
+		if (qcom->mode == USB_DR_MODE_HOST)
+			qcom->current_role = USB_ROLE_HOST;
+		else if (qcom->mode == USB_DR_MODE_PERIPHERAL)
+			qcom->current_role = USB_ROLE_DEVICE;
+		else
+			qcom->current_role = USB_ROLE_NONE;
+	}
+
 	if (legacy_binding)
 		ret = dwc3_qcom_of_register_core(pdev);
 	else
@@ -913,8 +988,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
 
 	if (qcom->dwc_dev)
 		qcom->mode = usb_get_dr_mode(&qcom->dwc_dev->dev);
-	else
-		qcom->mode = usb_get_dr_mode(dev);
 
 	/* enable vbus override for device mode */
 	if (qcom->mode != USB_DR_MODE_HOST)
