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

Commit 4b2a7d4f authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: dwc3:Handle race between composition switch and cable disconnect"

parents 0667e599 3f162422
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -825,6 +825,13 @@ enum dwc3_link_state {
	DWC3_LINK_STATE_MASK		= 0x0f,
};

enum gadget_state {
	DWC3_GADGET_INACTIVE,
	DWC3_GADGET_SOFT_CONN,
	DWC3_GADGET_CABLE_CONN,
	DWC3_GADGET_ACTIVE,
};

/* TRB Length, PCM and Status */
#define DWC3_TRB_SIZE_MASK	(0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n)	((n) & DWC3_TRB_SIZE_MASK)
@@ -1343,6 +1350,7 @@ struct dwc3 {
	unsigned int		irq_event_count[MAX_INTR_STATS];
	unsigned int		irq_dbg_index;

	enum gadget_state	gadget_state;
	/* Indicate if the gadget was powered by the otg driver */
	unsigned int		vbus_active:1;
	/* Indicate if software connect was issued by the usb_gadget_driver */
+70 −15
Original line number Diff line number Diff line
@@ -2226,6 +2226,71 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
	return 0;
}

static int dwc3_gadget_run_stop_util(struct dwc3 *dwc)
{
	int ret = 0;

	dev_dbg(dwc->dev, "%s: enter: %d\n", __func__, dwc->gadget_state);
	switch (dwc->gadget_state) {
	case DWC3_GADGET_INACTIVE:
		if (dwc->vbus_active && dwc->softconnect) {
			ret = dwc3_gadget_run_stop(dwc, true, false);
			dwc->gadget_state = DWC3_GADGET_ACTIVE;
			break;
		}

		if (dwc->vbus_active) {
			dwc->gadget_state = DWC3_GADGET_CABLE_CONN;
			break;
		}

		if (dwc->softconnect) {
			dwc->gadget_state = DWC3_GADGET_SOFT_CONN;
			break;
		}
	case DWC3_GADGET_SOFT_CONN:
		if (!dwc->softconnect) {
			dwc->gadget_state = DWC3_GADGET_INACTIVE;
			break;
		}

		if (dwc->vbus_active) {
			ret = dwc3_gadget_run_stop(dwc, true, false);
			dwc->gadget_state = DWC3_GADGET_ACTIVE;
		}
		break;
	case DWC3_GADGET_CABLE_CONN:
		if (!dwc->vbus_active) {
			dwc->gadget_state = DWC3_GADGET_INACTIVE;
			break;
		}

		if (dwc->softconnect) {
			ret = dwc3_gadget_run_stop(dwc, true, false);
			dwc->gadget_state = DWC3_GADGET_ACTIVE;
		}
		break;
	case DWC3_GADGET_ACTIVE:
		if (!dwc->vbus_active) {
			dwc->gadget_state = DWC3_GADGET_SOFT_CONN;
			ret = dwc3_gadget_run_stop(dwc, false, false);
			break;
		}

		if (!dwc->softconnect) {
			dwc->gadget_state = DWC3_GADGET_CABLE_CONN;
			ret = dwc3_gadget_run_stop(dwc, false, false);
			break;
		}
		break;
	default:
		dev_err(dwc->dev, "Invalid state\n");
	}

	dev_dbg(dwc->dev, "%s: exit: %d\n", __func__, dwc->gadget_state);
	return ret;
}

static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
{
	struct dwc3		*dwc = gadget_to_dwc(g);
@@ -2245,6 +2310,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
	ktime_t			diff;

	is_on = !!is_on;
	spin_lock_irqsave(&dwc->lock, flags);
	dwc->softconnect = is_on;

	if ((dwc3_is_otg_or_drd(dwc) && !dwc->vbus_active)
@@ -2253,9 +2319,11 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
		 * Need to wait for vbus_session(on) from otg driver or to
		 * the udc_start.
		 */
		spin_unlock_irqrestore(&dwc->lock, flags);
		dbg_event(0xFF, "WaitPullup", 0);
		return 0;
	}
	spin_unlock_irqrestore(&dwc->lock, flags);

	pm_runtime_get_sync(dwc->dev);
	dbg_event(0xFF, "Pullup gsync",
@@ -2307,7 +2375,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
	dwc->b_suspend = false;
	dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);

	ret = dwc3_gadget_run_stop(dwc, is_on, false);
	ret = dwc3_gadget_run_stop_util(dwc);
	spin_unlock_irqrestore(&dwc->lock, flags);
	if (!is_on && ret == -ETIMEDOUT) {
		dev_err(dwc->dev, "%s: Core soft reset...\n", __func__);
@@ -2421,26 +2489,13 @@ static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
	/* Mark that the vbus was powered */
	dwc->vbus_active = is_active;

	/*
	 * Check if upper level usb_gadget_driver was already registered with
	 * this udc controller driver (if dwc3_gadget_start was called)
	 */
	if (dwc->gadget_driver && dwc->softconnect) {
		if (dwc->vbus_active) {
			/*
			 * Both vbus was activated by otg and pullup was
			 * signaled by the gadget driver.
			 */
			ret = dwc3_gadget_run_stop(dwc, 1, false);
		}
	}
	ret = dwc3_gadget_run_stop_util(dwc);

	/*
	 * Clearing run/stop bit might occur before disconnect event is seen.
	 * Make sure to let gadget driver know in that case.
	 */
	if (!dwc->vbus_active) {
		ret = dwc3_gadget_run_stop(dwc, 0, false);
		dev_dbg(dwc->dev, "calling disconnect from %s\n", __func__);
		dwc3_gadget_disconnect_interrupt(dwc);
	}