Skip to content

Commit 5d69a3b

Browse files
Minas Harutyunyangregkh
authored andcommitted
usb: dwc2: gadget: LPM flow fix
Added functionality to exit from L1 state by device initiation using remote wakeup signaling, in case when function driver queuing request while core in L1 state. Fixes: 273d576 ("usb: dwc2: gadget: Add functionality to exit from LPM L1 state") Fixes: 88b02f2 ("usb: dwc2: Add core state checking") CC: stable@vger.kernel.org Signed-off-by: Minas Harutyunyan <Minas.Harutyunyan@synopsys.com> Link: https://lore.kernel.org/r/b4d9de5382375dddbf7ef6049d9a82066ad87d5d.1710166393.git.Minas.Harutyunyan@synopsys.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 31f42da commit 5d69a3b

3 files changed

Lines changed: 47 additions & 21 deletions

File tree

drivers/usb/dwc2/core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
13361336
int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg);
13371337

13381338
void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
1339+
void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup);
13391340

13401341
/* This function should be called on every hardware interrupt. */
13411342
irqreturn_t dwc2_handle_common_intr(int irq, void *dev);

drivers/usb/dwc2/core_intr.c

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,11 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
323323
* @hsotg: Programming view of DWC_otg controller
324324
*
325325
*/
326-
static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
326+
void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup)
327327
{
328328
u32 glpmcfg;
329-
u32 i = 0;
329+
u32 pcgctl;
330+
u32 dctl;
330331

331332
if (hsotg->lx_state != DWC2_L1) {
332333
dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n");
@@ -335,37 +336,57 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
335336

336337
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
337338
if (dwc2_is_device_mode(hsotg)) {
338-
dev_dbg(hsotg->dev, "Exit from L1 state\n");
339+
dev_dbg(hsotg->dev, "Exit from L1 state, remotewakeup=%d\n", remotewakeup);
339340
glpmcfg &= ~GLPMCFG_ENBLSLPM;
340-
glpmcfg &= ~GLPMCFG_HIRD_THRES_EN;
341+
glpmcfg &= ~GLPMCFG_HIRD_THRES_MASK;
341342
dwc2_writel(hsotg, glpmcfg, GLPMCFG);
342343

343-
do {
344-
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
344+
pcgctl = dwc2_readl(hsotg, PCGCTL);
345+
pcgctl &= ~PCGCTL_ENBL_SLEEP_GATING;
346+
dwc2_writel(hsotg, pcgctl, PCGCTL);
345347

346-
if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK |
347-
GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS)))
348-
break;
348+
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
349+
if (glpmcfg & GLPMCFG_ENBESL) {
350+
glpmcfg |= GLPMCFG_RSTRSLPSTS;
351+
dwc2_writel(hsotg, glpmcfg, GLPMCFG);
352+
}
353+
354+
if (remotewakeup) {
355+
if (dwc2_hsotg_wait_bit_set(hsotg, GLPMCFG, GLPMCFG_L1RESUMEOK, 1000)) {
356+
dev_warn(hsotg->dev, "%s: timeout GLPMCFG_L1RESUMEOK\n", __func__);
357+
goto fail;
358+
return;
359+
}
360+
361+
dctl = dwc2_readl(hsotg, DCTL);
362+
dctl |= DCTL_RMTWKUPSIG;
363+
dwc2_writel(hsotg, dctl, DCTL);
349364

350-
udelay(1);
351-
} while (++i < 200);
365+
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) {
366+
dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__);
367+
goto fail;
368+
return;
369+
}
370+
}
352371

353-
if (i == 200) {
354-
dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n");
372+
glpmcfg = dwc2_readl(hsotg, GLPMCFG);
373+
if (glpmcfg & GLPMCFG_COREL1RES_MASK || glpmcfg & GLPMCFG_SLPSTS ||
374+
glpmcfg & GLPMCFG_L1RESUMEOK) {
375+
goto fail;
355376
return;
356377
}
357-
dwc2_gadget_init_lpm(hsotg);
378+
379+
/* Inform gadget to exit from L1 */
380+
call_gadget(hsotg, resume);
381+
/* Change to L0 state */
382+
hsotg->lx_state = DWC2_L0;
383+
hsotg->bus_suspended = false;
384+
fail: dwc2_gadget_init_lpm(hsotg);
358385
} else {
359386
/* TODO */
360387
dev_err(hsotg->dev, "Host side LPM is not supported.\n");
361388
return;
362389
}
363-
364-
/* Change to L0 state */
365-
hsotg->lx_state = DWC2_L0;
366-
367-
/* Inform gadget to exit from L1 */
368-
call_gadget(hsotg, resume);
369390
}
370391

371392
/*
@@ -386,7 +407,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
386407
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
387408

388409
if (hsotg->lx_state == DWC2_L1) {
389-
dwc2_wakeup_from_lpm_l1(hsotg);
410+
dwc2_wakeup_from_lpm_l1(hsotg, false);
390411
return;
391412
}
392413

drivers/usb/dwc2/gadget.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,10 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
14151415
ep->name, req, req->length, req->buf, req->no_interrupt,
14161416
req->zero, req->short_not_ok);
14171417

1418+
if (hs->lx_state == DWC2_L1) {
1419+
dwc2_wakeup_from_lpm_l1(hs, true);
1420+
}
1421+
14181422
/* Prevent new request submission when controller is suspended */
14191423
if (hs->lx_state != DWC2_L0) {
14201424
dev_dbg(hs->dev, "%s: submit request only in active state\n",

0 commit comments

Comments
 (0)