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

Commit 43a7caa2 authored by Hemant Kumar's avatar Hemant Kumar Committed by Devdutt Patnaik
Browse files

usb: dwc3: Add support to LPM L1 remote wakeup for ep0 endpoints



When device controller gets an LPM request from the host during a
control transfer, it is not able to initiate remote wakeup
automatically. As a result it accepts the request, goes to L1 state,
and does not initiate wakeup to exit L1 state. Since host expects
the device to do a remote wakeup from L1 state and the device does
not initiate the wakeup, the host continues to wait and starts
enumeration again because control transfer timeout occurs. Fix this
issue by initiating remote wakeup before queuing the ep0 request
if bus is in L1 suspend state. Also add a counter which gets
incremented upon device sending remote wakeup before queuing ep0
request.

Change-Id: I307ad94d4cb40ce2bd85425f3a1c6316cded52b8
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent c81ea141
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -984,6 +984,8 @@ struct dwc3 {
	unsigned                irq_event_count[MAX_INTR_STATS];
	unsigned                irq_dbg_index;

	unsigned long		l1_remote_wakeup_cnt;

	wait_queue_head_t	wait_linkstate;
	void			*dwc_ipc_log_ctxt;
};
+4 −1
Original line number Diff line number Diff line
@@ -1051,9 +1051,12 @@ static int dwc3_gadget_int_events_show(struct seq_file *s, void *unused)
		seq_printf(s, "%d\t", dwc->bh_completion_time[i]);
	seq_putc(s, '\n');

	seq_printf(s, "t_pwr evt irq : %lld\t",
	seq_printf(s, "t_pwr evt irq : %lld\n",
			ktime_to_us(dwc->t_pwr_evt_irq));

	seq_printf(s, "l1_remote_wakeup_cnt : %lu\n",
		dwc->l1_remote_wakeup_cnt);

	spin_unlock_irqrestore(&dwc->lock, flags);
	return 0;
}
+14 −0
Original line number Diff line number Diff line
@@ -224,6 +224,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
	unsigned long			flags;

	int				ret;
	enum dwc3_link_state		link_state;
	u32				reg;

	spin_lock_irqsave(&dwc->lock, flags);
	if (!dep->endpoint.desc) {
@@ -240,6 +242,18 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
		goto out;
	}

	/* if link stats is in L1 initiate  remote wakeup before queuing req */
	if (dwc->speed != DWC3_DSTS_SUPERSPEED) {
		link_state = dwc3_get_link_state(dwc);
		/* in HS this link state is same as L1 */
		if (link_state == DWC3_LINK_STATE_U2) {
			dwc->l1_remote_wakeup_cnt++;
			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
			reg |= DWC3_DCTL_ULSTCHNG_RECOVERY;
			dwc3_writel(dwc->regs, DWC3_DCTL, reg);
		}
	}

	dwc3_trace(trace_dwc3_ep0,
			"queueing request %pK to %s length %d state '%s'",
			request, dep->name, request->length,
+0 −7
Original line number Diff line number Diff line
@@ -1416,13 +1416,6 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
	return 0;
}

static inline enum dwc3_link_state dwc3_get_link_state(struct dwc3 *dwc)
{
	u32 reg;
	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
	return DWC3_DSTS_USBLNKST(reg);
}

static bool dwc3_gadget_is_suspended(struct dwc3 *dwc)
{
	if (atomic_read(&dwc->in_lpm) ||
+8 −0
Original line number Diff line number Diff line
@@ -84,6 +84,14 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
	list_move_tail(&req->list, &dep->req_queued);
}

static inline enum dwc3_link_state dwc3_get_link_state(struct dwc3 *dwc)
{
	u32 reg;

	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
	return DWC3_DSTS_USBLNKST(reg);
}

void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
		int status);