usb: dwc3: Ensure blocking_sync waits until host mode starts or stops

Currently flushwork and drain_workqueue of resume work and sm_work
queue is taking care of blocking until host mode starts or stops.
Flush work only flushes the work which is currently being executed,
it cannot be guaranteed that entire dwc3_wq will be flushed by
flush_work, so when sm_work_queue is being drained there is still
a chance that resume_work queues work to sm_work_queue which is not
allowed and leads to dr swap failure.

So based on the event from policy engine whether it is start host or
stop host ensure that blocking sync blocks until the event is
completely executed.

Change-Id: Ice5f5e8e2a39fc444cdec63475a737a44be48619
Signed-off-by: Rohith Kollalsi <rkollals@codeaurora.org>
This commit is contained in:
Rohith Kollalsi 2020-11-09 22:47:57 +05:30
parent 123887723b
commit 7bb30153e7
2 changed files with 29 additions and 32 deletions

View File

@ -48,6 +48,7 @@
#include "xhci.h"
#define SDP_CONNETION_CHECK_TIME 10000 /* in ms */
#define EXTCON_SYNC_EVENT_TIMEOUT_MS 1500 /* in ms */
/* time out to wait for USB cable status notification (in ms)*/
#define SM_INIT_TIMEOUT 30000
@ -4424,47 +4425,40 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
return 0;
}
/**
* dwc3_usb_blocking_sync - Waits until event is completed or maximum upto 1.5
* secs.
*
* @host_enable_event: Event can be start usb host or stop usb host.
*/
static int dwc3_usb_blocking_sync(struct notifier_block *nb,
unsigned long event, void *ptr)
unsigned long host_enable_event, void *ptr)
{
struct dwc3 *dwc;
struct extcon_dev *edev = ptr;
struct extcon_nb *enb = container_of(nb, struct extcon_nb,
blocking_sync_nb);
struct dwc3_msm *mdwc = enb->mdwc;
int ret = 0;
unsigned long timeout_ms = jiffies +
msecs_to_jiffies(EXTCON_SYNC_EVENT_TIMEOUT_MS);
if (!edev || !mdwc)
return NOTIFY_DONE;
dwc = platform_get_drvdata(mdwc->dwc3);
dbg_event(0xFF, "fw_blocksync", 0);
flush_work(&mdwc->resume_work);
drain_workqueue(mdwc->sm_usb_wq);
if (!mdwc->in_host_mode && !mdwc->in_device_mode) {
dbg_event(0xFF, "lpm_state", atomic_read(&dwc->in_lpm));
do {
if (mdwc->drd_state == (host_enable_event ? DRD_STATE_HOST
: DRD_STATE_IDLE))
break;
msleep(50);
} while (time_before(jiffies, timeout_ms));
/*
* stop host mode functionality performs autosuspend with mdwc
* device, and it may take sometime to call PM runtime suspend.
* Hence call pm_runtime_suspend() API to invoke PM runtime
* suspend immediately to put USB controller and PHYs into
* suspend.
*/
ret = pm_runtime_suspend(mdwc->dev);
dbg_event(0xFF, "pm_runtime_sus", ret);
if (!time_before(jiffies, timeout_ms))
dev_err(mdwc->dev, "TIMEOUT when changing the state\n");
/*
* If mdwc device is already suspended, pm_runtime_suspend() API
* returns 1, which is not error. Overwrite with zero if it is.
*/
if (ret > 0)
ret = 0;
}
return ret;
return 0;
}
static int get_psy_type(struct dwc3_msm *mdwc)
@ -4684,8 +4678,6 @@ static void dwc3_otg_sm_work(struct work_struct *w)
mdwc->vbus_retry_count = 0;
work = 1;
} else {
mdwc->drd_state = DRD_STATE_HOST;
ret = dwc3_otg_start_host(mdwc, 1);
if ((ret == -EPROBE_DEFER) &&
mdwc->vbus_retry_count < 3) {
@ -4693,15 +4685,15 @@ static void dwc3_otg_sm_work(struct work_struct *w)
* Get regulator failed as regulator driver is
* not up yet. Will try to start host after 1sec
*/
mdwc->drd_state = DRD_STATE_HOST_IDLE;
dev_dbg(mdwc->dev, "Unable to get vbus regulator. Retrying...\n");
delay = VBUS_REG_CHECK_DELAY;
work = 1;
mdwc->vbus_retry_count++;
} else if (ret) {
dev_err(mdwc->dev, "unable to start host\n");
mdwc->drd_state = DRD_STATE_HOST_IDLE;
goto ret;
} else {
mdwc->drd_state = DRD_STATE_HOST;
}
}
break;

View File

@ -347,6 +347,10 @@ static void *usbpd_ipc_log;
#define ID_HDR_PRODUCT_AMA 5
#define ID_HDR_PRODUCT_VPD 6
/* params for usb_blocking_sync */
#define STOP_USB_HOST 0
#define START_USB_HOST 1
#define PD_MIN_SINK_CURRENT 900
static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */
@ -552,7 +556,7 @@ static inline void start_usb_host(struct usbpd *pd, bool ss)
extcon_set_state_sync(pd->extcon, EXTCON_USB_HOST, 1);
/* blocks until USB host is completely started */
ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, 1);
ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, START_USB_HOST);
if (ret) {
usbpd_err(&pd->dev, "err(%d) starting host", ret);
return;
@ -636,7 +640,7 @@ static int usbpd_release_ss_lane(struct usbpd *pd,
stop_usb_host(pd);
/* blocks until USB host is completely stopped */
ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, 0);
ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, STOP_USB_HOST);
if (ret) {
usbpd_err(&pd->dev, "err(%d) stopping host", ret);
goto err_exit;
@ -1905,7 +1909,8 @@ static void dr_swap(struct usbpd *pd)
typec_set_data_role(pd->typec_port, TYPEC_HOST);
/* ensure host is started before allowing DP */
extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, 0);
extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST,
START_USB_HOST);
usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_IDENTITY,
SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);