mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-14 00:24:41 -04:00
Merge "usb: dwc3: Handle race between composition switch and cable disconnect"
This commit is contained in:
commit
12af436ce6
2 changed files with 78 additions and 16 deletions
|
|
@ -850,6 +850,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)
|
||||
|
|
@ -1382,6 +1389,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 */
|
||||
|
|
|
|||
|
|
@ -2311,6 +2311,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);
|
||||
|
|
@ -2330,6 +2395,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 (((dwc->dr_mode > USB_DR_MODE_HOST) && !dwc->vbus_active)
|
||||
|
|
@ -2339,9 +2405,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",
|
||||
|
|
@ -2395,7 +2463,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) {
|
||||
/*
|
||||
|
|
@ -2516,21 +2584,7 @@ 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);
|
||||
} else {
|
||||
ret = dwc3_gadget_run_stop(dwc, 0, false);
|
||||
}
|
||||
}
|
||||
ret = dwc3_gadget_run_stop_util(dwc);
|
||||
|
||||
/*
|
||||
* Clearing run/stop bit might occur before disconnect event is seen.
|
||||
|
|
|
|||
Loading…
Reference in a new issue