Skip to content

Commit 6462033

Browse files
Prashanth Kjfvogel
authored andcommitted
usb: dwc3: gadget: Make gadget_wakeup asynchronous
commit 2372f1c upstream. Currently gadget_wakeup() waits for U0 synchronously if it was called from func_wakeup(), this is because we need to send the function wakeup command soon after the link is active. And the call is made synchronous by polling DSTS continuosly for 20000 times in __dwc3_gadget_wakeup(). But it observed that sometimes the link is not active even after polling 20K times, leading to remote wakeup failures. Adding a small delay between each poll helps, but that won't guarantee resolution in future. Hence make the gadget_wakeup completely asynchronous. Since multiple interfaces can issue a function wakeup at once, add a new variable wakeup_pending_funcs which will indicate the functions that has issued func_wakup, this is represented in a bitmap format. If the link is in U3, dwc3_gadget_func_wakeup() will set the bit corresponding to interface_id and bail out. Once link comes back to U0, linksts_change irq is triggered, where the function wakeup command is sent based on bitmap. Cc: stable <[email protected]> Fixes: 92c08a8 ("usb: dwc3: Add function suspend and function wakeup support") Signed-off-by: Prashanth K <[email protected]> Acked-by: Thinh Nguyen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit f6fdbe4d543bade58278cd2f309b2e818142fafb) Signed-off-by: Jack Vogel <[email protected]>
1 parent 1de1358 commit 6462033

File tree

2 files changed

+27
-37
lines changed

2 files changed

+27
-37
lines changed

drivers/usb/dwc3/core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,9 @@ struct dwc3_scratchpad_array {
11681168
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
11691169
* DATWRREQINFO, and DESWRREQINFO value passed from
11701170
* glue driver.
1171+
* @wakeup_pending_funcs: Indicates whether any interface has requested for
1172+
* function wakeup in bitmap format where bit position
1173+
* represents interface_id.
11711174
*/
11721175
struct dwc3 {
11731176
struct work_struct drd_work;
@@ -1398,6 +1401,7 @@ struct dwc3 {
13981401
int num_ep_resized;
13991402
struct dentry *debug_root;
14001403
u32 gsbuscfg0_reqinfo;
1404+
u32 wakeup_pending_funcs;
14011405
};
14021406

14031407
#define INCRX_BURST_MODE 0

drivers/usb/dwc3/gadget.c

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,6 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
277277
return ret;
278278
}
279279

280-
static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
281-
282280
/**
283281
* dwc3_send_gadget_ep_cmd - issue an endpoint command
284282
* @dep: the endpoint to which the command is going to be issued
@@ -2348,10 +2346,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
23482346
return __dwc3_gadget_get_frame(dwc);
23492347
}
23502348

2351-
static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
2349+
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
23522350
{
2353-
int retries;
2354-
23552351
int ret;
23562352
u32 reg;
23572353

@@ -2379,8 +2375,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
23792375
return -EINVAL;
23802376
}
23812377

2382-
if (async)
2383-
dwc3_gadget_enable_linksts_evts(dwc, true);
2378+
dwc3_gadget_enable_linksts_evts(dwc, true);
23842379

23852380
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
23862381
if (ret < 0) {
@@ -2399,27 +2394,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
23992394

24002395
/*
24012396
* Since link status change events are enabled we will receive
2402-
* an U0 event when wakeup is successful. So bail out.
2397+
* an U0 event when wakeup is successful.
24032398
*/
2404-
if (async)
2405-
return 0;
2406-
2407-
/* poll until Link State changes to ON */
2408-
retries = 20000;
2409-
2410-
while (retries--) {
2411-
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
2412-
2413-
/* in HS, means ON */
2414-
if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
2415-
break;
2416-
}
2417-
2418-
if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
2419-
dev_err(dwc->dev, "failed to send remote wakeup\n");
2420-
return -EINVAL;
2421-
}
2422-
24232399
return 0;
24242400
}
24252401

@@ -2440,7 +2416,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
24402416
spin_unlock_irqrestore(&dwc->lock, flags);
24412417
return -EINVAL;
24422418
}
2443-
ret = __dwc3_gadget_wakeup(dwc, true);
2419+
ret = __dwc3_gadget_wakeup(dwc);
24442420

24452421
spin_unlock_irqrestore(&dwc->lock, flags);
24462422

@@ -2468,14 +2444,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
24682444
*/
24692445
link_state = dwc3_gadget_get_link_state(dwc);
24702446
if (link_state == DWC3_LINK_STATE_U3) {
2471-
ret = __dwc3_gadget_wakeup(dwc, false);
2472-
if (ret) {
2473-
spin_unlock_irqrestore(&dwc->lock, flags);
2474-
return -EINVAL;
2475-
}
2476-
dwc3_resume_gadget(dwc);
2477-
dwc->suspended = false;
2478-
dwc->link_state = DWC3_LINK_STATE_U0;
2447+
dwc->wakeup_pending_funcs |= BIT(intf_id);
2448+
ret = __dwc3_gadget_wakeup(dwc);
2449+
spin_unlock_irqrestore(&dwc->lock, flags);
2450+
return ret;
24792451
}
24802452

24812453
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
@@ -4320,6 +4292,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
43204292
{
43214293
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
43224294
unsigned int pwropt;
4295+
int ret;
4296+
int intf_id;
43234297

43244298
/*
43254299
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
@@ -4395,7 +4369,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
43954369

43964370
switch (next) {
43974371
case DWC3_LINK_STATE_U0:
4398-
if (dwc->gadget->wakeup_armed) {
4372+
if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
43994373
dwc3_gadget_enable_linksts_evts(dwc, false);
44004374
dwc3_resume_gadget(dwc);
44014375
dwc->suspended = false;
@@ -4418,6 +4392,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
44184392
}
44194393

44204394
dwc->link_state = next;
4395+
4396+
/* Proceed with func wakeup if any interfaces that has requested */
4397+
while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) {
4398+
intf_id = ffs(dwc->wakeup_pending_funcs) - 1;
4399+
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
4400+
DWC3_DGCMDPAR_DN_FUNC_WAKE |
4401+
DWC3_DGCMDPAR_INTF_SEL(intf_id));
4402+
if (ret)
4403+
dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id);
4404+
4405+
dwc->wakeup_pending_funcs &= ~BIT(intf_id);
4406+
}
44214407
}
44224408

44234409
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,

0 commit comments

Comments
 (0)