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

Commit d5b55602 authored by Mayank Rana's avatar Mayank Rana
Browse files

dwc3: gadget: Replace polling mechnism to go into U0 state



Moving into U0 state is being confirmed by polling for 100ms after
performing remote wakeup from device. In some of cases where host is
taking more time to respond, remote wakeup is failing. Also USB
specification does not define any limit for the host response time.
Hence this change replaces polling mechnism by using LINK status
change event notification with core and increase host response time
from 100ms to 3 seconds. It also makes sure that composite_resume()
is being called after remote wakeup is completed succesfully.

It removes some of flag used to avoid race between bus suspend/resume
and fuction suspend/resume as those are serialize and not required
anymore.

CRs-Fixed: 712681
Change-Id: I71285daf117282c738e139e9a05ead6ef16dd202
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent 512860c2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <linux/mm.h>
#include <linux/debugfs.h>
#include <linux/hrtimer.h>
#include <linux/wait.h>

#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -799,6 +800,7 @@ struct dwc3_scratchpad_array {
 * @bh_completion_time: time taken for taklet completion
 * @bh_handled_evt_cnt: no. of events handled by tasklet per interrupt
 * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
 * @wait_linkstate: waitqueue for waiting LINK to move into required state
 */
struct dwc3 {
	struct usb_ctrlrequest	*ctrl_req;
@@ -926,6 +928,7 @@ struct dwc3 {
	unsigned                irq_completion_time[MAX_INTR_STATS];
	unsigned                irq_event_count[MAX_INTR_STATS];
	unsigned                irq_dbg_index;
	wait_queue_head_t	wait_linkstate;
};

/* -------------------------------------------------------------------------- */
+80 −28
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ static int bulk_ep_xfer_timeout_ms;
module_param(bulk_ep_xfer_timeout_ms, int, S_IRUGO | S_IWUSR);

#define bulk_ep_xfer_timeout_ns (bulk_ep_xfer_timeout_ms * NSEC_PER_MSEC)
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc);
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup);
static int dwc3_gadget_wakeup_int(struct dwc3 *dwc);
static void dwc3_restart_hrtimer(struct dwc3 *dwc);

@@ -1672,20 +1672,14 @@ static void dwc3_gadget_wakeup_work(struct work_struct *w)
{
	struct dwc3_usb_gadget *dwc3_gadget;
	struct dwc3		*dwc;
	unsigned long		flags;
	int			ret;

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


	spin_lock_irqsave(&dwc->lock, flags);

	if (atomic_read(&dwc->in_lpm)) {
		spin_unlock_irqrestore(&dwc->lock, flags);
		dbg_event(0xFF, "Gdgwake gsyn", 0);
		pm_runtime_get_sync(dwc->dev);
		spin_lock_irqsave(&dwc->lock, flags);
	}

	ret = dwc3_gadget_wakeup_int(dwc);
@@ -1694,19 +1688,20 @@ static void dwc3_gadget_wakeup_work(struct work_struct *w)
		pr_err("Remote wakeup failed. ret = %d.\n", ret);
	else
		pr_debug("Remote wakeup succeeded.\n");

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

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

	u32			reg;
	int			ret = 0;
	u8			link_state;
	unsigned long		flags;

	pr_debug("%s(): Entry\n", __func__);
	disable_irq(dwc->irq);
	spin_lock_irqsave(&dwc->lock, flags);
	/*
	 * According to the Databook Remote wakeup request should
	 * be issued only when the device is in early suspend state.
@@ -1732,9 +1727,25 @@ static int dwc3_gadget_wakeup_int(struct dwc3 *dwc)
		goto out;
	}

	/* Enable LINK STATUS change event */
	reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
	reg |= DWC3_DEVTEN_ULSTCNGEN;
	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
	/*
	 * memory barrier is required to make sure that required events
	 * with core is enabled before performing RECOVERY mechnism.
	 */
	mb();

	ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
	if (ret < 0) {
		dev_err(dwc->dev, "failed to put link in Recovery\n");
		/* Disable LINK STATUS change */
		reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
		reg &= ~DWC3_DEVTEN_ULSTCNGEN;
		dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
		/* Required to complete this operation before returning */
		mb();
		goto out;
	}

@@ -1746,22 +1757,39 @@ static int dwc3_gadget_wakeup_int(struct dwc3 *dwc)
		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
	}

	/* poll until Link State changes to ON */

	do {
		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
	spin_unlock_irqrestore(&dwc->lock, flags);
	enable_irq(dwc->irq);
	ret = wait_event_interruptible_timeout(dwc->wait_linkstate,
			(dwc->link_state < DWC3_LINK_STATE_U3) ||
			(dwc->link_state == DWC3_LINK_STATE_SS_DIS),
			msecs_to_jiffies(3000)); /* 3 seconds */

		/* in HS, means ON */
		if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
			break;
		udelay(10);
		timeout++;
	} while (timeout < 10000);
	spin_lock_irqsave(&dwc->lock, flags);
	/* Disable link status change event */
	reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
	reg &= ~DWC3_DEVTEN_ULSTCNGEN;
	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
	/*
	 * Complete this write before we go ahead and perform resume
	 * as we don't need link status change notificaiton anymore.
	 */
	mb();

	if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
		dev_err(dwc->dev, "failed to send remote wakeup\n");
	if (!ret) {
		dev_dbg(dwc->dev, "Timeout moving into state(%d)\n",
							dwc->link_state);
		ret = -EINVAL;
		goto out;
		spin_unlock_irqrestore(&dwc->lock, flags);
		goto out1;
	} else {
		ret = 0;
		/*
		 * If USB is disconnected OR received RESET from host,
		 * don't perform resume
		 */
		if (dwc->link_state == DWC3_LINK_STATE_SS_DIS ||
				dwc->gadget.state == USB_STATE_DEFAULT)
			link_recover_only = true;
	}

	/*
@@ -1772,8 +1800,17 @@ static int dwc3_gadget_wakeup_int(struct dwc3 *dwc)
	 * the resume sequence is initiated by SW.
	 */
	if (!link_recover_only)
		dwc3_gadget_wakeup_interrupt(dwc);
		dwc3_gadget_wakeup_interrupt(dwc, true);

	spin_unlock_irqrestore(&dwc->lock, flags);
	pr_debug("%s: Exit\n", __func__);
	return ret;

out:
	spin_unlock_irqrestore(&dwc->lock, flags);
	enable_irq(dwc->irq);

out1:
	return ret;
}

@@ -2195,6 +2232,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
	}

	spin_unlock_irqrestore(&dwc->lock, flags);
	init_waitqueue_head(&dwc->wait_linkstate);
	pm_runtime_put(dwc->dev);

	return 0;
@@ -2888,6 +2926,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
	dwc->gadget.speed = USB_SPEED_UNKNOWN;
	dwc->setup_packet_pending = false;
	dwc->link_state = DWC3_LINK_STATE_SS_DIS;
	wake_up_interruptible(&dwc->wait_linkstate);
}

void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
@@ -2976,6 +3015,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
	dwc->gadget.speed = USB_SPEED_UNKNOWN;
	dwc->link_state = DWC3_LINK_STATE_U0;
	wake_up_interruptible(&dwc->wait_linkstate);
}

static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
@@ -3111,12 +3151,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
	 */
}

static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup)
{
	bool perform_resume = true;

	dev_dbg(dwc->dev, "%s\n", __func__);

	/*
	 * Identify if it is called from wakeup_interrupt() context for bus
	 * resume or as part of remote wakeup. And based on that check for
	 * U3 state. as we need to handle case of L1 resume i.e. where we
	 * don't want to perform resume.
	 */
	if (!remote_wakeup && dwc->link_state != DWC3_LINK_STATE_U3)
		perform_resume = false;

	/* Only perform resume from L2 or Early Suspend states */
	if (dwc->link_state == DWC3_LINK_STATE_U3) {
	if (perform_resume) {
		dbg_event(0xFF, "WAKEUP", 0);

		/* Clear OTG suspend state */
@@ -3218,8 +3269,9 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
		}
	}

	dev_dbg(dwc->dev, "Going from (%d)--->(%d)\n", dwc->link_state, next);
	dwc->link_state = next;

	wake_up_interruptible(&dwc->wait_linkstate);
	dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
}

@@ -3311,7 +3363,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
		dwc->dbg_gadget_events.connect++;
		break;
	case DWC3_DEVICE_EVENT_WAKEUP:
		dwc3_gadget_wakeup_interrupt(dwc);
		dwc3_gadget_wakeup_interrupt(dwc, false);
		dwc->dbg_gadget_events.wakeup++;
		break;
	case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+6 −21
Original line number Diff line number Diff line
@@ -363,8 +363,7 @@ int usb_get_func_interface_id(struct usb_function *func)
	return -ENODEV;
}

static int usb_func_wakeup_int(struct usb_function *func,
					bool use_pending_flag)
static int usb_func_wakeup_int(struct usb_function *func)
{
	int ret;
	int interface_id;
@@ -372,13 +371,14 @@ static int usb_func_wakeup_int(struct usb_function *func,
	struct usb_gadget *gadget;
	struct usb_composite_dev *cdev;

	pr_debug("%s - %s function wakeup, use pending: %u\n",
		__func__, func->name ? func->name : "", use_pending_flag);

	if (!func || !func->config || !func->config->cdev ||
		!func->config->cdev->gadget)
		return -EINVAL;

	pr_debug("%s - %s function wakeup\n", __func__,
					func->name ? func->name : "");

	gadget = func->config->cdev->gadget;
	if ((gadget->speed != USB_SPEED_SUPER) || !func->func_wakeup_allowed) {
		DBG(func->config->cdev,
@@ -391,13 +391,6 @@ static int usb_func_wakeup_int(struct usb_function *func,

	cdev = get_gadget_data(gadget);
	spin_lock_irqsave(&cdev->lock, flags);

	if (use_pending_flag && !func->func_wakeup_pending) {
		pr_debug("Pending flag is cleared - Function wakeup is cancelled.\n");
		spin_unlock_irqrestore(&cdev->lock, flags);
		return 0;
	}

	ret = usb_get_func_interface_id(func);
	if (ret < 0) {
		ERROR(func->config->cdev,
@@ -410,14 +403,6 @@ static int usb_func_wakeup_int(struct usb_function *func,

	interface_id = ret;
	ret = usb_gadget_func_wakeup(gadget, interface_id);

	if (use_pending_flag) {
		func->func_wakeup_pending = false;
	} else {
		if (ret == -EAGAIN)
			func->func_wakeup_pending = true;
	}

	spin_unlock_irqrestore(&cdev->lock, flags);

	return ret;
@@ -430,7 +415,7 @@ int usb_func_wakeup(struct usb_function *func)
	pr_debug("%s function wakeup\n",
		func->name ? func->name : "");

	ret = usb_func_wakeup_int(func, false);
	ret = usb_func_wakeup_int(func);
	if (ret == -EAGAIN) {
		DBG(func->config->cdev,
			"Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n",
@@ -1914,7 +1899,7 @@ composite_resume(struct usb_gadget *gadget)

	if (cdev->config) {
		list_for_each_entry(f, &cdev->config->functions, list) {
			ret = usb_func_wakeup_int(f, true);
			ret = usb_func_wakeup_int(f);
			if (ret) {
				if (ret == -EAGAIN) {
					ERROR(f->config->cdev,