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

Commit 45b16e7b authored by Mayank Rana's avatar Mayank Rana Committed by Jack Pham
Browse files

usb: dwc3-msm: Use dedicated workqueue for resume_work



Currently resume_work() is scheduled on the system_wq workqueue,
however that may allow resume_work to get scheduled on multiple
CPUs simultaneously. This can be seen when multiple VBUS or ID
pin events are generated in rapid succession which can lead to
the input event bits getting set to an inconsistent state.
Alleviate this by creating a dedicated workqueue with the
WQ_ORDERED flag set, which will restrict resume_work() to only
run once at a time. Since it also flushes sm_work, that will
ensure that resume_work and sm_work are properly serialized.

Change-Id: I47e5167ee989b60e860391870e693ee631999809
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent 319472d7
Loading
Loading
Loading
Loading
+17 −7
Original line number Diff line number Diff line
@@ -201,6 +201,7 @@ struct dwc3_msm {
	struct delayed_work	resume_work;
	struct work_struct	restart_usb_work;
	bool			in_restart;
	struct workqueue_struct *dwc3_wq;
	struct delayed_work	sm_work;
	unsigned long		inputs;
	struct completion	dwc3_xcvr_vbus_init;
@@ -1706,7 +1707,8 @@ static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event)
		dev_dbg(mdwc->dev, "DWC3_CONTROLLER_NOTIFY_OTG_EVENT received\n");
		if (dwc->enable_bus_suspend) {
			mdwc->suspend = dwc->b_suspend;
			schedule_delayed_work(&mdwc->resume_work, 0);
			queue_delayed_work(mdwc->dwc3_wq,
					&mdwc->resume_work, 0);
		}
		break;
	case DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT:
@@ -2328,9 +2330,10 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy,
	case POWER_SUPPLY_PROP_USB_OTG:
		/* Let OTG know about ID detection */
		mdwc->id_state = val->intval ? DWC3_ID_GROUND : DWC3_ID_FLOAT;
		dbg_event(0xFF, "id_state", mdwc->id_state);
		if (dwc->is_drd)
			schedule_delayed_work(&mdwc->resume_work, 12);

			queue_delayed_work(mdwc->dwc3_wq,
					&mdwc->resume_work, 0);
		break;
	/* PMIC notification for DP_DM state */
	case POWER_SUPPLY_PROP_DP_DM:
@@ -2367,7 +2370,8 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy,
			 * charging CDP complaince test fails if delay > 120ms.
			 */
			dbg_event(0xFF, "Q RW (vbus)", val->intval);
			schedule_delayed_work(&mdwc->resume_work, 12);
			queue_delayed_work(mdwc->dwc3_wq,
					&mdwc->resume_work, 12);
		}
		break;
	case POWER_SUPPLY_PROP_ONLINE:
@@ -2616,6 +2620,12 @@ static int dwc3_msm_probe(struct platform_device *pdev)
	init_completion(&mdwc->dwc3_xcvr_vbus_init);
	INIT_DELAYED_WORK(&mdwc->sm_work, dwc3_otg_sm_work);

	mdwc->dwc3_wq = alloc_ordered_workqueue("dwc3_wq", 0);
	if (!mdwc->dwc3_wq) {
		pr_err("%s: Unable to create workqueue dwc3_wq\n", __func__);
		return -ENOMEM;
	}

	/* Get all clks and gdsc reference */
	ret = dwc3_msm_get_clk_gdsc(mdwc);
	if (ret) {
@@ -3627,7 +3637,7 @@ static int dwc3_msm_pm_suspend(struct device *dev)
	dev_dbg(dev, "dwc3-msm PM suspend\n");
	dbg_event(0xFF, "PM Sus", 0);

	flush_delayed_work(&mdwc->resume_work);
	flush_workqueue(mdwc->dwc3_wq);
	if (!atomic_read(&dwc->in_lpm)) {
		dev_err(mdwc->dev, "Abort PM suspend!! (USB is outside LPM)\n");
		return -EBUSY;
@@ -3649,11 +3659,11 @@ static int dwc3_msm_pm_resume(struct device *dev)
	dbg_event(0xFF, "PM Res", 0);

	/* flush to avoid race in read/write of pm_suspended */
	flush_delayed_work(&mdwc->resume_work);
	flush_workqueue(mdwc->dwc3_wq);
	atomic_set(&mdwc->pm_suspended, 0);

	/* kick in otg state machine */
	schedule_delayed_work(&mdwc->resume_work, 0);
	queue_delayed_work(mdwc->dwc3_wq, &mdwc->resume_work, 0);

	return 0;
}