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

Commit 65c7182d authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: dwc3: gadget: allow running wakeup in delayed context"

parents 48d513a4 b04eb454
Loading
Loading
Loading
Loading
+76 −19
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <linux/io.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>

#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -60,6 +61,13 @@
static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend);
static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend);
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc);
static void _dwc3_gadget_wakeup(struct dwc3 *dwc);


struct dwc3_usb_gadget {
	struct work_struct wakeup_work;
	struct dwc3 *dwc;
};

/**
 * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
@@ -1317,6 +1325,33 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
	return 0;
}

static int dwc3_gadget_wakeup(struct usb_gadget *g)
{
	struct dwc3_usb_gadget *dwc3_gadget = g->private;
	struct dwc3 *dwc = dwc3_gadget->dwc;
	unsigned long		flags;
	spin_lock_irqsave(&dwc->lock, flags);

	if (atomic_read(&dwc->in_lpm)) {
		spin_unlock_irqrestore(&dwc->lock, flags);
		schedule_work(&dwc3_gadget->wakeup_work);
		return -EBUSY;
	} else {
		_dwc3_gadget_wakeup(dwc);
	}

	spin_unlock_irqrestore(&dwc->lock, flags);

	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 int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
	gfp_t gfp_flags)
{
@@ -1508,22 +1543,37 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
	return DWC3_DSTS_SOFFN(reg);
}

static int dwc3_gadget_wakeup(struct usb_gadget *g)
static void dwc3_gadget_wakeup_work(struct work_struct *w)
{
	struct dwc3		*dwc = gadget_to_dwc(g);

	unsigned long		timeout;
	struct dwc3_usb_gadget *dwc3_gadget;
	struct dwc3		*dwc;
	unsigned long		flags;
	bool			link_recover_only = false;

	u32			reg;
	dwc3_gadget = container_of(w, struct dwc3_usb_gadget, wakeup_work);
	dwc = dwc3_gadget->dwc;

	int			ret = 0;

	u8			link_state;
	spin_lock_irqsave(&dwc->lock, flags);

	if (atomic_read(&dwc->in_lpm)) {
		spin_unlock_irqrestore(&dwc->lock, flags);
		pm_runtime_get_sync(dwc->dev);
		spin_lock_irqsave(&dwc->lock, flags);
	}

	_dwc3_gadget_wakeup(dwc);
	spin_unlock_irqrestore(&dwc->lock, flags);
}


static void _dwc3_gadget_wakeup(struct dwc3 *dwc)
{
	u32			timeout = 0;
	bool			link_recover_only = false;

	u32			reg;
	int			ret = 0;
	u8			link_state;

	/*
	 * According to the Databook Remote wakeup request should
@@ -1531,9 +1581,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
	 *
	 * We can check that via USB Link State bits in DSTS register.
	 */
	reg = dwc3_readl(dwc->regs, DWC3_DSTS);

	link_state = DWC3_DSTS_USBLNKST(reg);
	link_state = dwc3_get_link_state(dwc);

	switch (link_state) {
	case DWC3_LINK_STATE_RX_DET:	/* in HS, means Early Suspend */
@@ -1567,17 +1615,16 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
	}

	/* poll until Link State changes to ON */
	timeout = jiffies + msecs_to_jiffies(100);

	spin_unlock_irqrestore(&dwc->lock, flags);
	while (!time_after(jiffies, timeout)) {
	do {
		reg = dwc3_readl(dwc->regs, DWC3_DSTS);

		/* in HS, means ON */
		if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
			break;
	}
	spin_lock_irqsave(&dwc->lock, flags);
		udelay(10);
		timeout++;
	} while (timeout < 10000);

	if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
		dev_err(dwc->dev, "failed to send remote wakeup\n");
@@ -1594,9 +1641,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
	if (!link_recover_only)
		dwc3_gadget_wakeup_interrupt(dwc);
out:
	spin_unlock_irqrestore(&dwc->lock, flags);

	return ret;
	return;
}

static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
@@ -3121,6 +3167,15 @@ int dwc3_gadget_init(struct dwc3 *dwc)
{
	u32					reg;
	int					ret;
	struct dwc3_usb_gadget *dwc3_gadget;

	dwc3_gadget = vzalloc(sizeof(*dwc3_gadget));
	if (!dwc3_gadget) {
		dev_err(dwc->dev, "failed to allocate dwc3_gadget\n");
		return -ENOMEM;
	}
	dwc3_gadget->dwc = dwc;
	INIT_WORK(&dwc3_gadget->wakeup_work, dwc3_gadget_wakeup_work);

	dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
			&dwc->ctrl_req_addr, GFP_KERNEL);
@@ -3163,6 +3218,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
	dwc->gadget.speed		= USB_SPEED_UNKNOWN;
	dwc->gadget.sg_supported	= true;
	dwc->gadget.name		= "dwc3-gadget";
	dwc->gadget.private		= dwc3_gadget;

	/*
	 * REVISIT: Here we should clear all pending IRQs to be
@@ -3229,6 +3285,7 @@ err1:
			dwc->ctrl_req, dwc->ctrl_req_addr);

err0:
	vfree(dwc3_gadget);
	return ret;
}

+1 −0
Original line number Diff line number Diff line
@@ -562,6 +562,7 @@ struct usb_gadget {
	u8                  usb_core_id;
	bool                streaming_enabled;
	bool                remote_wakeup;
	void				*private;
};
#define work_to_gadget(w)	(container_of((w), struct usb_gadget, work))