From 1837de6381c9fd30a01f01a4877f37cdc1caad09 Mon Sep 17 00:00:00 2001 From: Pratham Pratap Date: Wed, 12 Sep 2018 13:35:37 +0530 Subject: [PATCH] usb: phy-msm-ssusb-qmp: Perform USB only reset in USB+DP mode Consider a case where USB thumb drive and DP is connected to a multiport adaptor(MPA) and PIPO is performed. If USB3 controller stops responding to stop endpoint command during MPA disconnect driver will treat it as HC died and move into error path. Now let's say, while the driver was handling this next connect came due to which HC was not able to handle setup packets, which leads the driver to powercycle the port.This performs global phy reset of the USB_DP combo phy but DP driver is not notified of this reset which results in unclocked access of DP link registers. Fix this by replacing phy global reset with USB only reset to not affect DP PHY when USB is reset in DP+USB concurrent mode. Change-Id: I2acde04c99b84293644fdefdceee4bd00a964e7f Signed-off-by: Pratham Pratap --- drivers/usb/dwc3/dwc3-msm.c | 14 ++++++++- drivers/usb/phy/phy-msm-ssusb-qmp.c | 45 ++++++++++++++++++++++++----- include/linux/usb/dwc3-msm.h | 7 +++-- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index f3a5d746a25d..d2593d754da1 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; }