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

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

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

parents be29a2b2 d5b55602
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);

@@ -1673,20 +1673,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);
@@ -1695,19 +1689,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.
@@ -1733,9 +1728,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;
	}

@@ -1747,22 +1758,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;
	}

	/*
@@ -1773,8 +1801,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;
}

@@ -2197,6 +2234,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);
	dbg_event(0xFF, "GdgStrt End", 0);

@@ -2891,6 +2929,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)
@@ -2979,6 +3018,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)
@@ -3114,12 +3154,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 */
@@ -3229,8 +3280,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);
}

@@ -3322,7 +3374,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,