dwc3-msm-core: Use refcount to handle USB DP concurrency case

DP driver uses altmode glink interface whereas USB driver uses
UCSI interface for getting PPM based DP and USB related notification.
PPM based notification can come in different order specifically when
PPM is being restarted with SSR. With PPM SSR (here ADSP SSR) DP driver
is getting disconnect notification but USB driver won't be getting
disconnect notification. Once ADSP SSR is being completed, DP does go
through re-initialization and it does need USB DP combo PHY as
concurrent mode (2 lane USB and 2 lane DP usage). Current code goes
ahead disabling USB host mode with DP not connected notification.

In some case DP notification is coming after ADSP SSR is being completed
but before USB notification has gone through. This results into DP
initiailzation failure (aux timeout) due to USB host mode not started
configuring USB DP combo PHY into concurrent mode.

Consider case: USB DP concurrent mode usage with connecting MPA and
performing ADSP SSR

a. USB and DP are initialized (concurrent mode)
b. ADSP SSR is being performed
c. USB gets usb role as UFP/host mode
d. DP notifies as DP not connected
e. DP notified as 2 lane usage

Current code tears down USB host mode functionality with DP not connected
notification. If above step c notification comes after step d, then step e
based functionality goes through as USB host mode is active. Due to
asynchronous nature of notification, usage of multiple interfaces (altmode
link and UCSI) and ADSP doesn't notify USB disconnect with SSR (due to USB
audio use case requirement) whereas notify disconnect with DP. It is needed
to use refcounting mechnism to decide when to tear down USB host session to
allow sharing of USB DP combo PHY. This change adds ref count usage to
address above issue.

Change-Id: I0ea80611e52c1e45fed50cde61741f7a9aedd87d
Signed-off-by: Mayank Rana <quic_mrana@quicinc.com>
This commit is contained in:
Mayank Rana 2022-06-02 11:26:25 -07:00 committed by Gerrit - the friendly Code Review server
parent e36999b737
commit 97bd9f3aab

View File

@ -348,6 +348,12 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
enum dp_lane {
DP_NONE = 0,
DP_2_LANE = 2,
DP_4_LANE = 4,
};
static const char *dwc3_drd_state_string(enum dwc3_drd_state state)
{
if (state < 0 || state >= ARRAY_SIZE(state_names))
@ -589,6 +595,8 @@ struct dwc3_msm {
bool force_gen1;
bool cached_dis_u1_entry_quirk;
bool cached_dis_u2_entry_quirk;
int refcnt_dp_usb;
enum dp_lane dp_state;
};
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
@ -4845,8 +4853,8 @@ static int dwc3_msm_set_role(struct dwc3_msm *mdwc, enum usb_role role)
mutex_lock(&mdwc->role_switch_mutex);
cur_role = dwc3_msm_get_role(mdwc);
dbg_log_string("cur_role:%s new_role:%s\n", dwc3_msm_usb_role_string(cur_role),
dwc3_msm_usb_role_string(role));
dbg_log_string("cur_role:%s new_role:%s refcnt:%d\n", dwc3_msm_usb_role_string(cur_role),
dwc3_msm_usb_role_string(role), mdwc->refcnt_dp_usb);
/*
* For boot up without USB cable connected case, don't check
@ -4863,18 +4871,33 @@ static int dwc3_msm_set_role(struct dwc3_msm *mdwc, enum usb_role role)
case USB_ROLE_HOST:
mdwc->vbus_active = false;
mdwc->id_state = DWC3_ID_GROUND;
WARN_ON(mdwc->refcnt_dp_usb);
mdwc->refcnt_dp_usb++;
break;
case USB_ROLE_DEVICE:
mdwc->vbus_active = true;
mdwc->id_state = DWC3_ID_FLOAT;
dbg_log_string("refcnt:%d reset refcnt_dp_usb\n", mdwc->refcnt_dp_usb);
mdwc->refcnt_dp_usb = 0;
break;
case USB_ROLE_NONE:
if (mdwc->dp_state != DP_NONE) {
mdwc->refcnt_dp_usb--;
dbg_log_string("DP (%d)session active, refcnt:%d\n",
mdwc->dp_state, mdwc->refcnt_dp_usb);
mutex_unlock(&mdwc->role_switch_mutex);
return 0;
}
mdwc->vbus_active = false;
mdwc->id_state = DWC3_ID_FLOAT;
mdwc->refcnt_dp_usb = 0;
break;
}
dbg_log_string("new_role:%s refcnt:%d\n",
dwc3_msm_usb_role_string(role), mdwc->refcnt_dp_usb);
mutex_unlock(&mdwc->role_switch_mutex);
dwc3_ext_event_notify(mdwc);
@ -5193,6 +5216,7 @@ static int dwc3_start_stop_device(struct dwc3_msm *mdwc, bool start)
static void dwc3_msm_clear_dp_only_params(struct dwc3_msm *mdwc)
{
dbg_log_string("resetting params for USB ss\n");
mdwc->ss_release_called = false;
mdwc->ss_phy->flags &= ~PHY_DP_MODE;
dwc3_msm_set_max_speed(mdwc, USB_SPEED_UNKNOWN);
@ -5222,9 +5246,17 @@ int dwc3_msm_set_dp_mode(struct device *dev, bool dp_connected, int lanes)
flush_work(&mdwc->resume_work);
flush_workqueue(mdwc->sm_usb_wq);
if (!dp_connected) {
dbg_event(0xFF, "DP not connected", 0);
dbg_log_string("DP: cur_state:%d new_state:%d lanes:%d\n",
mdwc->dp_state, dp_connected, lanes);
if (dp_connected && (lanes == mdwc->dp_state)) {
dbg_log_string("DP lane already configured\n");
return 0;
}
if (!dp_connected) {
dbg_log_string("DP not connected, refcnt:%d\n", mdwc->refcnt_dp_usb);
mutex_lock(&mdwc->role_switch_mutex);
mdwc->ss_release_called = false;
/*
* Special case for HOST mode, as we need to ensure that the DWC3
@ -5232,19 +5264,27 @@ int dwc3_msm_set_dp_mode(struct device *dev, bool dp_connected, int lanes)
* This is because, dwc3_gadget_init() will set the max speed
* for the USB gadget driver.
*/
if (mdwc->drd_state == DRD_STATE_HOST)
dwc3_start_stop_host(mdwc, false);
else
mdwc->refcnt_dp_usb--;
mdwc->dp_state = DP_NONE;
if (mdwc->drd_state == DRD_STATE_HOST) {
if (!mdwc->refcnt_dp_usb)
dwc3_start_stop_host(mdwc, false);
} else {
dwc3_msm_clear_dp_only_params(mdwc);
}
mdwc->ss_phy->flags &= ~PHY_USB_DP_CONCURRENT_MODE;
mutex_unlock(&mdwc->role_switch_mutex);
return 0;
}
dbg_event(0xFF, "Set DP mode", lanes);
dbg_log_string("Set DP lanes:%d refcnt:%d\n", lanes, mdwc->refcnt_dp_usb);
if (lanes == 2) {
mutex_lock(&mdwc->role_switch_mutex);
mdwc->dp_state = DP_2_LANE;
mdwc->refcnt_dp_usb++;
mutex_unlock(&mdwc->role_switch_mutex);
usb_redriver_release_lanes(mdwc->redriver, 2);
pm_runtime_get_sync(&mdwc->dwc3->dev);
mdwc->ss_phy->flags |= PHY_USB_DP_CONCURRENT_MODE;
@ -5257,6 +5297,11 @@ int dwc3_msm_set_dp_mode(struct device *dev, bool dp_connected, int lanes)
flush_workqueue(mdwc->sm_usb_wq);
mutex_lock(&mdwc->role_switch_mutex);
/* 4 lanes handling */
if (mdwc->dp_state != DP_2_LANE)
mdwc->refcnt_dp_usb++;
mdwc->dp_state = DP_4_LANE;
if (mdwc->id_state == DWC3_ID_GROUND) {
/* stop USB host mode */
ret = dwc3_start_stop_host(mdwc, false);
@ -5979,6 +6024,7 @@ static int dwc3_msm_host_ss_powerup(struct dwc3_msm *mdwc)
{
u32 reg;
dbg_log_string("start: speed:%d\n", dwc3_msm_get_max_speed(mdwc));
if (mdwc->disable_host_ssphy_powerdown ||
dwc3_msm_get_max_speed(mdwc) < USB_SPEED_SUPER)
return 0;
@ -6117,7 +6163,7 @@ 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;
dbg_event(0xFF, "hs_phy_flag:%x", mdwc->hs_phy->flags);
dbg_event(0xFF, "hs_phy_flag:", mdwc->hs_phy->flags);
ret = pm_runtime_resume_and_get(mdwc->dev);
if (ret < 0) {
dev_err(mdwc->dev, "%s: pm_runtime_resume_and_get failed\n", __func__);