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

Commit f291b2fb authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "defconfig: sdxlemur: Enable USB function remote wakeup functionality"

parents c6bb501a 0ca0b8da
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ CONFIG_QTI_TZ_LOG=y
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_QCOM_SOCINFO=y
CONFIG_USB_DWC3=y
CONFIG_USB_FUNC_WAKEUP_SUPPORTED=y
CONFIG_USB_DWC3_MSM=y
CONFIG_USB_QCOM_EMU_PHY=y
CONFIG_USB_MSM_SSPHY_QMP=y
+1 −0
Original line number Diff line number Diff line
@@ -1622,6 +1622,7 @@ static int dwc3_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, dwc);

	init_waitqueue_head(&dwc->wait_linkstate);
	spin_lock_init(&dwc->lock);

	pm_runtime_no_callbacks(dev);
+4 −0
Original line number Diff line number Diff line
@@ -1151,6 +1151,8 @@ struct dwc3_scratchpad_array {
 * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
 * @last_run_stop: timestamp denoting the last run_stop update
 * @is_remote_wakeup_enabled: remote wakeup status from host perspective
 * @wait_linkstate: waitqueue for waiting LINK to move into required state
 * @remote_wakeup_work: use to perform remote wakeup from this context
 */
struct dwc3 {
	struct work_struct	drd_work;
@@ -1397,6 +1399,8 @@ struct dwc3 {
	u32			gen2_tx_de_emph3;
	ktime_t			last_run_stop;
	bool			is_remote_wakeup_enabled;
	wait_queue_head_t	wait_linkstate;
	struct work_struct	remote_wakeup_work;
};

#define INCRX_BURST_MODE 0
+6 −1
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
		struct dwc3_ep *dep, struct dwc3_request *req);
static int dwc3_ep0_delegate_req(struct dwc3 *dwc,
		struct usb_ctrlrequest *ctrl);

static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
		dma_addr_t buf_dma, u32 len, u32 type, bool chain)
@@ -352,7 +354,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
		 * Function Remote Wake Capable	D0
		 * Function Remote Wakeup	D1
		 */
		break;
		return dwc3_ep0_delegate_req(dwc, ctrl);

	case USB_RECIP_ENDPOINT:
		dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
@@ -507,6 +509,9 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
		 * For now, we're not doing anything, just making sure we return
		 * 0 so USB Command Verifier tests pass without any errors.
		 */
		ret = dwc3_ep0_delegate_req(dwc, ctrl);
		if (ret)
			return ret;
		break;
	default:
		ret = -EINVAL;
+166 −26
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@

static int __dwc3_gadget_start(struct dwc3 *dwc);
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc);
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup);

/**
 * dwc3_gadget_set_test_mode - enables usb2 test modes
@@ -1924,15 +1925,17 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
	return __dwc3_gadget_get_frame(dwc);
}

static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
static int dwc3_gadget_remote_wakeup(struct dwc3 *dwc)
{
	int			retries;

	int			ret;
	int			ret = 0;
	u32			reg;

	u8			link_state;
	unsigned long		flags;
	bool			link_recover_only = false;

	dev_dbg(dwc->dev, "%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.
@@ -1942,21 +1945,45 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
	reg = dwc3_readl(dwc->regs, DWC3_DSTS);

	link_state = DWC3_DSTS_USBLNKST(reg);

	switch (link_state) {
	case DWC3_LINK_STATE_RESET:
	case DWC3_LINK_STATE_RX_DET:	/* in HS, means Early Suspend */
	case DWC3_LINK_STATE_U3:	/* in HS, means SUSPEND */
	case DWC3_LINK_STATE_RESUME:
		break;
	case DWC3_LINK_STATE_U1:
		if (dwc->gadget.speed != USB_SPEED_SUPER) {
			link_recover_only = true;
			break;
		}
		fallthrough;
	default:
		return -EINVAL;
		dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
				link_state);
		ret = -EINVAL;
		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");
		return ret;
		/* 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;
	}

	/* Recent versions do this automatically */
@@ -1967,30 +1994,115 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
	}

	/* poll until Link State changes to ON */
	retries = 20000;
	spin_unlock_irqrestore(&dwc->lock, flags);
	enable_irq(dwc->irq);

	while (retries--) {
		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
	/*
	 * Have bigger value (16 sec) for timeout since some host PCs driving
	 * resume for very long time (e.g. 8 sec)
	 */
	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(16000));

		/* in HS, means ON */
		if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
			break;
	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 (!ret) {
		dev_dbg(dwc->dev, "Timeout moving into state(%d)\n",
							dwc->link_state);
		ret = -EINVAL;
		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;
	}

	if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
		dev_err(dwc->dev, "failed to send remote wakeup\n");
		return -EINVAL;
	/*
	 * According to DWC3 databook, the controller does not
	 * trigger a wakeup event when remote-wakeup is used.
	 * Hence, after remote-wakeup sequence is complete, and
	 * the device is back at U0 state, it is required that
	 * the resume sequence is initiated by SW.
	 */
	if (!link_recover_only)
		dwc3_gadget_wakeup_interrupt(dwc, true);

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

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

out1:
	return ret;
}

	return 0;
#define DWC3_PM_RESUME_RETRIES		20    /* Max Number of retries */
#define DWC3_PM_RESUME_DELAY		100   /* 100 msec */
static void dwc3_gadget_remote_wakeup_work(struct work_struct *w)
{
	struct dwc3		*dwc;
	int			ret;
	static int		retry_count;

	dwc = container_of(w, struct dwc3, remote_wakeup_work);

	ret = pm_runtime_get_sync(dwc->dev);
	if (ret) {
		/* pm_runtime_get_sync returns -EACCES error between
		 * late_suspend and early_resume, wait for system resume to
		 * finish and queue work again
		 */
		dev_dbg(dwc->dev, "PM runtime get sync failed, ret %d\n", ret);
		if (ret == -EACCES) {
			pm_runtime_put_noidle(dwc->dev);
			if (retry_count == DWC3_PM_RESUME_RETRIES) {
				retry_count = 0;
				dev_err(dwc->dev, "pm_runtime_get_sync timed out\n");
				return;
			}
			msleep(DWC3_PM_RESUME_DELAY);
			retry_count++;
			schedule_work(&dwc->remote_wakeup_work);
			return;
		}
	}
	retry_count = 0;
	dbg_event(0xFF, "Gdgwake gsyn",
		atomic_read(&dwc->dev->power.usage_count));

	ret = dwc3_gadget_remote_wakeup(dwc);
	if (ret)
		dev_err(dwc->dev, "Remote wakeup failed. ret = %d\n", ret);

	pm_runtime_put_noidle(dwc->dev);
	dbg_event(0xFF, "Gdgwake put",
		atomic_read(&dwc->dev->power.usage_count));
}

static int dwc3_gadget_wakeup(struct usb_gadget *g)
{
	struct dwc3		*dwc = gadget_to_dwc(g);
	unsigned long	flags;
	int			ret;

	spin_lock_irqsave(&dwc->lock, flags);
	if (!dwc->is_remote_wakeup_enabled) {
@@ -1999,11 +2111,32 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
		return -EINVAL;
	}

	ret = __dwc3_gadget_wakeup(dwc);
	spin_unlock_irqrestore(&dwc->lock, flags);
	schedule_work(&dwc->remote_wakeup_work);
	return 0;
}

#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
static int dwc_gadget_func_wakeup(struct usb_gadget *g, int interface_id)
{
	int ret = 0;
	struct dwc3 *dwc = gadget_to_dwc(g);

	if (dwc3_gadget_is_suspended(dwc)) {
		dev_dbg(dwc->dev, "USB bus is suspended, scheduling wakeup\n");
		dwc3_gadget_wakeup(&dwc->gadget);
		return -EAGAIN;
	}

	ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_XMIT_DEV,
			0x1 | (interface_id << 4));
	if (ret)
		dev_err(dwc->dev, "Function wakeup HW command failed, ret %d\n",
				ret);

	return ret;
}
#endif

static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
		int is_selfpowered)
@@ -2659,6 +2792,9 @@ static void __maybe_unused dwc3_gadget_set_speed(struct usb_gadget *g,
static const struct usb_gadget_ops dwc3_gadget_ops = {
	.get_frame		= dwc3_gadget_get_frame,
	.wakeup			= dwc3_gadget_wakeup,
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
	.func_wakeup		= dwc_gadget_func_wakeup,
#endif
	.set_selfpowered	= dwc3_gadget_set_selfpowered,
	.vbus_session		= dwc3_gadget_vbus_session,
	.vbus_draw		= dwc3_gadget_vbus_draw,
@@ -3286,6 +3422,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
	usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);

	dwc->connected = false;
	wake_up_interruptible(&dwc->wait_linkstate);
}

static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -3371,6 +3508,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
	dwc->gadget.speed = USB_SPEED_UNKNOWN;
	dwc->link_state = DWC3_LINK_STATE_U0;
	dwc->is_remote_wakeup_enabled = false;
	wake_up_interruptible(&dwc->wait_linkstate);
}

static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
@@ -3503,7 +3641,7 @@ 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)
{
	enum dwc3_link_state link_state = dwc->link_state;

@@ -3511,7 +3649,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
	dwc->link_state = DWC3_LINK_STATE_U0;

	/* For L1 resume case, don't perform resume */
	if (link_state != DWC3_LINK_STATE_U3)
	if (!remote_wakeup && link_state != DWC3_LINK_STATE_U3)
		return;

	/* Handle bus resume case */
@@ -3617,7 +3755,9 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
	}

	dev_dbg(dwc->dev, "Going from (%d)--->(%d)\n", dwc->link_state, next);
	dbg_log_string("link state from %d to %d", dwc->link_state, next);
	dwc->link_state = next;
	wake_up_interruptible(&dwc->wait_linkstate);
}

static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
@@ -3694,7 +3834,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_HIBER_REQ:
@@ -3982,7 +4122,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
	}

	dwc->irq_gadget = irq;

	INIT_WORK(&dwc->remote_wakeup_work, dwc3_gadget_remote_wakeup_work);
	dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
					  sizeof(*dwc->ep0_trb) * 2,
					  &dwc->ep0_trb_addr, GFP_KERNEL);
Loading