Skip to content

Commit 2372f1c

Browse files
Prashanth Kgregkh
authored andcommitted
usb: dwc3: gadget: Make gadget_wakeup asynchronous
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 <stable@kernel.org> Fixes: 92c08a8 ("usb: dwc3: Add function suspend and function wakeup support") Signed-off-by: Prashanth K <prashanth.k@oss.qualcomm.com> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> Link: https://lore.kernel.org/r/20250422103231.1954387-4-prashanth.k@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 5977a58 commit 2372f1c

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
@@ -1164,6 +1164,9 @@ struct dwc3_scratchpad_array {
11641164
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
11651165
* DATWRREQINFO, and DESWRREQINFO value passed from
11661166
* glue driver.
1167+
* @wakeup_pending_funcs: Indicates whether any interface has requested for
1168+
* function wakeup in bitmap format where bit position
1169+
* represents interface_id.
11671170
*/
11681171
struct dwc3 {
11691172
struct work_struct drd_work;
@@ -1394,6 +1397,7 @@ struct dwc3 {
13941397
int num_ep_resized;
13951398
struct dentry *debug_root;
13961399
u32 gsbuscfg0_reqinfo;
1400+
u32 wakeup_pending_funcs;
13971401
};
13981402

13991403
#define INCRX_BURST_MODE 0

drivers/usb/dwc3/gadget.c

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

279-
static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
280-
281279
/**
282280
* dwc3_send_gadget_ep_cmd - issue an endpoint command
283281
* @dep: the endpoint to which the command is going to be issued
@@ -2359,10 +2357,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
23592357
return __dwc3_gadget_get_frame(dwc);
23602358
}
23612359

2362-
static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
2360+
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
23632361
{
2364-
int retries;
2365-
23662362
int ret;
23672363
u32 reg;
23682364

@@ -2390,8 +2386,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
23902386
return -EINVAL;
23912387
}
23922388

2393-
if (async)
2394-
dwc3_gadget_enable_linksts_evts(dwc, true);
2389+
dwc3_gadget_enable_linksts_evts(dwc, true);
23952390

23962391
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
23972392
if (ret < 0) {
@@ -2410,27 +2405,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
24102405

24112406
/*
24122407
* Since link status change events are enabled we will receive
2413-
* an U0 event when wakeup is successful. So bail out.
2408+
* an U0 event when wakeup is successful.
24142409
*/
2415-
if (async)
2416-
return 0;
2417-
2418-
/* poll until Link State changes to ON */
2419-
retries = 20000;
2420-
2421-
while (retries--) {
2422-
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
2423-
2424-
/* in HS, means ON */
2425-
if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
2426-
break;
2427-
}
2428-
2429-
if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
2430-
dev_err(dwc->dev, "failed to send remote wakeup\n");
2431-
return -EINVAL;
2432-
}
2433-
24342410
return 0;
24352411
}
24362412

@@ -2451,7 +2427,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
24512427
spin_unlock_irqrestore(&dwc->lock, flags);
24522428
return -EINVAL;
24532429
}
2454-
ret = __dwc3_gadget_wakeup(dwc, true);
2430+
ret = __dwc3_gadget_wakeup(dwc);
24552431

24562432
spin_unlock_irqrestore(&dwc->lock, flags);
24572433

@@ -2479,14 +2455,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
24792455
*/
24802456
link_state = dwc3_gadget_get_link_state(dwc);
24812457
if (link_state == DWC3_LINK_STATE_U3) {
2482-
ret = __dwc3_gadget_wakeup(dwc, false);
2483-
if (ret) {
2484-
spin_unlock_irqrestore(&dwc->lock, flags);
2485-
return -EINVAL;
2486-
}
2487-
dwc3_resume_gadget(dwc);
2488-
dwc->suspended = false;
2489-
dwc->link_state = DWC3_LINK_STATE_U0;
2458+
dwc->wakeup_pending_funcs |= BIT(intf_id);
2459+
ret = __dwc3_gadget_wakeup(dwc);
2460+
spin_unlock_irqrestore(&dwc->lock, flags);
2461+
return ret;
24902462
}
24912463

24922464
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
@@ -4353,6 +4325,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
43534325
{
43544326
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
43554327
unsigned int pwropt;
4328+
int ret;
4329+
int intf_id;
43564330

43574331
/*
43584332
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
@@ -4428,7 +4402,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
44284402

44294403
switch (next) {
44304404
case DWC3_LINK_STATE_U0:
4431-
if (dwc->gadget->wakeup_armed) {
4405+
if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
44324406
dwc3_gadget_enable_linksts_evts(dwc, false);
44334407
dwc3_resume_gadget(dwc);
44344408
dwc->suspended = false;
@@ -4451,6 +4425,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
44514425
}
44524426

44534427
dwc->link_state = next;
4428+
4429+
/* Proceed with func wakeup if any interfaces that has requested */
4430+
while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) {
4431+
intf_id = ffs(dwc->wakeup_pending_funcs) - 1;
4432+
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
4433+
DWC3_DGCMDPAR_DN_FUNC_WAKE |
4434+
DWC3_DGCMDPAR_INTF_SEL(intf_id));
4435+
if (ret)
4436+
dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id);
4437+
4438+
dwc->wakeup_pending_funcs &= ~BIT(intf_id);
4439+
}
44544440
}
44554441

44564442
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,

0 commit comments

Comments
 (0)