Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb34a13a authored by Ajay Agarwal's avatar Ajay Agarwal
Browse files

usb: dwc3: Add support for 4 PHYs for dual port controller



Add 2 new PHY instances (HS and SS) in dwc3 and dwc3-msm
structures. Probe and initialize them when the controller is
dual port capable. This capability is advertised by cores
supporting only host mode using the DT flag 'qcom,dual-port'.

Update PHY1 flags wherever applicable while maintaining single
port controller support.

Avoid workaround of SSPHY powerdown as a part of HS device
connect because of the known HW bug of the other SSPHY going in
and out of P3 when data transfers are being done.

Change-Id: If52fd6383cc2da8b763c774d2ad688448f90aea0
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
parent 37c2499e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -93,6 +93,8 @@ Optional properties :
	capable DWC3 which does not have extcon handle.
- qcom,default-mode-host: If present, start host mode on probe for an OTG
	capable DWC3 which does not have extcon handle.
- qcom,dual-port: If present, 2 different physical ports are supported by this
	core. Please note that this flag is valid for cores supporting only host mode.

Sub nodes:
- Sub node for "DWC3- USB3 controller".
+58 −1
Original line number Diff line number Diff line
@@ -198,6 +198,14 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
		return ret;
	}

	usb_phy_reset(dwc->usb2_phy1);
	ret = usb_phy_init(dwc->usb2_phy1);
	if (ret) {
		pr_err("%s: usb_phy_init(dwc->usb2_phy1) returned %d\n",
				__func__, ret);
		return ret;
	}

	if (dwc->maximum_speed <= USB_SPEED_HIGH)
		goto generic_phy_init;

@@ -215,6 +223,14 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
		return ret;
	}

	usb_phy_reset(dwc->usb3_phy1);
	ret = usb_phy_init(dwc->usb3_phy1);
	if (ret) {
		pr_err("%s: usb_phy_init(dwc->usb3_phy1) returned %d\n",
				__func__, ret);
		return ret;
	}

generic_phy_init:
	ret = phy_init(dwc->usb2_generic_phy);
	if (ret < 0)
@@ -670,12 +686,16 @@ static void dwc3_core_exit(struct dwc3 *dwc)
{
	dwc3_event_buffers_cleanup(dwc);

	usb_phy_shutdown(dwc->usb2_phy1);
	usb_phy_shutdown(dwc->usb2_phy);
	usb_phy_shutdown(dwc->usb3_phy1);
	usb_phy_shutdown(dwc->usb3_phy);
	phy_exit(dwc->usb2_generic_phy);
	phy_exit(dwc->usb3_generic_phy);

	usb_phy_set_suspend(dwc->usb2_phy1, 1);
	usb_phy_set_suspend(dwc->usb2_phy, 1);
	usb_phy_set_suspend(dwc->usb3_phy1, 1);
	usb_phy_set_suspend(dwc->usb3_phy, 1);
	phy_power_off(dwc->usb2_generic_phy);
	phy_power_off(dwc->usb3_generic_phy);
@@ -854,8 +874,11 @@ int dwc3_core_init(struct dwc3 *dwc)
	dwc3_frame_length_adjustment(dwc);

	usb_phy_set_suspend(dwc->usb2_phy, 0);
	if (dwc->maximum_speed >= USB_SPEED_SUPER)
	usb_phy_set_suspend(dwc->usb2_phy1, 0);
	if (dwc->maximum_speed >= USB_SPEED_SUPER) {
		usb_phy_set_suspend(dwc->usb3_phy, 0);
		usb_phy_set_suspend(dwc->usb3_phy1, 0);
	}

	ret = phy_power_on(dwc->usb2_generic_phy);
	if (ret < 0)
@@ -958,11 +981,15 @@ int dwc3_core_init(struct dwc3 *dwc)
	phy_power_off(dwc->usb2_generic_phy);

err2:
	usb_phy_set_suspend(dwc->usb2_phy1, 1);
	usb_phy_set_suspend(dwc->usb3_phy1, 1);
	usb_phy_set_suspend(dwc->usb2_phy, 1);
	usb_phy_set_suspend(dwc->usb3_phy, 1);
	dwc3_free_scratch_buffers(dwc);

err1:
	usb_phy_shutdown(dwc->usb2_phy1);
	usb_phy_shutdown(dwc->usb3_phy1);
	usb_phy_shutdown(dwc->usb2_phy);
	usb_phy_shutdown(dwc->usb3_phy);
	phy_exit(dwc->usb2_generic_phy);
@@ -984,6 +1011,12 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
	if (node) {
		dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
		dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
		if (dwc->dual_port) {
			dwc->usb2_phy1 = devm_usb_get_phy_by_phandle(dev,
								"usb-phy", 2);
			dwc->usb3_phy1 = devm_usb_get_phy_by_phandle(dev,
								"usb-phy", 3);
		}
	} else {
		dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
		dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
@@ -1013,6 +1046,30 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
		}
	}

	if (dwc->dual_port) {
		if (IS_ERR(dwc->usb2_phy1)) {
			ret = PTR_ERR(dwc->usb2_phy1);
			if (ret == -ENXIO || ret == -ENODEV) {
				dwc->usb2_phy1 = NULL;
			} else {
				if (ret != -EPROBE_DEFER)
					dev_err(dev, "no usb2 phy1 configured\n");
				return ret;
			}
		}

		if (IS_ERR(dwc->usb3_phy1)) {
			ret = PTR_ERR(dwc->usb3_phy1);
			if (ret == -ENXIO || ret == -ENODEV) {
				dwc->usb3_phy1 = NULL;
			} else {
				if (ret != -EPROBE_DEFER)
					dev_err(dev, "no usb3 phy1 configured\n");
				return ret;
			}
		}
	}

	dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
	if (IS_ERR(dwc->usb2_generic_phy)) {
		ret = PTR_ERR(dwc->usb2_generic_phy);
+8 −4
Original line number Diff line number Diff line
@@ -904,8 +904,10 @@ struct dwc3_scratchpad_array {
 * @hsphy_mode: UTMI phy mode, one of following:
 *		- USBPHY_INTERFACE_MODE_UTMI
 *		- USBPHY_INTERFACE_MODE_UTMIW
 * @usb2_phy: pointer to USB2 PHY
 * @usb3_phy: pointer to USB3 PHY
 * @usb2_phy: pointer to USB2 PHY 0
 * @usb2_phy1: pointer to USB2 PHY 1
 * @usb3_phy: pointer to USB3 PHY 0
 * @usb3_phy1: pointer to USB3 PHY 1
 * @usb2_generic_phy: pointer to USB2 PHY
 * @usb3_generic_phy: pointer to USB3 PHY
 * @phys_ready: flag to indicate that PHYs are ready
@@ -998,6 +1000,7 @@ struct dwc3_scratchpad_array {
 * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
 * @last_run_stop: timestamp denoting the last run_stop update
 * @num_gsi_eps: number of GSI based hardware accelerated endpoints
 * @dual_port: If true, this core supports two ports
 */
struct dwc3 {
	struct work_struct	drd_work;
@@ -1026,8 +1029,8 @@ struct dwc3 {
	struct usb_gadget	gadget;
	struct usb_gadget_driver *gadget_driver;

	struct usb_phy		*usb2_phy;
	struct usb_phy		*usb3_phy;
	struct usb_phy		*usb2_phy, *usb2_phy1;
	struct usb_phy		*usb3_phy, *usb3_phy1;

	struct phy		*usb2_generic_phy;
	struct phy		*usb3_generic_phy;
@@ -1219,6 +1222,7 @@ struct dwc3 {
	int			retries_on_error;
	ktime_t			last_run_stop;
	u32			num_gsi_eps;
	bool			dual_port;
};

#define work_to_dwc(w)		(container_of((w), struct dwc3, drd_work))
+159 −28
Original line number Diff line number Diff line
@@ -262,6 +262,7 @@ struct dwc3_msm {
	struct regulator	*dwc3_gdsc;

	struct usb_phy		*hs_phy, *ss_phy;
	struct usb_phy		*hs_phy1, *ss_phy1;

	struct dbm		*dbm;

@@ -333,6 +334,7 @@ struct dwc3_msm {

	struct device_node *dwc3_node;
	struct property *num_gsi_eps;
	bool			dual_port;
};

#define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -448,17 +450,37 @@ static bool dwc3_msm_is_host_superspeed(struct dwc3_msm *mdwc)
{
	int i, num_ports;
	u32 reg;
	bool is_host_ss = false;

	reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1);
	num_ports = HCS_MAX_PORTS(reg);

	/*
	 * For single port controller, PORTSC register 1 is for SS bus, hence it
	 * maps to the only SSPHY. But for dual port controllers, PORTSC
	 * registers 2 and 3 are for SS busses 0 and 1 respectively. Hence,
	 * PORTSC register 2 maps to SSPHY0 and 3 maps to SSPHY1. Handle the
	 * flag setting using the below logic which will also maintain single
	 * port core.
	 *       num_ports = 2 (or) 4 (depending on no. of ports)
	 *   ->num_ports/2 = 1 (or) 2
	 * So, for single port core, i % (num_ports/2) = 0, hence we will always
	 * be updating SSPHY0 flags.
	 * For dual port, i % (num_ports/2) = 0 for port 0 (or) 1 for port 1.
	 * Hence we update SSPHY0 for reg 0 and 2; and SSPHY1 for reg 1 and 3.
	 */
	for (i = 0; i < num_ports; i++) {
		reg = dwc3_msm_read_reg(mdwc->base, USB3_PORTSC + i*0x10);
		if ((reg & PORT_PE) && DEV_SUPERSPEED_ANY(reg))
			return true;
		if ((reg & PORT_PE) && DEV_SUPERSPEED_ANY(reg)) {
			is_host_ss = true;
			if (i % (num_ports / 2))
				mdwc->ss_phy1->flags |= DEVICE_IN_SS_MODE;
			else
				mdwc->ss_phy->flags |= DEVICE_IN_SS_MODE;
		}
	}

	return false;
	return is_host_ss;
}

static inline bool dwc3_msm_is_dev_superspeed(struct dwc3_msm *mdwc)
@@ -2305,13 +2327,58 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc, bool ignore_p3_state)
	return 0;
}

/* Generic functions to set/clear PHY flags */
static void dwc3_msm_set_hsphy_flags(struct dwc3_msm *mdwc, int set)
{
	mdwc->hs_phy->flags |= set;
	if (mdwc->hs_phy1)
		mdwc->hs_phy1->flags |= set;
}

static void dwc3_msm_clear_hsphy_flags(struct dwc3_msm *mdwc, int clear)
{
	mdwc->hs_phy->flags &= ~clear;
	if (mdwc->hs_phy1)
		mdwc->hs_phy1->flags &= ~clear;
}

static void dwc3_msm_set_ssphy_flags(struct dwc3_msm *mdwc, int set)
{
	mdwc->ss_phy->flags |= set;
	if (mdwc->ss_phy1)
		mdwc->ss_phy1->flags |= set;
}

static void dwc3_msm_clear_ssphy_flags(struct dwc3_msm *mdwc, int clear)
{
	mdwc->ss_phy->flags &= ~clear;
	if (mdwc->ss_phy1)
		mdwc->ss_phy1->flags &= ~clear;
}

static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc)
{
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	int i, num_ports;
	u32 reg;

	mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE);
	dwc3_msm_clear_hsphy_flags(mdwc, PHY_HSFS_MODE | PHY_LS_MODE);

	/*
	 * For single port controller, there are 2 PORTSC registers, one for
	 * HS bus and other for SS bus, hence both mapping to the only HSPHY.
	 * But for dual port controllers, there are 4 PORTSC registers: 0 and 1
	 * for HS busses 0 and 1 respectively; and registers 2 and 3 for SS
	 * busses 0 and 1 respectively. Hence, PORTSC registers 0 and 2 map to
	 * HSPHY 0 and registers 1 and 3 map to HSPHY1. Handle the flag setting
	 * using the below logic which will also maintain single port core.
	 *       num_ports = 2 (or) 4 (depending on no. of ports)
	 *   ->num_ports/2 = 1 (or) 2
	 * So, for single port core, i % (num_ports/2) = 0, hence we will always
	 * be updating HSPHY0 flags.
	 * For dual port, i % (num_ports/2) = 0 for port 0 (or) 1 for port 1.
	 * Hence we update HSPHY0 for reg 0 and 2; and HSPHY1 for reg 1 and 3.
	 */
	if (mdwc->in_host_mode) {
		reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1);
		num_ports = HCS_MAX_PORTS(reg);
@@ -2319,10 +2386,21 @@ static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc)
			reg = dwc3_msm_read_reg(mdwc->base,
					USB3_PORTSC + i*0x10);
			if (reg & PORT_PE) {
				if (DEV_HIGHSPEED(reg) || DEV_FULLSPEED(reg))
					mdwc->hs_phy->flags |= PHY_HSFS_MODE;
				else if (DEV_LOWSPEED(reg))
					mdwc->hs_phy->flags |= PHY_LS_MODE;
				if (DEV_HIGHSPEED(reg) || DEV_FULLSPEED(reg)) {
					if (i % (num_ports / 2))
						mdwc->hs_phy1->flags |=
								PHY_HSFS_MODE;
					else
						mdwc->hs_phy->flags |=
								PHY_HSFS_MODE;
				} else if (DEV_LOWSPEED(reg)) {
					if (i % (num_ports / 2))
						mdwc->hs_phy1->flags |=
								PHY_LS_MODE;
					else
						mdwc->hs_phy->flags |=
								PHY_LS_MODE;
				}
			}
		}
	} else {
@@ -2556,6 +2634,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse,
	dwc3_set_phy_speed_flags(mdwc);
	/* Suspend HS PHY */
	usb_phy_set_suspend(mdwc->hs_phy, 1);
	usb_phy_set_suspend(mdwc->hs_phy1, 1);

	/*
	 * Synopsys Superspeed PHY does not support ss_phy_irq, so to detect
@@ -2579,9 +2658,10 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse,
			dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
		}
		/* indicate phy about SS mode */
		if (dwc3_msm_is_superspeed(mdwc))
			mdwc->ss_phy->flags |= DEVICE_IN_SS_MODE;
		dwc3_msm_is_superspeed(mdwc);

		usb_phy_set_suspend(mdwc->ss_phy, 1);
		usb_phy_set_suspend(mdwc->ss_phy1, 1);
		mdwc->lpm_flags |= MDWC3_SS_PHY_SUSPEND;
	} else if (mdwc->use_pwr_event_for_wakeup) {
		dwc3_msm_set_ss_pwr_events(mdwc, true);
@@ -2770,13 +2850,14 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
	/* Resume SS PHY */
	if (dwc->maximum_speed >= USB_SPEED_SUPER &&
			mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
		mdwc->ss_phy->flags &= ~(PHY_LANE_A | PHY_LANE_B);
		dwc3_msm_clear_ssphy_flags(mdwc, PHY_LANE_A | PHY_LANE_B);
		if (mdwc->typec_orientation == ORIENTATION_CC1)
			mdwc->ss_phy->flags |= PHY_LANE_A;
		if (mdwc->typec_orientation == ORIENTATION_CC2)
			mdwc->ss_phy->flags |= PHY_LANE_B;
		usb_phy_set_suspend(mdwc->ss_phy1, 0);
		usb_phy_set_suspend(mdwc->ss_phy, 0);
		mdwc->ss_phy->flags &= ~DEVICE_IN_SS_MODE;
		dwc3_msm_clear_ssphy_flags(mdwc, DEVICE_IN_SS_MODE);
		mdwc->lpm_flags &= ~MDWC3_SS_PHY_SUSPEND;

		if (mdwc->in_host_mode) {
@@ -2787,8 +2868,9 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
		}
	}

	mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE);
	dwc3_msm_clear_hsphy_flags(mdwc, PHY_HSFS_MODE | PHY_LS_MODE);
	/* Resume HS PHY */
	usb_phy_set_suspend(mdwc->hs_phy1, 0);
	usb_phy_set_suspend(mdwc->hs_phy, 0);

	/* Recover from controller power collapse */
@@ -3720,6 +3802,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)

	mdwc->charging_disabled = of_property_read_bool(node,
				"qcom,charging-disabled");
	mdwc->dual_port = of_property_read_bool(node, "qcom,dual-port");

	ret = of_property_read_u32(node, "qcom,lpm-to-suspend-delay-ms",
				&mdwc->lpm_to_suspend_delay);
@@ -3909,6 +3992,24 @@ static int dwc3_msm_probe(struct platform_device *pdev)
		goto put_dwc3;
	}

	if (mdwc->dual_port) {
		mdwc->hs_phy1 = devm_usb_get_phy_by_phandle(&mdwc->dwc3->dev,
							"usb-phy", 2);
		if (IS_ERR(mdwc->hs_phy1)) {
			dev_err(&pdev->dev, "unable to get hsphy1 device\n");
			ret = PTR_ERR(mdwc->hs_phy1);
			goto put_dwc3;
		}

		mdwc->ss_phy1 = devm_usb_get_phy_by_phandle(&mdwc->dwc3->dev,
							"usb-phy", 3);
		if (IS_ERR(mdwc->ss_phy1)) {
			dev_err(&pdev->dev, "unable to get ssphy1 device\n");
			ret = PTR_ERR(mdwc->ss_phy1);
			goto put_dwc3;
		}
	}

	/* use default as nominal bus voting */
	mdwc->default_bus_vote = BUS_VOTE_NOMINAL;
	ret = of_property_read_u32(node, "qcom,default-bus-vote",
@@ -3969,6 +4070,13 @@ static int dwc3_msm_probe(struct platform_device *pdev)
								__func__);
	}

	if (mdwc->dual_port && dwc->dr_mode != USB_DR_MODE_HOST) {
		dev_err(&pdev->dev, "Dual port not allowed for DRD core\n");
		goto err_get_extcon;
	}

	dwc->dual_port = mdwc->dual_port;

	mutex_init(&mdwc->suspend_resume_mutex);

	if (of_property_read_bool(node, "extcon")) {
@@ -4102,8 +4210,7 @@ static int dwc3_msm_remove(struct platform_device *pdev)
	cancel_delayed_work_sync(&mdwc->perf_vote_work);
	cancel_delayed_work_sync(&mdwc->sm_work);

	if (mdwc->hs_phy)
		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
	dwc3_msm_clear_hsphy_flags(mdwc, PHY_HOST_MODE);
	dbg_event(0xFF, "Remov put", 0);
	platform_device_put(mdwc->dwc3);
	of_platform_depopulate(&pdev->dev);
@@ -4168,6 +4275,18 @@ static int dwc3_msm_host_notifier(struct notifier_block *nb,
	if (event != USB_DEVICE_ADD && event != USB_DEVICE_REMOVE)
		return NOTIFY_DONE;

	/*
	 * STAR: 9001378493: SSPHY1 going in and out of P3 when HS transfers
	 * being done on port 0. We do not need the below workaround of
	 * corresponding SSPHY powerdown for multiport controller. Instead, we
	 * will keep the SSPHY autosuspend disabled when USB is in resumed
	 * state. Since dual port controller is present only on automotive
	 * platforms, power leakage is not a concern. Also, as a part of USB
	 * suspend sequence, we will enable autosuspend for SSPHYs to go to P3.
	 */
	if (mdwc->dual_port)
		return NOTIFY_DONE;

	/*
	 * For direct-attach devices, new udev is direct child of root hub
	 * i.e. dwc -> xhci -> root_hub -> udev
@@ -4287,23 +4406,26 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
	if (on) {
		dev_dbg(mdwc->dev, "%s: turn on host\n", __func__);

		mdwc->hs_phy->flags |= PHY_HOST_MODE;
		dwc3_msm_set_hsphy_flags(mdwc, PHY_HOST_MODE);
		pm_runtime_get_sync(mdwc->dev);
		dbg_event(0xFF, "StrtHost gync",
			atomic_read(&mdwc->dev->power.usage_count));
		if (dwc->maximum_speed >= USB_SPEED_SUPER) {
			mdwc->ss_phy->flags |= PHY_HOST_MODE;
			dwc3_msm_set_ssphy_flags(mdwc, PHY_HOST_MODE);
			usb_phy_notify_connect(mdwc->ss_phy,
						USB_SPEED_SUPER);
			usb_phy_notify_connect(mdwc->ss_phy1,
						USB_SPEED_SUPER);
		}

		usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
		usb_phy_notify_connect(mdwc->hs_phy1, USB_SPEED_HIGH);
		if (!IS_ERR_OR_NULL(mdwc->vbus_reg))
			ret = regulator_enable(mdwc->vbus_reg);
		if (ret) {
			dev_err(mdwc->dev, "unable to enable vbus_reg\n");
			mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
			mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
			dwc3_msm_clear_hsphy_flags(mdwc, PHY_HOST_MODE);
			dwc3_msm_clear_ssphy_flags(mdwc, PHY_HOST_MODE);
			pm_runtime_put_sync(mdwc->dev);
			dbg_event(0xFF, "vregerr psync",
				atomic_read(&mdwc->dev->power.usage_count));
@@ -4326,8 +4448,8 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
			if (!IS_ERR_OR_NULL(mdwc->vbus_reg))
				regulator_disable(mdwc->vbus_reg);

			mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
			mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
			dwc3_msm_clear_hsphy_flags(mdwc, PHY_HOST_MODE);
			dwc3_msm_clear_ssphy_flags(mdwc, PHY_HOST_MODE);
			pm_runtime_put_sync(mdwc->dev);
			dbg_event(0xFF, "pdeverr psync",
				atomic_read(&mdwc->dev->power.usage_count));
@@ -4386,14 +4508,17 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
		pm_runtime_get_sync(mdwc->dev);
		dbg_event(0xFF, "StopHost gsync",
			atomic_read(&mdwc->dev->power.usage_count));
		usb_phy_notify_disconnect(mdwc->hs_phy1, USB_SPEED_HIGH);
		usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
		if (mdwc->ss_phy->flags & PHY_HOST_MODE) {
		if (dwc->maximum_speed >= USB_SPEED_SUPER) {
			usb_phy_notify_disconnect(mdwc->ss_phy1,
					USB_SPEED_SUPER);
			usb_phy_notify_disconnect(mdwc->ss_phy,
					USB_SPEED_SUPER);
			mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
		}

		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
		dwc3_msm_clear_ssphy_flags(mdwc, PHY_HOST_MODE);
		dwc3_msm_clear_hsphy_flags(mdwc, PHY_HOST_MODE);
		dwc3_host_exit(dwc);
		usb_unregister_notify(&mdwc->host_nb);

@@ -4918,10 +5043,12 @@ static int dwc3_msm_pm_freeze(struct device *dev)
	 * PHYs also need to be power collapsed, so call notify_disconnect
	 * before suspend to ensure it.
	 */
	usb_phy_notify_disconnect(mdwc->hs_phy1, USB_SPEED_HIGH);
	usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
	mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
	dwc3_msm_clear_hsphy_flags(mdwc, PHY_HOST_MODE);
	usb_phy_notify_disconnect(mdwc->ss_phy1, USB_SPEED_SUPER);
	usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER);
	mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
	dwc3_msm_clear_ssphy_flags(mdwc, PHY_HOST_MODE);

	/*
	 * Power collapse the core. Hence call dwc3_msm_suspend with
@@ -4960,12 +5087,16 @@ static int dwc3_msm_pm_restore(struct device *dev)
	pm_runtime_enable(dev);

	/* Restore PHY flags if hibernated in host mode */
	mdwc->hs_phy->flags |= PHY_HOST_MODE;
	dwc3_msm_set_hsphy_flags(mdwc, PHY_HOST_MODE);
	usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
	usb_phy_notify_connect(mdwc->hs_phy1, USB_SPEED_HIGH);

	if (dwc->maximum_speed >= USB_SPEED_SUPER) {
		mdwc->ss_phy->flags |= PHY_HOST_MODE;
		dwc3_msm_set_ssphy_flags(mdwc, PHY_HOST_MODE);
		usb_phy_notify_connect(mdwc->ss_phy,
					USB_SPEED_SUPER);
		usb_phy_notify_connect(mdwc->ss_phy1,
					USB_SPEED_SUPER);
	}

	/* kick in otg state machine */