diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index eb0924e535ad..68080bf4a557 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -4423,7 +4423,7 @@ static void dwc3_start_stop_device(struct dwc3_msm *mdwc, bool start) dbg_log_string("stop_device_mode completed"); } -int dwc3_msm_release_ss_lane(struct device *dev) +int dwc3_msm_release_ss_lane(struct device *dev, bool usb_dp_concurrent_mode) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); struct dwc3 *dwc = NULL; @@ -4439,6 +4439,16 @@ int dwc3_msm_release_ss_lane(struct device *dev) return -EAGAIN; } + /* + * If the MPA connected is multi_func capable set the flag assuming + * that USB and DP is operating in concurrent mode and bail out early. + */ + if (usb_dp_concurrent_mode) { + mdwc->ss_phy->flags |= PHY_USB_DP_CONCURRENT_MODE; + dbg_event(0xFF, "USB_DP_CONCURRENT_MODE", 1); + return 0; + } + dbg_event(0xFF, "ss_lane_release", 0); /* flush any pending work */ flush_work(&mdwc->resume_work); @@ -5244,6 +5254,7 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) USB_SPEED_SUPER); usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER); + mdwc->ss_phy->flags &= ~PHY_USB_DP_CONCURRENT_MODE; } redriver_notify_disconnect(mdwc->ss_redriver_node); @@ -5365,6 +5376,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH); usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER); redriver_notify_disconnect(mdwc->ss_redriver_node); + mdwc->ss_phy->flags &= ~PHY_USB_DP_CONCURRENT_MODE; dwc3_override_vbus_status(mdwc, false); dwc3_msm_write_reg_field(mdwc->base, DWC3_GUSB3PIPECTL(0), DWC3_GUSB3PIPECTL_SUSPHY, 0); diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 055d27ab9251..386b0aabbafc 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2021, The Linux Foundation. All rights reserved. */ #include @@ -405,9 +405,11 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy) } /* override hardware control for reset of qmp phy */ - writel_relaxed(SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET, - phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); + if (!(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) + writel_relaxed(SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET, + phy->base + + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); /* update port select */ if (val > 0) { @@ -417,11 +419,13 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy) phy->phy_reg[USB3_DP_COM_TYPEC_CTRL]); } - msm_ssphy_qmp_setmode(phy, USB3_DP_COMBO_MODE); + if (!(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) { + msm_ssphy_qmp_setmode(phy, USB3_DP_COMBO_MODE); - /* bring both USB and DP PHYs PCS block out of reset */ - writel_relaxed(0x00, phy->base + - phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); + /* bring both USB and DP PHYs PCS block out of reset */ + writel_relaxed(0x00, phy->base + + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); + } break; case USB3_OR_DP: if (val > 0) { @@ -513,6 +517,12 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy) goto fail; } + /* perform software reset of PHY common logic */ + if (phy->phy_type == USB3_AND_DP && + !(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) + writel_relaxed(0x00, + phy->base + phy->phy_reg[USB3_DP_COM_SW_RESET]); + /* perform software reset of PCS/Serdes */ writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_PHY_SW_RESET]); /* start PCS/Serdes to operation mode */ @@ -556,6 +566,25 @@ static int msm_ssphy_qmp_dp_combo_reset(struct usb_phy *uphy) phy); int ret = 0; + if (phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE) { + dev_dbg(uphy->dev, "Resetting USB part of QMP phy\n"); + + /* Assert USB3 PHY CSR reset */ + ret = reset_control_assert(phy->phy_reset); + if (ret) { + dev_err(uphy->dev, "phy_reset assert failed\n"); + goto exit; + } + + /* Deassert USB3 PHY CSR reset */ + ret = reset_control_deassert(phy->phy_reset); + if (ret) { + dev_err(uphy->dev, "phy_reset deassert failed\n"); + goto exit; + } + return 0; + } + dev_dbg(uphy->dev, "Global reset of QMP DP combo phy\n"); /* Assert global PHY reset */ ret = reset_control_assert(phy->global_phy_reset); diff --git a/include/linux/usb/dwc3-msm.h b/include/linux/usb/dwc3-msm.h index 6762da8c2c5f..3921c18df1cf 100644 --- a/include/linux/usb/dwc3-msm.h +++ b/include/linux/usb/dwc3-msm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #ifndef __LINUX_USB_DWC3_MSM_H @@ -19,6 +19,7 @@ #define EUD_SPOOF_DISCONNECT BIT(6) #define EUD_SPOOF_CONNECT BIT(7) #define PHY_SUS_OVERRIDE BIT(8) +#define PHY_USB_DP_CONCURRENT_MODE BIT(9) /* * The following are bit fields describing the USB BAM options. @@ -135,7 +136,7 @@ int msm_data_fifo_config(struct usb_ep *ep, unsigned long addr, u32 size, u8 dst_pipe_idx); bool msm_dwc3_reset_ep_after_lpm(struct usb_gadget *gadget); int msm_dwc3_reset_dbm_ep(struct usb_ep *ep); -int dwc3_msm_release_ss_lane(struct device *dev); +int dwc3_msm_release_ss_lane(struct device *dev, bool usb_dp_concurrent_mode); bool usb_get_remote_wakeup_status(struct usb_gadget *gadget); #else static inline struct usb_ep *usb_ep_autoconfig_by_name( @@ -162,7 +163,7 @@ static inline bool msm_dwc3_reset_ep_after_lpm(struct usb_gadget *gadget) { return false; } static inline int msm_dwc3_reset_dbm_ep(struct usb_ep *ep) { return -ENODEV; } -static inline int dwc3_msm_release_ss_lane(struct device *dev) +static inline int dwc3_msm_release_ss_lane(struct device *dev, bool usb_dp_concurrent_mode) { return -ENODEV; } static bool __maybe_unused usb_get_remote_wakeup_status(struct usb_gadget *gadget) { return false; }